1、js中set和map的作用和區別?
在 JavaScript 中,Set 和 Map 是兩種非常重要的集合類型
1、Set 是一種集合數據結構,用于存儲唯一值。它類似于數組,但成員的值都是唯一的,沒有重復的值。Set 中的值只能是唯一的,任何重復的值都會被自動忽略。Set 中的值可以是任何數據類型(原始值或對象引用)。Set 提供了操作集合的方法,比如添加、刪除、檢查成員等。add(value): 添加一個值到 Set 中,如果該值已存在,則不會改變 Set。delete(value): 從 Set 中刪除一個值,如果刪除成功則返回 true,否則返回 false。has(value): 檢查 Set 中是否包含一個值,如果包含則返回 true,否則返回 false。clear(): 清除 Set 中的所有值。size: 獲取 Set 中元素的數量let mySet = new Set();mySet.add(1);mySet.add(1);console.log(mySet); // 輸出: Set {1}2、Map 是一種鍵值對集合,類似于對象,但“鍵”的范圍不限于字符串和符號,可以是任何數據類型(原始值或對象引用)。Map 中的鍵和值都是唯一的,鍵和值可以是任意類型。Map 保持插入順序,即迭代 Map 對象時,元素的順序與它們被插入的順序一致。Map 提供了操作鍵值對的方法,比如設置、獲取、刪除、檢查鍵等。set(key, value): 設置 Map 對象中指定元素的值。get(key): 返回指定鍵的值,如果不存在則返回 undefined。delete(key): 移除 Map 對象中指定的元素,如果刪除成功則返回 true,否則返回 false。has(key): 判斷 Map 對象中是否存在指定的鍵,如果存在則返回 true,否則返回 false。clear(): 移除 Map 對象中的所有鍵/值對。size: 獲取 Map 中鍵值對的數量。let myMap = new Map();myMap.set('name', 'Alice');myMap.set(1, 'first');myMap.set(1, 'second'); // 后面的值會覆蓋前面的值,但鍵還是1console.log(myMap); // 輸出: Map(2) { 'name' => 'Alice', 1 => 'second' }3、區別存儲內容:Set 只存儲唯一值,沒有鍵和值的區分。Map 存儲鍵值對,鍵和值都可以是任意類型,且鍵是唯一的。鍵的類型:Set 中的元素作為值,沒有鍵。Map 中的元素作為鍵,每個鍵都映射到一個值。順序:Set 和 Map 都保持插入順序。方法:Set 提供了與集合操作相關的方法,如 add、delete、has 等。Map 提供了與鍵值對操作相關的方法,如 set、get、delete、has 等。
2、flex 1是哪幾個的縮寫?
flex: 1 是 flex-grow、flex-shrink 和 flex-basis 的簡寫形式。具體來說:flex-grow: 1:表示彈性盒子在主軸方向上的增長比例為 1,即它可以占用剩余空間。flex-shrink: 1:表示彈性盒子在主軸方向上的收縮比例為 1,即在空間不足時可以縮小。flex-basis: 0%(或簡寫為 0,在某些情況下等同于 auto 的初始效果,但在此上下文中通常理解為 0 以確保空間分配):表示彈性盒子的初始大小為 0,這樣可以確保所有設置了 flex: 1 的彈性盒子平分剩余空間。
3、flex布局有哪幾種屬性?
display: flex;
flex-direction;//決定主軸的方向(即項目的排列方向)
flex-wrap;//定義如果一條軸線排不下項目,如何換行。
justify-content;//定義項目在主軸上的對齊方式 space-between,space-around,space-evenly
align-items;//定義項目在交叉軸上如何對齊
align-content;//定義了多根軸線(多行)在交叉軸上的對齊方式
4、vuex里變各屬性介紹?
Vuex 包含五個核心概念,分別是 state、getters、mutations、actions 和 modules。1、state定義:state是Vuex中的基本數據,用于存儲變量,相當于Vue 組件中的 data。特性:state中的數據是響應式的,當數據發生變化時,依賴該數據的組件會自動更新。使用:在組件中可以通過this.$store.state.xxx 或使用輔助函數mapState將數據映射到組件的computed 計算屬性中來訪問state中的數據。
2、getters定義:getters 是從 state 中派生的數據,相當于 state 的計算屬性。特性:getters 的返回值會根據其依賴的 state 值的變化而重新計算,并且具有緩存功能,只有當依賴值發生變 化時才會重新計算。使用:在組件中可以通過 this.$store.getters.xxx 或使用輔助函數 mapGetters 將 getters 映射到組 件的 computed 計算屬性中來訪問 getters 的返回值。
3、mutations定義:mutations 是 Vuex 中唯一允許更新 state 的方法,它必須是同步的。特性:每個 mutation 都有一個字符串的事件類型(type)和一個回調函數(handler),回調函數用于執行狀 態更新操作,并且會接受 state 作為第一個參數。使用:在組件中通過 this.$store.commit('xxx', payload) 來觸發 mutation,其中 'xxx' 是 mutation 的事件類型,payload 是傳遞給 mutation 的額外參數。
4、actions定義:actions 類似于 mutations,但不同的是 actions 提交的是 mutations,而不是直接變更狀態,并且 actions 可以包含任意異步操作。特性:actions 的回調函數接收一個上下文對象(context),該對象包含了 commit 方法用于觸發mutation, 以及 state 和 getters 等屬性用于訪問狀態和 getters。使用:在組件中通過 this.$store.dispatch('xxx', payload) 來觸發 action,其中 'xxx' 是 action 的事件類型,payload 是傳遞給 action 的額外參數。或使用輔助函數 mapActions
5、modules定義:modules 是 Vuex 的模塊化機制,允許將 store 分割成模塊(module),每個模塊擁有自己的 state、mutation、action、getter,甚至是嵌套子模塊。特性:模塊化使得 Vuex 的結構更加清晰,方便管理。每個模塊都是獨立的,可以單獨進行狀態管理,同時也可以通 過 namespaced: true 來啟用命名空間,避免不同模塊之間的命名沖突。使用:在創建 Vuex store 時,可以通過 modules 選項來定義模塊。在組件中訪問模塊的狀態和方法時,需要 使用模塊的命名空間(如果啟用了命名空間)來區分不同模塊的狀態和方法。
//一:定義store
//引入Vue和Vuex庫
import Vue from 'vue';
import Vuex from 'vuex';Vue.use(Vuex);// 使用Vuex插件//定義一個名為cartModule的模塊,用于管理購物車狀態
const cartModule = {// state對象包含模塊的狀態state: {items: [],// 購物車中的商品列表totalQuantity: 0,// 購物車中商品的總數量totalPrice: 0.0,// 購物車中商品的總價格checkoutStatus: false,// 結賬狀態,默認為未結賬lastAddedItemId: 0// 最后添加商品的ID,用于唯一標識商品},// getters對象包含基于state的派生狀態(計算屬性)getters: {getItems: state => state.items,// 獲取購物車中的商品列表getTotalQuantity: state => state.totalQuantity,// 獲取購物車中商品的總數量getTotalPrice: state => state.totalPrice,// 獲取購物車中商品的總價格getCheckoutStatus: state => state.checkoutStatus,// 獲取結賬狀態getLastAddedItemId: state => state.lastAddedItemId// 獲取最后添加商品的ID},// mutations對象包含改變狀態的同步方法mutations: {ADD_ITEM(state, item) {//向購物車中添加商品,并更新總數量和總價格state.items.push({ ...item, id: ++state.lastAddedItemId });state.totalQuantity += item.quantity;//總數量state.totalPrice += item.price * item.quantity;//價格*總數量},REMOVE_ITEM(state, itemId) {// 從購物車中移除指定ID的商品,并更新總數量和總價格const item = state.items.find(i => i.id === itemId);if (item) {state.totalQuantity -= item.quantity;state.totalPrice -= item.price * item.quantity;state.items = state.items.filter(i => i.id !== itemId);}},SET_CHECKOUT_STATUS(state, status) {// 設置結賬狀態state.checkoutStatus = status;}},// actions對象包含改變狀態的異步方法或批量更新actions: {addItem({ commit }, item) {// 調用mutation方法向購物車中添加商品commit('ADD_ITEM', item);},removeItem({ commit }, itemId) {// 調用mutation方法從購物車中移除商品commit('REMOVE_ITEM', itemId);},checkout({ commit }) {// 調用mutation方法設置結賬狀態為已結賬commit('SET_CHECKOUT_STATUS', true);}}
};// 創建Vuex store實例,并將cartModule作為模塊傳入
export default new Vuex.Store({modules: {cart: cartModule//namespaced: true 啟用了命名空間,可以定義多個不同的store}
});
<template><div><h1>購物車</h1><!-- 使用v-for指令遍歷購物車中的商品列表 --><ul><li v-for="item in items" :key="item.id"><!-- 顯示商品名稱、數量和價格 -->{{ item.name }} - {{ item.quantity }} x ${{ item.price }}<!-- 提供一個按鈕用于移除當前商品 --><button @click="removeItem(item.id)">移除</button></li></ul><!-- 顯示購物車中商品的總數量和總價格 --><p>總數量: {{ totalQuantity }}</p><p>總價: ${{ totalPrice }}</p><!-- 提供一個按鈕用于添加新商品 --><button @click="addItem">添加商品</button><!-- 提供一個按鈕用于結賬 --><button @click="checkout">結賬</button><!-- 如果結賬狀態為已結賬,則顯示已結賬提示 --><p v-if="checkoutStatus">已結賬</p></div>
</template><script>
export default {// 使用computed屬性從Vuex store中獲取購物車狀態computed: {items() {// 使用getters獲取購物車中的商品列表return this.$store.getters['cart/getItems'];},totalQuantity() {// 使用getters獲取購物車中商品的總數量return this.$store.getters['cart/getTotalQuantity'];},totalPrice() {// 使用getters獲取購物車中商品的總價格return this.$store.getters['cart/getTotalPrice'];},checkoutStatus() {// 使用getters獲取結賬狀態return this.$store.getters['cart/getCheckoutStatus'];}},// 定義方法用于與Vuex store交互methods: {addItem() {// 創建一個新商品對象,并通過dispatch調用action方法將其添加到購物車中const newItem = { name: '商品', quantity: 1, price: 10.0 };this.$store.dispatch('cart/addItem', newItem);},removeItem(itemId) {// 通過dispatch調用action方法從購物車中移除指定ID的商品this.$store.dispatch('cart/removeItem', itemId);},checkout() {// 通過dispatch調用action方法設置結賬狀態為已結賬this.$store.dispatch('cart/checkout');}}
};
</script>
5、ref()和 reactive() 的作用以及區別
ref() 和 reactive() 都是 Vue.js 3 中用于創建響應式數據的方法
一、ref() 的作用及特點1、ref() 主要用于包裝 JavaScript 的基本類型數據(如字符串、數字、布爾值等),使其具有響應性。2、它返回一個響應式引用對象,該對象有一個 .value 屬性來存儲傳入的值3、在html模板中,Vue 會自動解包 ref,使得可以直接使用 ref 變量而無需 .value 屬性。4、在JavaScript代碼中訪問或修改 ref 包裝的數據時,需要通過 .value 屬性來獲取或設置其實際值
二、reactive() 的作用及特點1、reactive() 主要用于包裝 JavaScript 對象和數組等復雜類型的數據,使其具有響應性2、它返回一個響應式代理對象,該對象可以攔截對原始對象的各種操作(如屬性讀取、賦值等),并在數據變化時觸 發更新。3、可以直接訪問和修改對象或數組的屬性或元素,而無需使用 .value 屬性。4、內部使用 Proxy 對象來實現數據代理和響應式機制。
三、使用場景:當需要處理簡單的、單一的響應式數據時,優先選擇 ref()。當需要處理復雜對象或數組時,考慮使用 reactive()。
四、實現原理:ref() 通過 Object.defineProperty() 的 get 和 set 方法實現數據代理。reactive() 使用 ES6 的 Proxy 對象來實現數據代理,可以更細粒度地控制對象數據的訪問和修改。import { reactive,ref } from 'vue';const count = ref(0); // 使用 ref() 包裝一個數字 function increment() {count.value++; // 通過 .value 屬性修改值}const user = reactive({name: 'John Doe',age: 30}); // 使用 reactive() 包裝一個對象function updateUser() {user.name = 'Jane Doe'; // 直接修改對象屬性user.age = 28;}
6、箭頭函數和普通函數的區別?
1、箭頭函數:使用箭頭(=>)來定義,語法更加簡潔;普通函數:使用function關鍵字來定義,語法相對傳統;
2、箭頭函數:如果只有一個參數,可以省略參數周圍的括號;如果函數體只有一行代碼且不需要返回值(即隱式返回),可以省略大括號和return關鍵字。
普通函數:參數和函數體都需要使用括號明確包圍;
3、箭頭函數:沒有自己的this,它會捕獲其所在上下文的this值作為自己的this值。這意味著在箭頭函數內部,this始終指向定義該函數時的上下文。普通函數:this的指向是可變的,它通常指向調用它的對象。在嚴格模式下('use strict'),如果未指定上下文(即非方法調用),this將默認為undefined;在非嚴格模式下,將默認為全局對象(在瀏覽器中通常是window)
4、箭頭函數:不綁定arguments對象。如果需要訪問傳遞給函數的參數列表,可以使用剩余參數(...args)語法。普通函數:每次調用都會創建一個新的arguments對象,該對象包含傳遞給函數的所有參數。
5、箭頭函數:不能用作構造函數,因此沒有prototype屬性。普通函數:可以用作構造函數來創建對象實例
6、箭頭函數:不能使用super關鍵字普通函數:在類的方法中,可以使用super關鍵字來調用父類的方法或訪問父類的屬性。
7、css中定位有哪些,有什么區別,哪些會影響性能
一、靜態定位(Static)特點:這是所有元素的默認定位方式。元素按照正常的文檔流進行排列,不會受到top、bottom、left、right屬性的影響。
二、相對定位(Relative)特點:元素相對于其正常位置進行定位。即使移動了元素,它原本在文檔流中的空間仍然會被保留。
三、絕對定位(Absolute)特點:元素脫離正常的文檔流,相對于最近的已定位(即position屬性不為static)的祖先元素進行定位。如果沒 有已定位的祖先元素,則相對于文檔的初始包含塊(通常是<html>元素或瀏覽器窗口)進行定位。(父相子絕)
四、固定定位(Fixed)特點:元素相對于瀏覽器窗口進行定位,無論頁面如何滾動,元素始終保持在指定的位置。元素脫離文檔流,不占據 原來的空間。
五、粘性定位(Sticky)特點:元素在滾動到特定位置之前表現為相對定位,滾動到特定位置后表現為固定定位。這允許元素在滾動過程中固 定在某個位置,直到滾動超出其父容器的邊界。
六、對性能的影響*靜態定位和相對定位:由于它們不改變元素在文檔流中的位置或只進行微小的偏移,因此對性能的影響較小。*絕對定位和固定定位:元素脫離文檔流,可能導致其他元素重新排列以填補空白。這可能會增加瀏覽器的重排和重繪工作,從而在某些情況下影響性能。然而,對于少量元素或簡單布局,這種影響通常是微不足道的。*粘性定位:粘性定位的實現可能涉及復雜的計算和狀態切換,因此在某些情況下可能對性能產生一定影響。然而,現代瀏覽器已經對粘性定位進行了優化,以提供流暢的用戶體驗。
8、重排和重繪的區別?
1、重排(Reflow):*當頁面元素的尺寸、結構或某些屬性(如邊距、內邊距、邊框寬度、字體大小等)發生改變時,會觸發重排。*添加、刪除DOM元素,或者改變DOM元素的位置也會觸發重排。*瀏覽器窗口大小的變化(如調整窗口大小或旋轉設備)同樣會導致重排。
2、重繪(Repaint):*當頁面元素的樣式發生改變,但這些改變不影響元素在文檔流中的位置和大小時,會觸發重繪。*常見的觸發重繪的樣式變化包括顏色、背景、文本顏色、邊框顏色等的改變。
3、性能開銷重排:*重排是一種比較昂貴的操作,因為它需要瀏覽器重新計算元素的幾何屬性并重新布局頁面。*這會消耗較多的計算資源和時間,尤其是在頁面包含大量元素或復雜布局的情況下。重繪:*相比重排,重繪是一種比較輕量級的操作。*它只需要瀏覽器重新渲染元素的外觀,而不需要重新計算元素的位置和大小。
4、優化建議*合并樣式改變:盡量將多次樣式改變合并成一次,以減少重排和重繪的次數。*使用CSS動畫:利用CSS動畫代替JavaScript操作來更新樣式,因為CSS動畫通常在瀏覽器內部進行了優化,可以減少性能開銷。*避免頻繁操作DOM:減少不必要的DOM操作,尤其是在循環或頻繁觸發的事件處理程序中。*使用絕對定位或固定定位:對于不需要參與文檔流布局的元素,可以使用絕對定位或固定定位來減少對其他元素的影響。*利用文檔片段:在大量添加或刪除DOM元素時,可以使用文檔片段(DocumentFragment)來減少重排次數。文檔片段是一個輕量級的文檔對象,可以在其中構建DOM結構,然后一次性將其添加到頁面中。
9、vue中nexttick的作用和用法,以及場景
1、$nextTick的主要作用是確保在DOM更新完成后執行一些操作。在Vue中,數據的更新是異步的,即Vue會異步執行更新隊列,而不是立即操作DOM。因此,如果需要在數據更新后立即獲取更新后的DOM元素或執行某些依賴于最新DOM狀態的操作,就需要使用$nextTick。
// 使用回調函數
this.$nextTick(function() {// 在DOM更新后執行的代碼console.log('DOM已更新');
});// 使用Promise對象
this.$nextTick().then(function() {// 在DOM更新后執行的代碼console.log('DOM已更新');
});2、常見使用場景(1)在數據變化后立即獲取更新后的DOM:當數據發生變化后,如果需要立即獲取更新后的DOM元素的狀態(如尺寸、位置、屬性等),可以使用$nextTick。(2)確保在DOM更新后執行某些操作:有時需要在DOM更新后執行一些操作,比如添加或刪除元素、更新元素的樣式、觸發動畫效果等。使用$nextTick可以確保這些操作在DOM更新后進行,避免操作無效或報錯。(3)在組件的生命周期鉤子中使用:在Vue組件的mounted和updated生命周期鉤子中,可以使用$nextTick來確保在DOM更新后執行某些邏輯。這特別適用于需要在組件掛載或更新后立即操作DOM的場景。(4)在動態渲染組件時使用:當動態渲染組件時,可以使用$nextTick來確保組件已經渲染完成。這對于需要在組件渲染后立即執行某些操作的場景非常有用。(5)在集成第三方庫時使用:在Vue中集成第三方庫時,有時需要確保第三方庫在正確的DOM狀態下初始化。使用$nextTick可以確保在DOM更新完成后初始化第三方庫。
10、computed和watch的區別?
computed和watch是兩個用于響應數據變化的特性。
一、computed(計算屬性)計算屬性是基于其他數據計算得出的屬性,它的值會根據依賴的數據自動更新。計算屬性會被緩存,只有當依賴的數據發生變化時,才會重新計算計算屬性更適合處理數據的計算和衍生,它提供了一種簡潔和高效的方式來處理數據的計算和格式化。計算屬性返回一個新的計算后的值,通常用于模板中。當需要根據其他數據進行計算或格式化時,例如根據輸入框的值計算出其他相關數據、根據列表數據計算出統計信息等。computed在依賴未變化的情況下避免了多次計算,適用于頻繁讀取的數據。
二、watch(偵聽器)watch用于監視數據的變化,并在數據變化時執行相應的操作。watch可以監聽單個數據、對象的屬性或數組的變化,并且可以進行深度監聽。watch適用于需要在數據變化時執行異步或開銷較大的操作,例如發送網絡請求、處理復雜的計算邏輯等。watch提供了更靈活的方式來響應數據的變化,并且可以處理更復雜的業務邏輯。watch的回調函數沒有返回值,它的重點在于執行的副作用,例如更新DOM、調用方法等。當需要監聽某個數據的變化并執行異步操作時,例如API請求或復雜的邏輯處理。當需要在數據變化后觸發某些副作用,例如重置表單、清空數據等操作時。watch更多地用于處理復雜的邏輯和異步操作,可能在性能方面考慮較少。
11、v-if和v-for在vue2和vue3中的區別?
1、在Vue2中當v-if和v-for同時出現在一個元素上時,v-for的優先級高于v-if。這意味著v-for會先遍歷數據,然后再根據v-if的條件決定是否渲染每個元素。
2、在Vue3中在Vue3中,v-if的優先級高于v-for。這意味著Vue會先根據v-if的條件過濾數據,然后再對過濾后的數據進行v-for遍歷。
12、vue中操作dom的方法有哪些
1、使用 ref 引用 DOM 元素
<template><div><input ref="myInput" type="text" />//引用信息將會注冊到父組件的 $refs 對象上。<button @click="focusInput">Focus Input</button></div>
</template><script>
export default {methods: {focusInput() {this.$refs.myInput.focus();//引用信息將會注冊到父組件的 $refs 對象上。}}
}
</script>
2、使用生命周期鉤子
//在 mounted 鉤子中,組件的 DOM 已經被渲染和插入到文檔中。
<template><div ref="myDiv">Hello, Vue!</div>
</template><script>
export default {mounted() {this.$refs.myDiv.style.color = 'red';}
}
</script>3、使用第三方庫(如 jQuery)
<template><div ref="myDiv">Hello, Vue with jQuery!</div>
</template><script>
import $ from 'jquery';export default {mounted() {$(this.$refs.myDiv).css('color', 'blue');}
}
</script>
13、vue中父子組件間傳參的方式有哪些?
一、使用Props傳遞參數父組件通過屬性向子組件傳遞數據。子組件通過props選項接收父組件傳遞的數據,并在模板中使用這些數據。*數據傳遞是單向的,即父組件向子組件傳遞
二、使用事件傳遞參數子組件通過觸發事件向父組件傳遞數據子組件使用$emit方法觸發事件,并傳遞數據作為參數,父組件通過監聽子組件觸發的事件來接收數據。
三、使用Provide和Inject(依賴注入)父組件通過provide提供數據,子組件通過inject注入這些數據可以實現跨多個層級的組件通信,適用于祖孫組件或更深層次的組件間通信
四、使用parent和children父組件通過$children屬性訪問子組件實例,子組件通過$parent屬性訪問父組件實例。
五、使用Vuex或事件總線(Bus)
//一、使用Props傳遞參數
//父組件
<template><div><ChildComponent :message="parentMessage"></ChildComponent></div>
</template><script>
import ChildComponent from './ChildComponent.vue';export default {components: {ChildComponent},data() {return {parentMessage: 'Hello from Parent!'};}
}
</script>
//子組件
<template><div>{{ message }}</div>
</template><script>
export default {props: {message: {type: String,required: true}}
}
</script>
//二、使用事件傳遞參數
//父組件
<template><div>//和子組件$emit的第一個參數保持一直<ChildComponent @message-from-child="handleMessageFromChild"></ChildComponent><p>{{ childMessage }}</p></div>
</template><script>
import ChildComponent from './ChildComponent.vue';export default {components: {ChildComponent},data() {return {childMessage: ''};},methods: {handleMessageFromChild(message) {//監聽的事件this.childMessage = message;}}
}
</script>
//子組件
<template><button @click="sendMessageToParent">Send Message to Parent</button>
</template><script>
export default {methods: {sendMessageToParent() {//第一個參數要個父組件@方法名保持一致,第二個參數是要傳遞的內容this.$emit('message-from-child', 'Hello from Child!');}}
}
</script>
14、vue3中路由守衛?
Vue Router中的路由守衛主要分為全局守衛、路由獨享守衛和組件內守衛三種類型。
一、全局守衛beforeEach:在路由即將改變前調用,參數包括即將進入的目標路由對象to、即將離開的當前路由對象from以及一 個用于阻止導航的next函數。beforeResolve:在路由解析之后但在導航確認之前調用,通常用于數據預取。afterEach:在路由改變之后調用,不接收next函數,因此不能改變導航結果。二、路由獨享守衛路由獨享守衛是在單個路由配置對象中定義的,它們只會在該路由匹配時生效。常見的路由獨享守衛有:beforeEnter:在路由即將進入前調用,參數包括即將進入的目標路由對象to、即將離開的當前路由對象from以及一個用于阻止導航的next函數。三、組件內守衛組件內守衛是在Vue組件內部定義的,它們會在組件的生命周期鉤子中調用。beforeRouteEnter:在路由進入組件之前調用,此時組件實例還未創建,不能訪問this。參數包括即將進入的目標路由對象to、即將離開的當前路由對象from以及一個用于繼續導航的next函數(在next中傳遞的參數會作為組件的初始props)。beforeRouteUpdate:在路由更新組件時調用(例如,從一個/user/1路由跳轉到/user/2)。此時組件實例已經存在,可以訪問this。參數同樣包括to、from和next。beforeRouteLeave:在導航離開組件時調用。參數包括即將離開的當前路由對象from、即將進入的目標路由對象to以及一個用于阻止導航的next函數。// 全局守衛
router.beforeEach((to, from, next) => {// 權限驗證邏輯if (to.meta.requiresAuth && !isAuthenticated) {// 未登錄則重定向到登錄頁面next({ path: '/login' });} else {// 已登錄或不需要驗證則繼續導航next();}
});// 路由獨享守衛
const route = {path: '/profile',component: Profile,beforeEnter: (to, from, next) => {// 特定路由的驗證邏輯if (to.meta.requiresProfile) {// 驗證邏輯...next();} else {next({ path: '/' });}}
};// 組件內守衛
export default {name: 'UserProfile',beforeRouteEnter(to, from, next) {// 組件即將進入前的邏輯next(vm => {// 可以通過vm訪問組件實例});},beforeRouteUpdate(to, from, next) {// 路由更新時的邏輯next();},beforeRouteLeave(to, from, next) {// 離開組件前的邏輯next();}
};
15、js中原型和原型鏈?
一、原型(Prototype)*在JavaScript中,對象有一個特殊的隱藏屬性[[Prototype]],它要么為null,要么就是對另一個對象的引用,該對象被稱為“原型”*通過原型,可以定義對象的共享屬性和方法。這意味著所有對象實例都可以訪問和修改這些屬性和方法,這在創建大量具有相同屬性和方法的對象時非常有用,因為它可以避免在每個對象實例中重復定義這些屬性和方法。*原型還可以用于動態修改和擴展對象,允許我們在不修改原始構造函數的情況下,為所有對象實例添加新的屬性和方法。
二、原型鏈(Prototype Chain)原型鏈是JavaScript中實現對象繼承的主要機制。當一個對象試圖訪問一個屬性時,如果它自身沒有這個屬性,JavaScript會在它的原型鏈上查找這個屬性,直到找到這個屬性或者到達鏈的盡頭(null)。原型是 JavaScript 中每個對象上都有的一個特殊屬性,它指向另一個對象,這個對象即原型對象。原型對象也可以有自己的原型,這樣就形成了一條鏈,即原型鏈。原型鏈是由一系列原型對象連接而成的鏈,每個對象都有一個 __proto__ 屬性指向它的原型對象,直到 Object.prototype,這個原型對象是最終的原型對象,也就是說它沒有自己的原型。function Person(name, age) {this.name = name;this.age = age;
}Person.prototype.sayHello = function() {console.log("Hello, my name is " + this.name);
};const alice = new Person("Alice", 30);
const bob = new Person("Bob", 25);alice.sayHello(); // 輸出: Hello, my name is Alice
bob.sayHello(); // 輸出: Hello, my name is Bob
16、js中對數組和對象的操作方法有哪些?
一、數組的操作方法
1、添加/刪除元素push():向數組的末尾添加一個或多個元素,并返回新的長度。pop():刪除并返回數組的最后一個元素。shift():刪除并返回數組的第一個元素。unshift():向數組的開頭添加一個或多個元素,并返回新的長度。splice():通過刪除或替換現有元素或者添加新元素來修改數組,并以數組的形式返回被修改的內容。此方法會改變原數組。
2、截取/復制數組slice():返回一個新的數組對象,這個對象是一個由原數組的指定開始到結束(不包括結束)的淺拷貝。原始數組不會被改變。concat():用于連接兩個或更多的數組。該方法不會改變現有的數組,而是返回一個新數組。3、排序/反轉數組sort():對數組的元素進行排序,并返回數組。默認情況下,sort()方法將元素轉換為字符串,然后比較它們的UTF-16代碼單元值序列,以進行排序。如果需要對數字進行排序,需要提供一個比較函數。reverse():反轉數組中元素的順序。4、遍歷/映射數組forEach():對數組的每個元素執行一次給定的函數。map():創建一個新數組,其結果是該數組中的每個元素是調用一次提供的函數后的返回值。for循環:通過索引來遍歷數組元素。for...of循環:直接遍歷數組(或任何可迭代對象)的值,無需關心索引。5、搜索/查找數組find():返回數組中滿足提供的測試函數的第一個元素的值。否則返回undefined。findIndex():返回數組中滿足提供的測試函數的第一個元素的索引。否則返回-1。includes():判斷一個數組是否包含一個指定的值,根據情況可從左往右或從右往左遍歷。indexOf():在數組中從左到右搜索一個值,并返回其索引(從0開始)。lastIndexOf():在數組中從右到左搜索一個值,并返回其索引(從0開始)。
6、歸約/累加數組reduce():對累加器和數組中的每個元素(從左到右)應用一個函數,將其減少為單個值。reduceRight():與reduce()類似,但是從右到左。7、其他方法fill():使用給定的值填充數組的從起始索引到結束索引的所有元素。不會改變原數組的大小。copyWithin():在當前數組中,將指定位置的元素復制到其他位置,并返回該數組。不會改變數組的長度。flat()和flatMap():flat()方法會按照一個可指定的深度遞歸遍歷數組,并將所有元素合并為一個新數組。 flatMap()方法首先使用映射函數映射每個元素,然后將結果展平成一個新數組。toString():把數組轉換為字符串,并返回結果。數組中的元素之間用逗號分隔。values():返回一個新的數組迭代器對象,該對象包含數組中的每個索引的鍵/值對。entries():返回一個新的數組迭代器對象,該對象包含數組中的每個索引的鍵/值對(索引作為鍵)。keys():返回一個新的數組迭代器對象,該對象包含數組中的每個索引的鍵。二、對象的操作方法1、創建對象Object.create():創建一個新對象,使用現有的對象來提供新創建的對象的__proto__。字面量語法:使用花括號{}直接創建一個對象。
2、添加/刪除屬性點(.)或方括號([])語法:向對象添加或訪問屬性。delete操作符:刪除對象的屬性。
3、屬性描述符Object.defineProperty():在對象上定義一個新屬性,或修改一個對象的現有屬性,并返回該對象。Object.defineProperties():在對象上定義多個新屬性或修改現有屬性,并返回該對象。
4、獲取對象信息Object.keys():返回一個給定對象自身可枚舉屬性組成的數組,數組中屬性名的排列順序和使用for...in循環遍歷該對象時返回的順序一致(兩者的主要區別是for-in循環還會枚舉其原型鏈上的屬性)。Object.values():返回一個給定對象自身的所有可枚舉屬性值的數組,其排列與Object.keys()返回的數組中的屬性名排列相同。Object.entries():返回一個給定對象自身可枚舉屬性的鍵值對數組,其排列與通過手動遍歷該對象屬性返回的順序一致。Object.getOwnPropertyNames():返回一個數組,該數組對對象的所有自身屬性(不包括不可枚舉屬性,但包括符號屬性)的鍵進行排序。Object.getOwnPropertySymbols():返回一個給定對象自身的所有符號屬性的數組。
5、合并對象Object.assign():將所有可枚舉屬性的值從一個或多個源對象復制到目標對象。它將返回目標對象。
6、凍結/密封對象Object.freeze():可以凍結一個對象。凍結對象后,不能向對象添加新的屬性,不能刪除現有屬性,不能修改現有屬性的值,不能修改現有屬性的可枚舉性、可配置性,也不能修改現有屬性的[[Writable]]特性。Object.seal():可以封閉一個對象,阻止添加新的屬性并將所有現有屬性標記為不可配置。當前屬性的值只要可寫,依然可以被修改。
7、其他方法Object.is():確定兩個值是否相同,與嚴格相等運算符(===)的行為存在一些細微差別。Object.hasOwnProperty():返回一個布爾值,表示對象自身屬性中是否存在指定的屬性(不包括繼承的屬性)。Object.prototype.toString.call():可以用來獲取對象的內部[[Class]]屬性,返回表示該對象的字符串。
17、get請求和post請求的區別?
1、數據傳輸方式GET請求:將數據附加在URL的查詢字符串中;POST請求:將數據放在HTTP請求體中。這意味著,請求的參數不會直接暴露在URL上,而是包含在請求的正文部分。
2、緩存處理GET請求:通常會被瀏覽器緩存;POST請求:通常不會被瀏覽器緩存;
3、安全性:GET請求:參數直接暴露在URL上,容易泄露敏感信息。因此,GET請求不適合傳輸敏感數據。POST請求:參數不會暴露在URL上,相對更安全。
4、冪等性:GET請求:是冪等的。這意味著,多次執行同一個GET請求不會產生副作用。例如,多次請求同一個資源,返回的結果 應該是相同的。POST請求:不是冪等的。多次執行同一個POST請求可能會改變服務器的狀態。例如,多次提交表單數據,可能會導 致數據庫中插入多條記錄。
5、數據長度限制GET請求:參數長度受URL長度限制。POST請求:沒有長度限制。
6、書簽保存與歷史記錄GET請求:URL可以保存為書簽,并且參數會保留在瀏覽器歷史記錄中。這使得用戶可以通過書簽或歷史記錄方便地重新訪問之前請求過的資源。POST請求:URL不能直接保存為書簽,并且參數不會保留在瀏覽器歷史記錄中
7、數據類型限制GET請求:通常只接受ASCII字符POST請求:沒有數據類型限制import axios from 'axios';// 封裝一個GET請求函數
export const getRequest = (url, params) => {return axios.get(url, {params: params, // 將參數作為查詢字符串附加到URL上}).then(response => {// 如果請求成功,返回響應數據return response.data;}).catch(error => {// 如果請求失敗,拋出錯誤console.error('GET request error:', error);throw error;});
};// 封裝一個POST請求函數
export const postRequest = (url, data) => {return axios.post(url, data).then(response => {// 如果請求成功,返回響應數據return response.data;}).catch(error => {// 如果請求失敗,拋出錯誤console.error('POST request error:', error);throw error;});
};
//調用
getRequest('https://api.example.com/data', { id: 123 })
postRequest('https://api.example.com/data', { name: 'John Doe', age: 30 })
18、js中,請求攔截器和響應攔截器都能分別做什么,給出代碼案例
一、請求攔截器請求攔截器可以在請求被發送到服務器之前對其進行修改或添加一些額外的處理邏輯。例如,你可以在每個請求中添加認證令牌、修改請求頭或處理錯誤。
const axios = require('axios');// 添加請求攔截器
axios.interceptors.request.use(config => {// 在發送請求之前做些什么// 例如,添加認證令牌到請求頭const token = 'your-auth-token';if (token) {config.headers['Authorization'] = `Bearer ${token}`;}return config;
}, error => {// 對請求錯誤做些什么return Promise.reject(error);
});// 發送請求
axios.get('/some/endpoint').then(response => {console.log(response.data);}).catch(error => {console.error('Error:', error);});二、響應攔截器響應攔截器可以在服務器響應到達客戶端之前對其進行處理。例如,你可以統一處理錯誤響應、轉換響應數據格式或根據響應狀態碼執行不同的邏輯。const axios = require('axios');// 添加響應攔截器
axios.interceptors.response.use(response => {// 對響應數據做點什么// 例如,檢查響應狀態碼并處理錯誤if (response.status === 200) {// 請求成功,返回響應數據return response.data;} else {// 請求失敗,拋出錯誤return Promise.reject(new Error(`Error ${response.status}: ${response.statusText}`));}
}, error => {// 對響應錯誤做點什么// 例如,統一處理401未授權錯誤并重定向到登錄頁面if (error.response && error.response.status === 401) {// 執行重定向或其他邏輯window.location.href = '/login';}return Promise.reject(error);
});// 發送請求
axios.get('/some/endpoint').then(data => {console.log('Data:', data);}).catch(error => {console.error('Error:', error.message);});
19、vue3中緩存組件怎么處理,里邊用到了那個鉤子函數?
在Vue 3中,緩存組件通常使用<keep-alive>組件來實現。<keep-alive>是Vue內置的一個組件,它能夠緩存不活動的組件實例,而不是銷毀它們,從而保留組件的狀態并避免重復渲染,進而提升性能和用戶體驗。<keep-alive>還支持include和exclude屬性,允許你精確控制哪些組件需要被緩存,哪些需要被排除。你還可以使用max屬性來限制緩存組件的最大數量。在Vue 3中,當緩存的組件被激活或停用時,會觸發特定的生命周期鉤子函數(1)onActivated:當組件被插入DOM時觸發。這通常發生在用戶導航回到該組件所在的頁面或視圖時。在這個鉤子函數中,你可以執行一些需要在組件激活時進行的操作,比如更新數據或重新獲取焦點等。(2)onDeactivated:當組件從DOM中移除時觸發。這通常發生在用戶導航離開該組件所在的頁面或視圖時。在這個鉤子函數中,你可以執行一些清理工作,比如取消訂閱事件或停止某些后臺操作等。
20、vue2和vue3的區別?
一、響應式數據綁定原理Vue 2:使用ES5的Object.defineProperty方法,通過發布/訂閱模式實現雙向數據綁定。這種方式存在一些局限性,例如它只能監聽某個屬性,不能對全對象進行監聽,且需要額外的操作來監聽數組的變化。Vue 3:引入ES6的Proxy對象,對數據進行代理,從而實現了更強大和靈活的響應式系統。Proxy可以監聽對象和數組的變化,無需進行額外的操作,并且可以直接綁定整個對象,提高了效率和易用性。二、API設計Vue 2:使用選項式API(Options API),組件的邏輯被分散在data、methods、computed等選項中。這種方式在組件較復雜時,可能會導致代碼組織不夠清晰。Vue 3:引入了組合式API(Composition API),通過setup函數來組織組件的邏輯。這種方式使得代碼更加模塊化和可復用,邏輯更加清晰,易于理解和維護。
三、生命周期鉤子函數Vue 2:提供了如beforeCreate、created、beforeMount、mounted等生命周期鉤子函數。Vue 3:同樣提供了生命周期鉤子函數,但命名和觸發時機有所調整。例如,setup函數在beforeCreate和created之前執行,而onBeforeMount和onMounted分別對應Vue 2中的beforeMount和mounted。此外,Vue 3還增加了onRenderTracked和onRenderTriggered兩個調試鉤子。四、TypeScript支持Vue 2:雖然可以使用TypeScript,但支持不夠完善,類型推斷和類型檢查較弱。Vue 3:從設計之初就考慮了TypeScript的支持,提供了更好的類型推斷,允許更安全的開發體驗。這使得在Vue 3項目中使用TypeScript變得更加自然和高效。
五、組件結構Vue 2:在<template>中必須只有一個根標簽。Vue 3:支持碎片化(Fragments),允許組件有多個根標簽。Vue 3會默認把這些標簽包裹在一個虛擬標簽中,以減少內存占用。
六、創建Vue實例Vue 2:通過new Vue()構造函數來創建Vue實例,通常是在main.js文件中直接創建應用實例,并將路由和狀態管理作為選項傳入。Vue 3:使用createApp函數來創建應用實例,這使得創建過程更加清晰。路由和狀態管理通過use方法進行插件注冊。
七、性能優化Vue 2:性能較好,但在大型應用中,當數據變化頻繁時可能出現性能瓶頸。Vue 3:引入了虛擬DOM的優化,減少了不必要的渲染;使用編譯時優化,生成更小的代碼,提高了運行時性能。此外,Vue 3的響應式系統也更加高效,進一步提升了性能。
21、css中像素有那些?
一、、CSS像素(px)定義:CSS像素是Web編程中的概念,指的是CSS樣式代碼中使用的邏輯像素。它是瀏覽器內一切長度的基本單位。特性:*CSS像素是一個相對單位,它相對于設備像素(device pixel)而言。在同樣一個設備上,每1個CSS像素所 代表的物理像素是可以變化的。*在不同的設備之間,每1個CSS像素所代表的物理像素也是可以變化的。*CSS像素的值是固定的,不會隨屏幕尺寸或分辨率變化。二、、物理像素(pt)定義:物理像素是顯示屏上能控制顯示的最小物理單位,即顯示器上一個個的點。從屏幕生產出來的那天起,它上面的物理像素點就固定不變了。單位換算:1pt = 1/72英寸,而1英寸等于2.54厘米。因此,pt是一個真正的絕對單位。三、設備像素比(DPR)與設備獨立像素(DIP設備像素比(DPR):設備像素比描述的是未縮放狀態下,物理像素和CSS像素的初始比例關系。它可以通過設備的物理像素除以CSS像素來計算。例如,在Retina屏的iPhone上,DPR的值為2,意味著1個CSS像素相當于2個物理像素。(物理像素/CSS像素)=2【【假設有一個元素的CSS寬度為100px。在DPR = 2的設備上,這個元素實際上會占據200x設備物理像素的寬度(因為每個CSS像素由2x2個物理像素組成,所以100px * 2 = 200個物理像素寬度)。】】設備獨立像素(DIP):設備獨立像素也稱為邏輯像素,它可以是系統中的一個點,這個點代表一個可以由程序使用的虛擬像素。在移動端瀏覽器中以及某些桌面瀏覽器中,window對象有一個devicePixelRatio屬性,它的定義為設備物理像素和設備獨立像素的比例。CSS像素就可以看做是設備的獨立像素。
四、視窗相關像素單位(vh、vw等)vh:視窗高度(viewpoint height),1vh等于視窗高度的1%。vw:視窗寬度(viewpoint width),類似地,1vw等于視窗寬度的1%。
五、其他相對單位(em、rem)em:相對單位,基準點為父節點字體的大小。如果自身定義了font-size,則按自身來計算。整個頁面內1em的值不是固定的。rem:相對單位,可理解為“root em”,即相對根節點html的字體大小來計算。這是CSS3新加的屬性,被Chrome、Firefox、IE9+等瀏覽器支持。
六、百分比像素單位(%)定義:百分比是一個相對單位,它表示某個值相對于另一個值(通常是父元素的某個屬性)的百分比。在CSS中,百分比單位常用于定義元素的寬度、高度、邊距、內邊距等屬性。為了在不同的DPR屏幕下讓圖片看起來都不失真,開發者需要為不同DPR的屏幕提供不同大小的圖片資源。例如:
對于DPR = 1的設備,提供標準大小的圖片。
對于DPR = 2的設備,提供兩倍大小的圖片(即圖片寬度和高度都是原來的兩倍)。
對于DPR = 3的設備,提供三倍大小的圖片。
<img src="image-small.png" srcset="image-small.png 1x, image-medium.png 2x, image-large.png 3x">
22、promise有哪些狀態和方法?
一、Promise的三種狀態*Pending(進行中):這是Promise的初始狀態,表示異步操作尚未完成,處于等待狀態。*Fulfilled(已完成):表示異步操作已成功完成,并返回了結果。此時,Promise的狀態從Pending變為Fulfilled。*Rejected(已拒絕):表示異步操作失敗,并返回了錯誤原因。此時,Promise的狀態從Pending變為Rejected。
二、Promise的實例方法then(onFulfilled, onRejected):catch(onRejected)finally(onFinally)三、Promise的靜態方法Promise.resolve(value)返回一個狀態為fulfilled的Promise對象,并將傳入的值作為該Promise對象的結果。Promise.reject(reason)返回一個狀態為rejected的Promise對象,并將傳入的錯誤作為該Promise對象的結果Promise.all(iterable)接收一個包含多個Promise對象的可迭代對象(如Promise數組),并返回一個新的PromisePromise.allSettled(iterable)只有當所有傳入的Promise對象都變為settled(即fulfilled或rejected)時,返回的Promise才變為fulfilled。Promise.race(iterable)返回的Promise的狀態和結果由第一個變為settled(fulfilled或rejected)的Promise決定Promise.any(iterable)只要存在一個Promise變為fulfilled,返回的Promise就變為fulfilled,其結果為第一個fulfilled的Promise的結果。
23、v-if和v-show的區別?
**相同點:
在 Vue.js 中,v-if 和 v-show 都用于條件渲染元素
**不同點:
1、實現原理v-if直接通過 添加/移除 DOM 元素 控制顯示。若條件為 false,元素會被完全銷毀,相關組件實例和事件監聽也會被銷毀。v-show通過 切換 CSS 的 display 屬性(如 display: none)控制顯示。元素始終存在于 DOM 中,只是視覺不可見。
2、性能差異v-if適合低頻切換:條件變化時觸發 DOM 增刪,初始渲染成本較高,但條件穩定后無額外開銷v-show適合高頻切換:僅修改 CSS,性能開銷小。
3、支持的指令v-if支持 v-else、v-else-if 實現多條件分支v-show不支持 v-else,只能控制單個元素的顯示狀態。
4、生命周期影響v-if條件變化時觸發組件的 創建/銷毀 生命周期(如 mounted/unmounted)。v-show不觸發生命周期,僅改變樣式,組件狀態保持。
5、典型場景v-if需要完全移除元素(如權限控制、動態組件)。條件切換頻率低。v-show需要頻繁切換顯示狀態(如折疊菜單、模態框);需要保留元素狀態(如表單輸入值)。
24、apply、call、bind的相同點和不同點,以及如何使用,給出代碼示例
一、相同點*改變 this 指向:這三個方法都可以用來改變函數調用時 this 的指向。*與函數相關:它們都是函數對象的方法
二、不同點
1、參數傳遞call 方法接受一個參數列表,第一個參數是 this 的值,后續參數按順序傳遞給函數。apply 方法也接受 this 的值作為第一個參數,但后續參數是以數組(或類數組對象)的形式傳遞的。bind 方法返回一個新的函數,這個新函數在被調用時會將其 this 關鍵字設置為 bind 方法的第一個參數,同時可以接受一個參數列表(可選),這些參數會預先傳遞給原函數。
2、執行時機call 和 apply 會立即調用函數。bind 不會立即調用函數,而是返回一個新的函數,這個新函數在被調用時才會執行原函數。三、使用示例//call的使用function greet() {console.log(`Hello, ${this.name}!`);}const person = { name: 'Alice' };greet.call(person); // 輸出: Hello, Alice!//apply的使用function sum(a, b) {return a + b;}const numbers = [3, 5];console.log(sum.apply(null, numbers)); // 輸出: 8,這里 null 因為 sum 函數不依賴 this//bind的使用function describe(city, country) {console.log(`I'm visiting ${city} in ${country}`);}const visit = describe.bind(null, 'Paris'); // 綁定 this 為 null,并預先傳遞 'Paris'visit('France'); // 輸出: I'm visiting Paris in France
25、js中計算一個數組的數組里面數據的和該用哪些方法?
計算一個二維數組(數組的數組)中所有數據的和
//方法一:使用嵌套的 for 循環
function sumNestedArray(arr) {let sum = 0;for (let i = 0; i < arr.length; i++) {for (let j = 0; j < arr[i].length; j++) {sum += arr[i][j];}}return sum;
}const nestedArray = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
console.log(sumNestedArray(nestedArray)); // 輸出 45//方法二:使用 reduce 方法
//reduce 方法可以用來對數組中的元素進行累積操
function sumNestedArray(arr) {return arr.reduce((acc, subArray) => acc + subArray.reduce((sum, num) => sum + num, 0), 0);
}const nestedArray = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
console.log(sumNestedArray(nestedArray)); // 輸出 45//方法三:使用 flat 方法
//flat 方法可以將多維數組展平為一維數組,然后使用 reduce 或其他方法求和
function sumNestedArray(arr) {const flattenedArray = arr.flat();return flattenedArray.reduce((sum, num) => sum + num, 0);
}const nestedArray = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
console.log(sumNestedArray(nestedArray)); // 輸出 45//方法四:使用 forEach 方法
function sumNestedArray(arr) {let sum = 0;arr.forEach(subArray => {subArray.forEach(num => {sum += num;});});return sum;
}const nestedArray = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
console.log(sumNestedArray(nestedArray)); // 輸出 45//方法五:使用遞歸(適用于不規則嵌套數組)
//如果數組的嵌套層次不固定,可以使用遞歸方法來求和。
function sumNestedArray(arr) {let sum = 0;arr.forEach(item => {if (Array.isArray(item)) {sum += sumNestedArray(item);} else {sum += item;}});return sum;
}const nestedArray = [1, [2, [3, 4], 5], 6, [7, 8, 9]];
console.log(sumNestedArray(nestedArray)); // 輸出 45//方法六:使用 for...of 循環
function sumNestedArray(arr) {let sum = 0;for (const subArray of arr) {for (const num of subArray) {sum += num;}}return sum;
}const nestedArray = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
console.log(sumNestedArray(nestedArray)); // 輸出 45
26、vue3中如何封裝一個公共組件,要從那些方面考慮,并給出代碼案例?
在 Vue 3 中封裝公共組件時,需要從多個方面考慮,以確保組件的可復用性、靈活性和可維護性。以下是一些關鍵點和一個簡單的代碼示例:
1. 組件的可配置性Props:通過 props 提供組件的配置項,允許使用者自定義組件的行為和外觀。默認值:為 props 提供默認值,以減少使用者的配置負擔。類型檢查:使用 TypeScript 或 Vue 的 PropType 來確保傳入的值符合預期。
2. 事件交互自定義事件:通過 $emit發出事件,允許父組件監聽和響應子組件的行為。事件命名:使用清晰的事件命名,避免與原生事件沖突。
3. 樣式封裝局部樣式:使用 scoped 樣式或 CSS Modules,避免樣式污染。可覆蓋樣式:提供一些可覆蓋的樣式類或樣式變量,方便使用者自定義樣式。
4. 邏輯復用組合式 API:使用 Vue 3 的組合式 API (setup 函數) 來封裝邏輯,便于復用。計算屬性和偵聽器:合理使用計算屬性和偵聽器來處理數據。
5. 文檔和示例文檔:為組件提供清晰的文檔,說明 props、事件、插槽等的使用方法。示例:提供使用示例,幫助使用者快速上手。
//封裝的組件
<template><div class="counter"><button @click="decrement">-</button><span>{{ count }}</span><button @click="increment">+</button></div>
</template><script setup>
import { ref, watch, defineProps, defineEmits } from 'vue';const props = defineProps({modelValue: {type: Number,default: 0,},min: {type: Number,default: 0,},max: {type: Number,default: 100,},
});
//emit 函數被定義為一個可以觸發 'update:modelValue' 和 'change' 兩個事件的對象。
//'update:modelValue' 通常用于V-Model的雙向綁定更新。當組件內部需要更新父組件通過v-model綁定的值時,可以觸發這個事件。
//'change' 事件則是一個自定義事件,可以在組件內部邏輯需要時觸發,通知父組件或其他監聽者某些狀態或數據已經改變。
const emit = defineEmits(['update:modelValue', 'change']);const count = ref(props.modelValue);//watch 函數偵聽的是一個箭頭函數 () => props.modelValue 的返回值。
//通常是通過父組件傳遞給子組件的數據,使用v-model綁定時,modelValue 就是綁定的值。
//當 props.modelValue 發生變化時,watch 函數的回調函數會被調用,接收新的值 newValue 作為參數。
watch(() => props.modelValue, (newValue) => {count.value = newValue;
});function increment() {if (count.value < props.max) {count.value++;emit('update:modelValue', count.value);emit('change', count.value);}
}function decrement() {if (count.value > props.min) {count.value--;emit('update:modelValue', count.value);emit('change', count.value);}
}
</script><style scoped>
.counter {display: flex;align-items: center;gap: 8px;
}button {padding: 4px 8px;cursor: pointer;
}span {font-size: 16px;font-weight: bold;
}
</style>
//使用案例
<template><div><Counter v-model="counterValue" :min="0" :max="10" @change="handleChange" /><p>Counter Value: {{ counterValue }}</p></div>
</template><script setup>
import { ref } from 'vue';
import Counter from './Counter.vue';const counterValue = ref(5);function handleChange(value) {console.log('Counter changed:', value);
}
</script>
27、場景題:假設目前已經封裝了一個公共組件,但在用時要根據不同的需求,再里邊添加輸入框(根據不同需求添加一個或多個),如何處理這種情況,給出代碼示例
在這種場景下,可以通過 插槽(Slots) 來實現組件的靈活性。插槽允許父組件在子組件的特定位置插入自定義內容,從而滿足不同需求。
父組件:根據需求在子組件中插入不同數量的輸入框。
子組件:使用插槽來支持動態插入內容。
//公共組件CustomComponent.vue
<template><div class="custom-component"><div class="header"><slot name="header">默認頭部內容</slot></div><div class="content">//補寫name就是 默認插槽<slot>默認內容區域</slot></div><div class="footer"><slot name="footer">默認底部內容</slot></div></div>
</template><script setup>
import { defineProps, defineEmits } from 'vue';const props = defineProps({// 可以根據需要定義 props
});const emit = defineEmits([]);
</script><style scoped>
.custom-component {border: 1px solid #ccc;padding: 16px;border-radius: 8px;
}.header, .content, .footer {margin-bottom: 16px;
}
</style>
//父組件代碼ParentComponent.vue
<template><div><h2>使用 CustomComponent 并插入輸入框</h2><CustomComponent><!-- 插入一個輸入框 --><template #content><div><label for="input1">輸入框 1:</label><input id="input1" type="text" v-model="input1" /></div></template></CustomComponent><h2>使用 CustomComponent 并插入多個輸入框</h2><CustomComponent><!-- 插入多個輸入框 --><template #content><div><label for="input2">輸入框 2:</label><input id="input2" type="text" v-model="input2" /></div><div><label for="input3">輸入框 3:</label><input id="input3" type="text" v-model="input3" /></div></template></CustomComponent></div>
</template><script setup>
import { ref } from 'vue';
import CustomComponent from './CustomComponent.vue';const input1 = ref('');
const input2 = ref('');
const input3 = ref('');
</script>tips:
在 Vue 3 中,#default 是默認插槽的別名,而 # 是 v-slot 的簡寫。因此,#content 實際上是默認插槽的一種自定義命名方式。如果子組件中沒有顯式定義名為 content 的具名插槽,Vue 會將 #content 的內容插入到默認插槽中。
28、具名插槽,有什么作用和如何使用,給出代碼示例
它允許父組件在子組件的特定位置插入內容。具名插槽的作用是讓父組件能夠更精確地控制子組件的內部結構,同時保持子組件的通用性和靈活性。具名插槽通常用于需要在子組件的多個不同位置插入內容的場景。一、如何使用具名插槽1、定義具名插槽在子組件中,使用 <slot name="slotName"> 定義具名插槽。name 屬性用于標識插槽的名稱。2. 使用具名插槽在父組件中,使用 <template #slotName> 或 <template v-slot:slotName> 將內容插入到具名插槽中。
29、vue2h和vue3中父子傳值方式有何不同,給出代碼案例
一、從父組件向子組件傳值
在 Vue 2 中,父組件通過 props 向子組件傳值。
在 Vue 3 中,父組件同樣通過 props 向子組件傳值,但可以使用 defineProps 來更簡潔地定義 props。//vue2子組件
<template><div><p>從父組件接收到的值:{{ message }}</p></div>
</template><script>
export default {props: {message: {type: String,default: ''}}
};
</script>//vue2父組件
<template><div><ChildComponent :message="parentMessage" /></div>
</template><script>
import ChildComponent from './ChildComponent.vue';export default {components: {ChildComponent},data() {return {parentMessage: 'Hello from Parent!'};}
};
</script>//vue3子組件
<template><div><p>從父組件接收到的值:{{ message }}</p></div>
</template><script setup>
import { defineProps } from 'vue';const props = defineProps({message: {type: String,default: ''}
});
</script>//vue3父組件
<template><div><ChildComponent :message="parentMessage" /></div>
</template><script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';const parentMessage = ref('Hello from Parent!');
</script>二、從子組件向父組件傳值在 Vue 2 中,子組件通過 $emit 向父組件發送事件,父組件通過監聽這些事件來接收數據在 Vue 3 中,子組件同樣通過 emit 向父組件發送事件,父組件通過監聽這些事件來接收數據。但可以使用 defineEmits 來更簡潔地定義事件。defineEmits 的主要作用是提供類型安全,確保發出的事件名稱和參數類型正確。tips:在 Vue 3 中,推薦使用 emit 而不是 $emit。這是因為在 Vue 3 的組合式 API (setup 函數) 中,emit 是通過 defineEmits 定義的,它是一個更簡潔和類型安全的方式。而 $emit 是 Vue 2 中的用法,雖然在 Vue 3 中仍然可以使用,但不推薦在組合式 API 中使用。
雖然在 Vue 3 中仍然可以使用 $emit,但通常只在選項式 API 中使用。在組合式 API 中,推薦使用 emit。//vue2子組件
<template><div><button @click="sendMessage">發送消息到父組件</button></div>
</template><script>
export default {methods: {sendMessage() {this.$emit('message-標識-sent', '傳遞的值');}}
};
</script>//vue2父組件
<template><div><ChildComponent @message-標識-sent="handleMessage" /><p>從子組件接收到的消息:{{ childMessage }}</p></div>
</template><script>
import ChildComponent from './ChildComponent.vue';export default {components: {ChildComponent},data() {return {childMessage: ''};},methods: {handleMessage(message) {this.childMessage = message;}}
};
</script>//vue3子組件
<template><div><button @click="sendMessage">發送消息到父組件</button></div>
</template><script setup>
import { defineEmits } from 'vue';const emit = defineEmits(['message-標識-sent']);const sendMessage = () => {emit('message-標識-sent', '傳遞的值');
};
</script>//vue3父組件
<template><div><ChildComponent @message-標識-sent="handleMessage" /><p>從子組件接收到的消息:{{ childMessage }}</p></div>
</template><script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';const childMessage = ref('');const handleMessage = (message) => {childMessage.value = message;
};
</script>三、雙向綁定(v-model)在 Vue 2 中,v-model 默認綁定到子組件的 value 屬性和 input 事件。如果需要自定義 v-model,可以通過 model 選項來實現。在 Vue 3 中,v-model 的使用更加靈活,可以通過 defineProps 和 defineEmits 來實現自定義 v-model。//vue2子組件
<template><div><input :value="value" @input="$emit('input', $event.target.value)" /></div>
</template><script>
export default {props: ['value']
};
</script>//vue2父組件
<template><div><ChildComponent v-model="parentValue" /><p>父組件的值:{{ parentValue }}</p></div>
</template><script>
import ChildComponent from './ChildComponent.vue';export default {components: {ChildComponent},data() {return {parentValue: 'Initial Value'};}
};
</script>//vue3子組件
<template><div><input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" /></div>
</template><script setup>
import { defineProps, defineEmits } from 'vue';const props = defineProps({modelValue: String
});const emit = defineEmits(['update:modelValue']);
</script>//vue3父組件
<template><div><ChildComponent v-model="parentValue" /><p>父組件的值:{{ parentValue }}</p></div>
</template><script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';const parentValue = ref('Initial Value');
</script>
30、let、 var 、const的區別?
1、作用域(1)var 聲明的變量在函數內有效,如果在全局作用域中聲明,則在全局范圍內有效,會變量提升(2)let 聲明的變量在塊級作用域內有效(如 if、for 等),不會變量提升(3)const 聲明的變量在塊級作用域內有效(如 if、for 等),不會變量提升
2、可變性 (1)var 聲明的變量可以被重新賦值。(2)let 聲明的變量可以被重新賦值(3)const 聲明的變量不能被重新賦值,但對象或數組的屬性或元素可以被修改。3、重復聲明(1)var 允許重復聲明變量,后面的聲明會覆蓋前面的聲明。(2)在同一個塊級作用域內,let 不允許重復聲明變量(3)在同一個塊級作用域內,const 不允許重復聲明變量
tips:優先使用 const:默認情況下,使用 const 聲明變量,除非需要重新賦值,才使用 let。避免使用 var:var 由于其提升行為和作用域問題,容易導致錯誤,應盡量避免使用。
31、在vue3組合式中,hooks是什么,有什么作用,如何封裝一個hooks
在 Vue 3 的組合式 API 中,Hooks(通常稱為組合式函數或 Composables)是一種用于封裝和復用有狀態邏輯的函數。這些函數可以包含響應式數據、計算屬性、生命周期鉤子等,從而實現代碼的高內聚和低耦合*Hooks 的作用:(1)代碼復用:允許在多個組件中共享邏輯,避免重復代碼。(2)邏輯分離:將特定功能的邏輯封裝到一個函數中,使組件的 setup 函數更加清晰。(3)響應式連接:返回的響應式數據與 Vue 的響應式系統相連,自動觸發組件更新。*封裝一個 Hook 的步驟如下:(1)定義函數:創建一個函數,通常以 use 開頭,例如 useCounter。(2)使用組合式 API:在函數內部使用 ref、reactive、computed、watch 等組合式 API 來創建響應式數據和邏輯。(3)返回對象:返回一個包含響應式數據和方法的對象。//封裝一個簡單的 useCounter Hook
// useCounter.js
import { ref } from 'vue';export function useCounter(initialValue = 0) {const count = ref(initialValue);const increment = () => {count.value++;};const decrement = () => {count.value--;};return {count,increment,decrement,};
}//在組件中使用封裝的 Hook
<template><div><p>Count: {{ count }}</p><button @click="increment">Increment</button><button @click="decrement">Decrement</button></div>
</template><script setup lang="ts">
import { useCounter } from './useCounter';const { count, increment, decrement } = useCounter(5);
</script>
//封裝一個更復雜的 Hook:useMouse
// useMouse.ts
import { onMounted, onUnmounted, ref } from 'vue';export function useMouse() {const x = ref(0);const y = ref(0);function update(event: MouseEvent) {x.value = event.pageX;y.value = event.pageY;}onMounted(() => window.addEventListener('mousemove', update));onUnmounted(() => window.removeEventListener('mousemove', update));return { x, y };
}//在組件中使用 useMouse
<template><div>Mouse position: {{ x }}, {{ y }}</div>
</template><script setup lang="ts">
import { useMouse } from './useMouse';const { x, y } = useMouse();
</script>
//封裝異步邏輯的 Hook:useFetch
// useFetch.ts
import { ref, onMounted } from 'vue';export function useFetch(url: string) {const data = ref(null);const loading = ref(true);const error = ref(null);const fetchData = async () => {try {loading.value = true;const response = await fetch(url);data.value = await response.json();} catch (err) {error.value = err;} finally {loading.value = false;}};onMounted(fetchData);return { data, loading, error, fetchData };
}//在組件中使用 useFetch
<template><div><p v-if="loading">Loading...</p><p v-else-if="error">Error: {{ error }}</p><div v-else><pre>{{ JSON.stringify(data, null, 2) }}</pre></div></div>
</template><script setup lang="ts">
import { useFetch } from './useFetch';const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/posts');
</script>
32、es6新增了哪些?
1、變量聲明
(1)let 和 const:這兩個關鍵字用于聲明變量。let 聲明的變量具有塊級作用域,避免了變量提升的問題;而const 用于聲明常量,一旦賦值后不可更改2、箭頭函數
3、模板字符串 使用反引號(`)包圍,并通過 ${} 插入變量。
4、解構賦值
5、Promise提供了三種狀態:pending(進行中)、fulfilled(已成功)和 rejected(已失敗)
6、擴展運算符
//合并對象 .assign
//把lisi 的所有的屬性 給了 zhangsan , lisi沒有任何的變化var zhangsan = {name:'zhangsan'};var lisi = {age:18};Object.assign(zhangsan,lisi)let res = Object.assign(zhangsan,lisi)//把lisi合并到zhangsan console.log(zhangsan)//{name: "zhangsan", age: 18}console.log(res)//{name: "zhangsan", age: 18}console.log(lisi)//{age: 18}
7、SymbolSymbol 是一種新的原始數據類型,常用于創建唯一的鍵名,以解決對象屬性名沖突的問題
8、Map 和 SetMap 和 Set 是新的數據結構,分別用于存儲鍵值對和集合,它們提供了更高效的數據操作方式
9、迭代器和生成器 迭代器接口和生成器函數(使用 yield 關鍵字)使得異步編程更加高效和優雅
10、默認參數 函數可以設置默認參數值,當調用函數時未傳遞參數時,將使用默認值
11、模塊化 ES6 引入了 import 和 export 語句,支持模塊化編程,使代碼組織更加清晰和可維護
12、其他特性剩余參數(Rest Parameters):用于將多余的參數收集到一個數組中。Proxy 和 Reflect:提供了更強大的元編程能力,Proxy 用于攔截對象操作,Reflect 提供了操作對象的方法
33、es6中的Proxy如何用?
一、作用Proxy 是 ES6 中引入的一個強大的特性,它允許你創建一個代理對象,從而攔截并自定義對目標對象的操作。通過 Proxy,你可以對對象的讀取、設置、刪除等操作進行攔截和控制。二、基本語法const proxy = new Proxy(target, handler);target:要代理的目標對象(可以是任何類型的對象,包括數組、函數等)。handler:一個對象,其屬性是當執行各種操作時定義代理的行為的函數。常見的攔截操作:(1)get(target, prop, receiver):攔截對象屬性的讀取操作。(2)set(target, prop, value, receiver):攔截對象屬性的設置操作。(3)has(target, prop):攔截 in 操作符。(4)deleteProperty(target, prop):攔截 delete 操作符。(5)ownKeys(target):攔截Object.getOwnPropertyNames和Object.getOwnPropertySymbols。(6)apply(target, thisArg, argumentsList):攔截函數調用。(7)construct(target, argumentsList, newTarget):攔截 new 操作符。三、示例代碼1、攔截屬性的讀取和設置const target = {name: 'Kimi',age: 25};const handler = {get(target, prop, receiver) {console.log(`Getting property: ${prop}`);return Reflect.get(target, prop, receiver);},set(target, prop, value, receiver) {console.log(`Setting property: ${prop} to ${value}`);return Reflect.set(target, prop, value, receiver);}};const proxy = new Proxy(target, handler);console.log(proxy.name); // 輸出: Getting property: nameproxy.age = 26; // 輸出: Setting property: age to 26console.log(proxy.age); // 輸出: Getting property: age2、攔截in操作符const target = {name: 'Kimi',age: 25};const handler = {has(target, prop) {console.log(`Checking if property exists: ${prop}`);return Reflect.has(target, prop);}};const proxy = new Proxy(target, handler);console.log('name' in proxy); // 輸出: Checking if property exists: nameconsole.log('gender' in proxy); // 輸出: Checking if property exists: gender3、攔截delete 操作符const target = {name: 'Kimi',age: 25};const handler = {deleteProperty(target, prop) {console.log(`Deleting property: ${prop}`);return Reflect.deleteProperty(target, prop);}};const proxy = new Proxy(target, handler);delete proxy.name; // 輸出: Deleting property: nameconsole.log(target); // 輸出: { age: 25 }4、攔截函數調用const target = function (x, y) {return x + y;};const handler = {apply(target, thisArg, argumentsList) {console.log(`Calling function with arguments: ${argumentsList}`);return Reflect.apply(target, thisArg, argumentsList);}};const proxy = new Proxy(target, handler);console.log(proxy(2, 3)); // 輸出: Calling function with arguments: 2,3
34、事件循環
一、瀏覽器有哪些進程和線程?瀏覽器是?個多進程多線程的應?程序1. 瀏覽器進程主要負責界?顯示、?戶交互、?進程管理等。瀏覽器進程內部會啟動多個線程處理不同的任務。2. ?絡進程負責加載?絡資源。?絡進程內部會啟動多個線程來處理不同的?絡任務。3. 渲染進程(本節課重點講解的進程)渲染進程啟動后,會開啟?個渲染主線程,主線程負責執? HTML、CSS、JS 代碼。默認情況下,瀏覽器會為每個標簽?開啟?個新的渲染進程,以保證不同的標簽?之間不相互影響。
二、渲染主線程是如何?作的?渲染主線程是瀏覽器中最繁忙的線程,需要它處理的任務包括但不限于:解析 HTML解析 CSS計算樣式布局處理圖層每秒把??畫 60 次執?全局 JS 代碼執?事件處理函數執?計時器的回調函數
35、如何理解 JS 的異步?
JS是??單線程的語?,這是因為它運?在瀏覽器的渲染主線程中,?渲染主線程只有?個。
?渲染主線程承擔著諸多的?作,渲染??、執? JS 都在其中運?。如果使?同步的?式,就極有可能導致主線程產?阻塞,從?導致消息隊列中的很多其他任務?法得到執?。這樣?來,???會導致繁忙的主線程?
?的消耗時間,另???導致???法及時更新,給?戶造成卡死現象。所以瀏覽器采?異步的?式來避免。具體做法是當某些任務發?時,?如計時器、?絡、事件監聽,主線程將任務交給其他線程去處理,?身?即結束任務的執?,轉?執?后續代碼。當其他線程完成時,將事先傳遞的回調函數包裝成任務,加?到消息隊列的末尾排隊,等待主線程調度執?。
在這種異步模式下,瀏覽器永不阻塞,從?最?限度的保證了單線程的流暢運?
36、闡述?下 JS 的事件循環?
事件循環?叫做消息循環,是瀏覽器渲染主線程的?作?式。在 Chrome 的源碼中,它開啟?個不會結束的 for 循環,每次循環從消息隊列中取出第?個任務執?,?其他線程只需要在合適的時候將任務加?到隊列末尾即可。過去把消息隊列簡單分為宏隊列和微隊列,這種說法?前已?法滿?復雜的瀏覽器環境,取?代之的是?種更加靈活多變的處理?式。
根據 W3C 官?的解釋,每個任務有不同的類型,同類型的任務必須在同?個隊列,不同的任務可以屬于不同的隊列。不同任務隊列有不同的優先級,在?次事件循環中,由瀏覽器??決定取哪?個隊列的任務。但瀏覽器必須有?個微隊列,微隊列的任務?定具有最?的優先級,必須優先調度執?。
37、Cookie、sessionStorage、localStorage 的區別?
相同點:
? 存儲在客?端
不同點:? cookie數據??不能超過4k;sessionStorage和localStorage的存儲?cookie?得多,可以達到 5M+ ? cookie設置的過期時間之前?直有效;localStorage永久存儲,瀏覽器關閉后數據不丟失除?主動 刪除數據;sessionStorage數據在當前瀏覽器窗?關閉后?動刪除? cookie的數據會?動的傳遞到服務器;sessionStorage和localStorage數據保存在本地
38、粘包問題分析與對策?
TCP粘包是指發送?發送的若?包數據到接收?接收時粘成?包,從接收緩沖區看,后?包數據的頭緊 接著前?包數據的尾。
粘包出現原因
簡單得說,在流傳輸中出現,UDP不會出現粘包,因為它有消息邊界
粘包情況有兩種,?種是粘在?起的包都是完整的數據包 ,另?種情況是 粘在?起的包有不完整的 包 。
為了避免粘包現象,可采取以下?種措施:
(1)對于發送?引起的粘包現象,??可通過編程設置來避免, TCP提供了強制數據?即傳送的操作 指令push ,TCP軟件收到該操作指令后,就?即將本段數據發送出去,?不必等待發送緩沖區滿;
(2)對于接收?引起的粘包,則可通過優化程序設計、精簡接收進程?作量、 提?接收進程優先級 等措施 ,使其及時接收數據,從?盡量避免出現粘包現象;
(3)由接收?控制,將?包數據按結構字段,?為控制分多次接收,然后合并,通過這種?段來避免 粘包。 分包多發 。 以上提到的三種措施,都有其不?之處。 * 第?種編程設置?法雖然可以避免發送?引起的粘包,但它關閉了優化算法,降低了?絡發送 效率,影響應?程序的性能,?般不建議使?。 * 第?種?法只能減少出現粘包的可能性,但并不能完全避免粘包,當發送頻率較?時,或由于 ?絡突發可能使某個時間段數據包到達接收?較快,接收?還是有可能來不及接收,從?導致粘包。 * 第三種?法雖然避免了粘包,但應?程序的效率較低,對實時應?的場合不適合。 ?種?較周全的對策是:接收?創建?預處理線程,對接收到的數據包進?預處理,將粘連的包分 開。實驗證明這種?法是?效可?的。
39、HTTP 請求跨域問題
1. 跨域的原理
2. 跨域,是指瀏覽器不能執?其他?站的腳本。它是由瀏覽器的 同源策略 造成的。 同源策略,是瀏覽器對 JavaScript 實施的安全限制,只要 協議、域名、端? 有任何?個不同,都 被當作是不同的域。 跨域原理,即是通過各種?式, 避開瀏覽器的安全限制 。
3. 解決?案
4. 最初做項?的時候,使?的是jsonp,但存在?些問題,使?get請求不安全,攜帶數據較?,后來 也?過iframe,但只有主域相同才?,也是存在些問題,后來通過了解和學習發現使?代理和 proxy代理配合起來使??較?便,就引導后臺按這種?式做下服務器配置,在開發中使?proxy, 在服務器上使?nginx代理,這樣開發過程中彼此都?便,效率也?;現在h5新特性還有 windows.postMessage() ?
5. JSONP:ajax 請求受同源策略影響,不允許進?跨域請求,? script 標簽 src 屬性中的鏈 接卻可以訪問 跨域的 js 腳本,利?這個特性,服務端不再返回 JSON 格式的數據,?是 返回?段調?某個函 數的 js 代碼,在 src 中進?了調?,這樣實現了跨域。
6.步驟:i. 去創建?個script標簽ii. script的src屬性設置接?地址 iii. 接?參數,必須要帶?個?定義函數名,要不然后臺?法返回數據iv. 通過定義函數名去接受返回的數據 //動態創建 scriptvar script = document.createElement('script');// 設置回調函數function getData(data) { console.log(data);}//設置 script 的 src 屬性,并設 置請求地址script.src = 'http://localhost:3000/?callback=getData';// 讓 script ?效document.body.appendChild(script); JSONP 的缺點: JSON 只?持 get,因為 script 標簽只能使? get 請求; JSONP 需要后端配合返回指定格式的 數據。 document.domain 基礎域名相同 ?域名不同 ? window.name 利?在?個瀏覽器窗?內,載?所有的域名都是共享?個window.name ?CORS CORS(Cross-origin resource sharing)跨域資源共享 服務器設置對CORS的?持原理:服 務器設置Access-Control-Allow-Origin HTTP響應頭之后,瀏覽器將會允許跨域請求 ?proxy代理 ?前常??式,通過服務器設置代理 ?window.postMessage() 利?h5新特性window.postMessage()
40、http 和 https 的區別及優缺點?
? http 是超?本傳輸協議,信息是明?傳輸,HTTPS 協議要? http 協議 安全 ,https 是具有安全性 的 ssl 加密傳輸協議,可防?數據在傳輸過程中被竊取、改變,確保數據的完整性(當然這種安全性 并?絕對的,對于更深?的 Web 安全問題,此處暫且不表)。
? http 協議的 默認端? 為 80,https 的默認端?為 443。
? http 的連接很簡單,是?狀態的。https 握?階段?較 費時 ,會使??加載時間延? 50%,增加 10%~20%的耗電。
? https 緩存 不如 http ?效,會增加數據開銷。
? Https 協議需要 ca 證書,費?較?,功能越強?的 證書費 ?越?。
? SSL 證書需要綁定 域名 。
41、https 協議的?作原理
客?端在使? HTTPS ?式與 Web 服務器通信時有以下?個步驟:
1.客?端使? https url 訪問服務器,則要求 web 服務器 建? ssl 鏈接 。
2.web 服務器接收到客?端的請求之后,會 將?站的證書(證書中包含了公鑰),傳輸給客?端 。
3.客?端和 web 服務器端開始 協商 SSL 鏈接的安全等級 ,也就是加密等級。
4.客?端瀏覽器通過雙?協商?致的安全等級, 建?會話密鑰 ,然后通過?站的公鑰來加密會話密 鑰,并傳送給?站。
5.web 服務器 通過??的私鑰解密出會話密鑰 。
6.web 服務器 通過會話密鑰加密與客?端之間的通信 。
42、TCP三次握?
1. 第?次握?: 建?連接時,客?端發送syn包(syn=j)到服務器,并進?SYN_SENT狀態,等待 服務器確認 ;SYN:同步序列編號(Synchronize Sequence Numbers)。
2. 第?次握?: 服務器收到syn包并確認客?的SYN (ack=j+1), 同時也發送?個??的SYN包 (syn=k),即SYN+ACK包,此時服務器進?SYN_RECV狀態;
3. 第三次握?: 客?端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1) ,此包發 送完畢,客?端和服務器進?ESTABLISHED(TCP連接成功)狀態,完成三次握?。
4. 握?過程中傳送的包?不包含數據,三次握?完畢后,客?端與服務器才正式開始傳送數據。
43、TCP 四次揮?
1.客?端進程發出連接釋放報? ,并且停?發送數據。釋放數據報??部,FIN=1,其序列號為 seq=u(等于前?已經傳送過來的數據的最后?個字節的序號加1),此時, 客?端進?FIN- WAIT-1(終?等待1)狀態 。 TCP規定,FIN報?段即使不攜帶數據,也要消耗?個序號。
2.服務器收到連接釋放報?,發出確認報? ,ACK=1,ack=u+1,并且帶上??的序列號seq=v, 此時, 服務端就進?了CLOSE-WAIT(關閉等待)狀態 。TCP服務器通知?層的應?進程,客?端向 服務器的?向就釋放了,這時候處于半關閉狀態,即客?端已經沒有數據要發送了,但是服務器若發 送數據,客?端依然要接受。這個狀態還要持續?段時間,也就是整個CLOSE-WAIT狀態持續的時間。
3.客?端收到服務器的確認請求后,此時, 客?端就進?FIN-WAIT-2(終?等待2)狀態 ,等待 服務器發送連接釋放報?(在這之前還需要接受服務器發送的最 后的數據)。
4.服務器將最后的數據發送完畢后,就向客?端發送連接釋放報? ,FIN=1,ack=u+1,由于在半 關閉狀態,服務器很可能?發送了?些數據,假定此時的序列號為seq=w,此時, 服務器就進?了 LAST-ACK(最后確認)狀態 ,等待客?端的確認。
5.客?端收到服務器的連接釋放報?后,必須發出確認 ,ACK=1,ack=w+1,???的序列號是 seq=u+1,此時, 客?端就進?了TIME-WAIT(時間等待)狀態 。注意此時TCP連接還沒有釋放, 必須經過2X區MSL(最長報文段壽命)的時間后,當客戶端撤銷相應的TCB后,才進入CLOSED狀
6.服務器只要收到了客戶端發出的確認,立即進入CLOSED狀態 。同樣,撤銷TCB后,就結束了這次的TCP連接。可以看到,服務器結束TCP連接的時間要比客戶端早一些。
44、TCP/IP / 如何保證數據包傳輸的有序可靠?
對字節流分段并進?編號然后 通過 ACK 回復 和 超時重發 這兩個機制來保證。
(1)為了保證數據包的可靠傳遞,發送?必須把已發送的數據包保留在緩沖區;
(2)并為每個已發送的數據包啟動?個超時定時器;
(3)如在定時器超時之前收到了對?發來的應答信息(可能是對本包的應答,也可以是對本包后續包 的應答),則釋放該數據包占?的緩沖區;
(4)否則,重傳該數據包,直到收到應答或重傳次數超過規定的最?次數為?。
(5)接收?收到數據包后,先進?CRC校驗,如果正確則把數據交給上層協議,然后給發送?發送? 個累計應答包,表明該數據已收到,如果接收?正好也有數據要發給發送?,應答包也可?在數據包 中捎帶過去。
45、TCP和UDP的區別
1. TCP是?向 鏈接 的,?UDP是?向?連接的。
2. TCP僅?持 單播傳輸 ,UDP 提供了單播,多播,?播的功能。
3. TCP的三次握?保證了連接的 可靠性 ; UDP是?連接的、不可靠的?種數據傳輸協議,?先不可靠 性體現在?連接上,通信都不需要建?連接,對接收到的數據也不發送確認信號,發送端不知道數 據是否會正確接收。
4. UDP的 頭部開銷 ?TCP的更?,數據 傳輸速率更? , 實時性更好 。
46、vue3中組件懶加載如何做?
//一:使用 defineAsyncComponent 定義異步組件
<template><div><AsyncComponent /></div>
</template>
<script setup>
import { defineAsyncComponent } from 'vue';
//組件懶加載
const AsyncComponent = defineAsyncComponent(() =>import('./components/MyComponent.vue')
);//普通加載
//import AsyncComponent from '@/components/MyComponent.vue'
</script>//二:在路由中實現懶加載
//當用戶訪問 /foo 路由時,才會加載 Foo.vue 組件
const routes = [{path: '/foo',component: () => import('./Foo.vue')}
];//三:使用 <Suspense> 組件處理加載狀態
<template><Suspense>//默認加載的<template #default><Tableview /></template>//出錯時加載的<template #fallback><div class="loading-state"><FailView /><p>內容正在加載中,請稍候...</p></div></template></Suspense>
</template>
47、前端如何解決不同瀏覽器及不同版本的兼容問題?
在前端開發中,解決不同瀏覽器及不同版本的兼容性問題是一個常見的挑戰。以下是一些有效的解決方案和策略:
1. 使用標準的 HTML/CSS/JavaScript
遵循W3C等標準化組織制定的Web標準是解決兼容性問題的基礎。使用HTML5、CSS3和ES6等現代標準可以確保代碼在大多數現代瀏覽器中的兼容性。
2. 使用CSS Reset 或 Normalize.css
CSS Reset 可以消除瀏覽器默認樣式的差異,使不同瀏覽器中的樣式表現一致。常見的CSS Reset 庫有 Normalize.css 和 Reset.css。
Normalize.css:保留了一些有用的默認樣式,適合現代開發。
Reset.css:將所有元素的默認樣式重置為一致的基準。
3. 利用現代化工具和庫
使用現代化的工具和庫可以幫助開發者更容易地解決兼容性問題:
Babel:將 ES6+ 代碼編譯為 ES5,確保在舊瀏覽器中的兼容性。
PostCSS 和 Autoprefixer:自動為 CSS 屬性添加瀏覽器前綴,確保在不同瀏覽器中的兼容性。
4. 進行跨瀏覽器測試
跨瀏覽器測試是確保前端代碼在不同瀏覽器中兼容性的關鍵步驟:
BrowserStack:在線跨瀏覽器測試工具,支持真實設備和瀏覽器測試。
Sauce Labs:提供廣泛的瀏覽器和設備支持,支持自動化測試。
5. 采用漸進增強和優雅降級策略
漸進增強:從基礎功能開始構建網站,逐步添加高級功能,確保即使瀏覽器不支持某些高級功能,用戶仍能使用基本功能。
優雅降級:從高級功能開始構建網站,為不支持這些功能的瀏覽器提供替代方案。
6. 使用 Polyfill 和 Shim
Polyfill 和 Shim 可以在舊瀏覽器中實現新功能,提高兼容性:
Polyfill.io:根據瀏覽器特性動態加載所需的 Polyfill。
Modernizr:檢測瀏覽器特性,并根據特性加載所需的 Polyfill。
7. 使用成熟的框架和庫
使用成熟的前端框架和庫可以減少兼容性問題:
React:虛擬 DOM 和組件化設計,減少兼容性問題。
Vue.js:雙向數據綁定和組件化設計,提高開發效率。
Angular:模塊化設計和依賴注入,減少兼容性問題。
8. 注意 JavaScript 兼容性
對于 JavaScript 的兼容性問題,可以采用以下方法:
事件綁定:統一使用兼容性更好的事件綁定方法,如 addEventListener 和 attachEvent 的兼容寫法。
DOM 操作:避免使用過時的 DOM 方法,統一使用標準的 DOM 方法。
變量聲明:在聲明變量時,一律加上 var 關鍵字,避免歧義。
9. 使用 CSS 前綴
為 CSS 屬性添加瀏覽器前綴可以確保在不同瀏覽器中的兼容性。例如,使用 Autoprefixer 自動添加前綴。
10. 關注瀏覽器更新
及時關注瀏覽器的更新和變化,可以幫助開發者更好地解決兼容性問題。
通過以上方法,可以有效解決前端開發中的瀏覽器兼容性問題,提高用戶體驗和代碼質量。
48、webSocket的使用
WebSocket是在單個TCP連接上提供全雙工通信的協議。它允許雙方同時進行數據傳輸,而不需要等待對方的響應,簡單說就是他是服務器和客戶端相互主動傳輸信息的約定協議。
優點:
雙向通信:WebSocket 允許客戶端和服務器之間進行雙向通信,客戶端可以主動向服務器發送消息,而不需要等待服務器的請求
低延遲:WebSocket 連接在建立后保持打開狀態,減少了每次請求和響應所需的開銷,從而降低了延遲。
持久連接:WebSocket 連接是持久的,允許長時間保持連接而無需重新建立。
跨域支持:WebSocket 協議支持跨域通信,可以與不同源的服務器進行交互。
總之,WebSocket 提供了一種高效、靈活的方式來實現實時數據傳輸,特別適合需要快速和頻繁更新數據的應用程序。//javascript代碼
// 初始化 WebSocket 實例,連接到指定的 WebSocket 服務器 (后端地址10.10.10.100:8080)
const webSocket = new WebSocket('ws://10.10.10.100:8080/webSocket');
//檢查WebSocket連接狀態 if (webSocket.readyState === WebSocket.CONNECTING) {console.log("連接正在建立..."); } else if (webSocket.readyState === WebSocket.OPEN) {console.log("連接已建立!"); } else if (webSocket.readyState === WebSocket.CLOSING) {console.log("連接正在關閉..."); } else if (webSocket.readyState === WebSocket.CLOSED) {console.log("連接已關閉!"); }//連接成功時的回調函數
webSocket.onopen = (event)=>{console.log('連接已建立!'); //在這里可以執行連接成功后的操作,例如發送數據webSocket.send('Hello, WebSocket Server!');
};//接收到消息時的回調函數
webSocket.onmessage = (event)=>{//在這里可以處理接收到的消息console.log('接收到消息:', event.data);
};//連接關閉時的回調函數
webSocket.onclose = (event)=> {// 在這里可以執行連接關閉后的操作console.log('連接已關閉!');
};//連接發生錯誤時的回調函數
webSocket.onerror = (error)=> {//在這里可執行連接失敗可重連console.error('連接發生錯誤:', error);
};
48.5vue3項目如何使用 Websocket 及時通信技術,處理審批消息,提高系統的實時性和效率
在 Vue 3 項目中使用 WebSocket 實現實時通信技術,可以顯著提高系統的實時性和效率,特別是在處理審批消息等需要即時反饋的場景中。以下是一個基本的實現步驟和示例:實現步驟1、引入 WebSocket 客戶端庫:你可以使用瀏覽器原生的 WebSocket 對象,或者使用第三方庫(如 socket.io-client)來簡化 WebSocket 的使用。2、建立 WebSocket 連接:在 Vue 組件的生命周期鉤子中(如 onMounted)建立 WebSocket 連接。3、處理 WebSocket 消息:定義消息處理函數,用于接收和處理來自服務器的消息。4、發送 WebSocket 消息:根據需要,通過 WebSocket 連接發送消息到服務器。5、關閉 WebSocket 連接:在組件卸載時(如 onUnmounted)關閉 WebSocket 連接,以釋放資源。示例代碼以下是一個使用原生 WebSocket 的簡單示例:<template><div><h1>審批消息</h1><ul><li v-for="message in messages" :key="message.id">{{ message.content }}</li></ul></div>
</template><script>
import { ref, onMounted, onUnmounted } from 'vue';export default {setup() {const messages = ref([]);let socket;let reconnectInterval = 5000; // 重連間隔時間(毫秒)let heartbeatInterval;const heartbeatMessage = JSON.stringify({ type: 'ping' }); // 心跳消息格式const connectWebSocket = () => {socket = new WebSocket('ws://your-websocket-server-url');socket.onopen = () => {console.log('WebSocket connection established');// 可以在這里發送初始消息或訂閱特定頻道startHeartbeat();};socket.onmessage = (event) => {const message = JSON.parse(event.data);messages.value.push(message);};socket.onerror = (error) => {console.error('WebSocket error:', error);// 可以在這里執行一些錯誤處理邏輯,但通常重連邏輯在 onclose 中處理};socket.onclose = () => {console.log('WebSocket connection closed');stopHeartbeat();// 嘗試重連setTimeout(connectWebSocket, reconnectInterval); };};//心跳消息const startHeartbeat = () => {if (!heartbeatInterval) {heartbeatInterval = setInterval(() => {if (socket && socket.readyState === WebSocket.OPEN) {socket.send(heartbeatMessage);}}, 30000); // 每30秒發送一次心跳消息}};const stopHeartbeat = () => {if (heartbeatInterval) {clearInterval(heartbeatInterval);heartbeatInterval = null;}}; onMounted(() => {connectWebSocket();});onUnmounted(() => {if (socket) {socket.close();}});return {messages,};},
};
</script><style scoped>
/* 添加一些基本樣式 */
</style>1、自動重連機制:
在 onclose 回調中,使用 setTimeout 調用 connectWebSocket 函數,嘗試在連接關閉后重新連接。
reconnectInterval 變量控制重連的時間間隔(這里設置為 5000 毫秒,即 5 秒)。2、心跳檢測:
使用 setInterval 定期發送心跳消息(如每 30 秒發送一次)。
startHeartbeat 函數用于啟動心跳檢測,stopHeartbeat 函數用于停止心跳檢測。
在 onopen 回調中啟動心跳檢測,在 onclose 回調中停止心跳檢測。
3、WebSocket 狀態檢查:
在發送心跳消息前,檢查 socket.readyState 是否為 WebSocket.OPEN,以確保連接是打開的。
4、資源釋放:
在組件卸載時(onUnmounted),關閉 WebSocket 連接并停止心跳檢測,以釋放資源。
49、route和router的區別?
一: router(路由器)
1、定義:*router 是一個路由管理器,它負責管理整個應用的路由規則和導航邏輯。它是一個全局對象,通常在整個應用中只有一個實例。*它定義了如何根據不同的 URL 映射到不同的頁面或組件,并提供了方法來控制導航(例如跳轉、返回等)。
2、職責:
*定義路由規則:router 配置了所有可能的路由路徑(URL)以及對應的組件或頁面。
*監聽 URL 變化:當用戶在瀏覽器中輸入 URL 或點擊鏈接時,router 會監聽這些變化,并根據配置的路由規則進行處理。
*控制導航:router 提供了方法(如 push、replace、go 等)來編程式地控制頁面跳轉。
*管理路由狀態:router 管理整個應用的路由狀態,包括當前的路徑、歷史記錄等。
import { createRouter, createWebHistory } from 'vue-router';
import Home from './views/Home.vue';
import About from './views/About.vue';
const router = createRouter({history: createWebHistory(),routes: [{ path: '/', component: Home },{ path: '/about', component: About }]
});
export default router;二:route(路由)
1、定義:route 是一個具體的路由記錄,它表示一個特定的路徑(URL)以及與之關聯的組件、配置等信息。每個 route 都是一個對象,包含了路徑、組件、名稱、元信息等屬性。
2、職責:表示一個路由路徑:route 定義了一個具體的 URL 路徑。關聯組件:route 指定了當用戶訪問該路徑時,應該渲染哪個組件。攜帶參數:route 可以攜帶參數(如動態路由參數、查詢參數等)。元信息:route 可以包含一些元信息(如標題、權限等),用于在導航時進行額外的處理。
const routes = [{path: '/user/:id',component: User,name: 'User',meta: { requiresAuth: true } // 元信息}
];
50、路由的模式有哪兩種,有啥區別,優缺點是啥?
在前端開發中,常見的路由模式主要有兩種:HashRouter 和 BrowserRouter。它們的主要區別在于URL的表現形式以及對服務器配置的要求。以下是它們的定義、區別、優缺點:
1. HashRouter(哈希模式)定義:通過在URL的哈希部分(#)進行路由,不會與服務器進行實際通信。優點:兼容性好,可以在不同的服務器上正常工作。不需要服務器配置,適合靜態頁面或簡單的單頁應用(SPA)。缺點:URL中包含哈希(#),不太美觀。可能影響搜索引擎優化(SEO),因為搜索引擎可能不會解析哈希部分。不支持 HTML5的history.pushState和history.replaceState 方法。
2. BrowserRouter(歷史模式)定義:使用 HTML5 的 history API 來處理路由,URL 更接近傳統的路徑形式。優點:URL 更美觀,沒有哈希(#),用戶體驗更好。支持 HTML5 的 history.pushState 和 history.replaceState方法,可以實現更復雜的路由邏輯。缺點:需要服務器配置支持,服務器需要能夠處理所有路徑并返回同一個HTML文件。如果服務器配置不當,可能會導致頁面刷新時找不到路由。
3. 選擇哪種模式?選擇 HashRouter:如果你的應用不需要復雜的服務器配置,或者你希望快速部署一個簡單的單頁應用。如果你擔心服務器配置的復雜性,或者你的服務器不支持HTML5的history API。選擇 BrowserRouter:如果你希望提供更美觀的URL,并且能夠更好地支持搜索引擎優化(SEO)。如果你的服務器可以配置,或者你已經在使用支持 HTML5 的 history API 的服務器。
總結
HashRouter:適合簡單應用,不需要服務器配置,但URL不夠美觀。
BrowserRouter:適合需要更美觀URL的應用,但需要服務器配置支持。
51、判斷數據類型的方式有哪些?
1. typeof 運算符
typeof 是一個運算符,用于返回變量的數據類型。它返回一個字符串,表示變量的類型。
語法:typeof value;
"string":字符串
"number":數字
"boolean":布爾值
"object":對象、數組、null
"function":函數
"undefined":未定義
"symbol":ES6 中的 Symbol 類型
"bigint":ES2020 中的 BigInt 類型2. instanceof 運算符
instanceof 用于檢測一個對象是否是某個構造函數的實例。它檢查對象的原型鏈中是否包含構造函數的 prototype 屬性。
語法:object instanceof Constructor;
示例:
class MyClass {}
const obj = new MyClass();
console.log(obj instanceof MyClass); // true
console.log(obj instanceof Object); // true(因為 MyClass 的原型鏈上包含 Object.prototype)console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
console.log(null instanceof Object); // false3. Array.isArray()
Array.isArray() 是一個內置方法,用于檢測一個值是否是數組。它返回一個布爾值。
語法: Array.isArray(value);
示例:
console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
console.log(Array.isArray("hello")); // false
console.log(Array.isArray(null)); // false4. Object.prototype.toString.call()
Object.prototype.toString.call() 是一種更通用的方法,用于檢測數據類型。它返回一個表示類型的字符串,格式為 [object Type]。
語法:Object.prototype.toString.call(value);
示例:
console.log(Object.prototype.toString.call("hello")); // "[object String]"
console.log(Object.prototype.toString.call(123)); // "[object Number]"
console.log(Object.prototype.toString.call(true)); // "[object Boolean]"
console.log(Object.prototype.toString.call({})); // "[object Object]"
console.log(Object.prototype.toString.call([])); // "[object Array]"
console.log(Object.prototype.toString.call(null)); // "[object Null]"
console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]"
console.log(Object.prototype.toString.call(function() {})); // "[object Function]"
console.log(Object.prototype.toString.call(Symbol('sym'))); // "[object Symbol]"
console.log(Object.prototype.toString.call(BigInt(123))); // "[object BigInt]"5. constructor 屬性
每個對象都有一個 constructor 屬性,指向創建它的構造函數。通過 constructor 屬性,可以判斷對象的類型。
示例:
const obj = {};
console.log(obj.constructor === Object); // trueconst arr = [];
console.log(arr.constructor === Array); // trueconst str = "hello";
console.log(str.constructor === String); // trueconst num = 123;
console.log(num.constructor === Number); // trueconst fn = function() {};
console.log(fn.constructor === Function); // true6. ArrayBuffer、TypedArray 和其他特殊類型
對于一些特殊類型(如 ArrayBuffer、TypedArray 等),可以使用 instanceof 或Object.prototype.toString.call() 來檢測。7. 判斷 null 和 undefined
由于 typeof null 返回 "object",因此需要特別處理 null 和 undefined。8. 判斷對象是否為空
可以使用 Object.keys() 或 JSON.stringify() 來判斷對象是否為空。總結
typeof:簡單快捷,但對 null 和數組的判斷不夠準確。
instanceof:用于檢測對象是否是某個構造函數的實例。
Array.isArray():專門用于檢測數組。
Object.prototype.toString.call():最通用的方法,可以準確判斷所有類型。
constructor:通過構造函數判斷類型,但可能被修改。
特殊類型:使用 instanceof 或 Object.prototype.toString.call() 檢測。
52、數組去重方法?
1、利用Array.from(new Set)去重
2、利用includes去重
3、利用map去重
4、利用indexOf去重
5、利用單層for循環去重
6、利用雙層for循環去重
7、利用遞歸去重
8、利用Array.filter和map對象數組去重 (性能較高)
9、利用Array.filter和Array.includes 對象數組去重1、利用Array.from(new Set)去重
// Set是es6新增的數據結構,似于數組,但它的一大特性就是所有元素都是唯一的,沒有重復的值,
// Array.from()就是將一個類數組對象或者可遍歷對象轉換成一個真正的數組,也是ES6的新增方法
let list = ['牛牛牛', 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8, 8,8,'牛牛牛',]
let newList = Array.from(new Set(list))
console.log(newList);2、利用includes去重
利用Array.includes 去重
//includes() 方法用來判斷一個數組是否包含一個指定的值,如果是返回true,否則false。
let list = ['牛牛牛', 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8, 8,8,'牛牛牛',]
let newList = []
list.forEach((item) => {// 空數組newList 不包含item為false ,取反為true 執行數組添加操作// 如果數組包含了 item為true 取反為false 不執行數組添加操作if (!newList.includes(item)) {newList.push(item)}})console.log(newList);3、利用map去重
//map數據結構是es6中新出的語法,其本質也是鍵值對,只是其鍵不局限于普通對象的字符串
let list = ['牛牛牛', 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8, 8,8,'牛牛牛',]
let newList = [];
let map = new Map()
list.forEach((item) => {// 如果map.has指定的item不存在,那么就設置key和value 這個item就是當前map里面不存在的key,把這個item添加到新數組// 如果下次出現重復的item,那么map.has(item等于true取反 !map.has(item) 不執行if (!map.has(item)) {map.set(item,true)newList.push(item)}})console.log(newList);4、利用indexOf去重
//indexOf() 方法可返回某個指定的字符串值在字符串中首次出現的位置。如果沒有找到匹配的字符串則返回 -1
let list = ['牛牛牛', 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8, 8,8,'牛牛牛',]
let newList = [];
list.forEach((item) => {// 空數組newList4第一次循環沒有找到匹配的item 返回-1 執行數組添加操作// 如果數組在第n次循環中找到了newList4數組中item 例如:item等于6 而在newList4數組中已經有9 所以indexOf就不等于-1 不執行數組添加操作if (newList.indexOf(item) === -1) {newList.push(item)}})console.log(newList);5、利用單層for循環去重
// Array.splice() 方法用于添加或刪除數組中的元素。會改變原數組
let list = ['牛牛牛', 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8, 8,8,'牛牛牛',]
let newList = [];
for (let i = 0; i < list.sort().length; i++) {if (list[i] == list[i + 1]) {list.splice(i, 1)i--}}console.log(list);6、利用雙層for循環去重
// Array.splice() 方法用于添加或刪除數組中的元素。會改變原數組
let list = ['牛牛牛', 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8, 98,8,8,'牛牛牛',]for (let i = 0; i < list.sort().length; i++) {for (let j = i + 1; j < list.sort().length; j++) {if (list[i] == list[j]) {list.splice(i, 1)j--}}}console.log(list);7、利用遞歸去重
let list = ['牛牛牛', 1, 1, 2, 2, 3, 1.06,0.2,3, 4, 4, 0.2,5, 5, 6, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8, 98,8,8,'牛牛牛',]
function callBack(index) {// 數組的長度不能等于0if (index >= 1) {// 第一次如果數組最后一個索引和最后第二個索引相等if (list[index] === list[index - 1]) {// 那么就刪除這個索引的元素list.splice(index, 1)}// 繼續調用這個函數 第一次傳入的參數是數組末尾第一個 第二次傳入的參數是數組末尾第二個 層層遞減callBack(index - 1)}}//傳入排序好的數組的最大索引indexcallBack(list.sort().length - 1)console.log(list);8、利用Array.filter和map對象數組去重 (性能較高)
let map = new Map()
let list = [{name: '牛牛牛1',id: 1},{name: '牛牛牛1',id: 2},{name: '牛牛牛2',id: 3},{name: '牛牛牛3',id: 3},{name: '牛牛ya牛3',id: 4},{name: '牛ya牛ya牛3',id: 4}]// 對象數組去重function fn(list, key) {return list.filter((item) => !map.has(item[key].toString()) && map.set(item[key].toString()))}console.log(fn(list, 'id'));9、利用Array.filter和Array.includes 對象數組去重
let list = [{name: '牛牛牛1',id: 1},{name: '牛牛牛1',id: 2},{name: '牛牛牛2',id: 3},{name: '牛牛牛3',id: 3},{name: '牛牛ya牛3',id: 4}]function fn(arr) {let list = [];return arr.filter((item) => !list.includes(item.name) && list.push(item.name))}console.log( fn(list));
53、常見的HTTP響應狀態碼?
通常會出現五種狀態碼:
100 ~ 199
200 ~ 299
300 ~ 399
400 ~ 499
500 ~ 5991、100 ~ 199一般我們看不到,因為表示請求繼續100: 繼續請求,前面的一部分內容服務端已經接受到了,正在等待后續內容101: 請求者已經準備切換協議,服務器頁表示同意
2、200 ~ 2992 開頭的都是表示成功,本次請求成功了,只不過不一樣的狀態碼有不一樣的含義(語義化)200: 標準請求成功(一般表示服務端提供的是網頁)**201: 創建成功(一般是注冊的時候,表示新用戶信息已經添加到數據庫)203: 表示服務器已經成功處理了請求,但是返回的信息可能來自另一源204: 服務端已經成功處理了請求,但是沒有任何數據返回3、300 ~ 3993 開頭也是成功的一種,但是一般表示重定向301: 永久重定向****302: 臨時重定向304: 使用的是緩存的數據*******(get 請求默認都會被緩存)305: 使用代理
4、400 ~ 4994 開頭表示客戶端出現錯誤了400: 請求的語法服務端不認識401: 未授權(你要登錄的網站需要授權登錄)403: 服務器拒絕了你的請求****404: 服務器找不到你請求的 URL*****407: 你的代理沒有授權408: 請求超時410: 你請求的數據已經被服務端永久刪除
5、500 ~ 5995 開頭的表示服務端出現了錯誤500: 服務器內部錯誤 ****503: 服務器當前不可用(過載或者維護)505: 請求的協議服務器不支持
54、關于Promise
一:promise的使用用來封裝異步操作的,專門用來解決異步 回調地獄 的問題,先創建一個promise對象, 創建一個函數參數,這個函數參數又有兩個參數resolve, reject, 函數參數中寫異步操作,操作成功調用resolve,操作失敗調用reject, 然后promise對象就可以知道異步操作的狀態,p.then 成功的時執行,p.catch失敗的時候執行二:PROMISE(許諾,承諾)(es6)
promise的語法:
let p = new Promise(function (resolve, reject) {//異步操作 異步操作成功調用reslove(res) 異步操作失敗的調用 reject()// resolve 表示成功的回調// reject 表示失敗的回調
});p.then(function (res) {// 如果 p內部調用了resolve(結果) ,這個函數就會執行,resolve的結果會傳遞給 這里的形參res
})
p.catch(function (err) {// 如果 p內部調用了reject(結果) ,這個函數就會執行//reject的結果會傳遞給 這里的形參err
})
p.finally(function () {//不管是調用了成功還是失敗,finally都會執行,最終執行的函數--一般用的少
});promise 是狀態機 -- 記錄了內部的異步操作的狀態 1-promise 創建以后就處于 進行中 pending(執行中狀態)2-一旦調用 resolve 就變成了 fulfilled 完成狀態(成功狀態)3-一旦調用 reject 就變成了 rejected 失敗狀態(失敗狀態)三:Promise的靜態方法-race-all
1、Promise.all()
const p = Promise.all([p1, p2, p3]);
p.then(function(){})
.catch(function(){})上面代碼中,Promise.all()方法接受一個數組作為參數,p1、p2、p3都是 Promise 實例,
p的狀態由p1、p2、p3決定,分成兩種情況。(1)只有p1、p2、p3的狀態都變成fulfilled,p的狀態才會變成fulfilled,此時p1、p2、p3的返回值組成一個數組,傳遞給p的回調函數。
(2)只要p1、p2、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。2、Promise.race()
const p = Promise.race([p1, p2, p3]);
上面代碼中,只要`p1`、`p2`、`p3`之中有一個實例率先改變狀態,`p`的狀態就跟著改變。那個率先改變的 Promise 實例的返回值,就傳遞給`p`的回調函數3、Promise.resolve()
Promise.resolve('foo')
// 等價于
new Promise(resolve => resolve('foo'))4、Promise.reject()
const p = Promise.reject('出錯了');
// 等同于
const p = new Promise((resolve, reject) => reject('出錯了'))
p.then(null, function (s) {// 出錯了console.log(s)
});
55、Promise和async-await的區別,有何有缺點
Promise和async/await都是JavaScript中用于處理異步操作的重要工具,它們之間存在一些顯著的區別,并且各自具有優缺點。一、區別
語法結構:
Promise使用.then()和.catch()方法來處理異步操作的結果和異常。
async/await則使用async關鍵字定義異步函數,使用await關鍵字等待異步操作的結果,并且可以使用try...catch語句處理異常。
代碼可讀性:
Promise的鏈式調用可能會導致代碼結構復雜,多層嵌套使得代碼難以閱讀和維護。
async/await則讓異步代碼看起來更像同步代碼,減少了回調函數的嵌套,提高了代碼的可讀性和可維護性。
錯誤處理:
Promise的錯誤處理依賴于.catch()方法,如果錯誤發生在鏈式調用的中間環節,可能需要多個.catch()來捕獲。
async/await則可以使用try...catch語句直接捕獲異步操作中的錯誤,使得錯誤處理更加直觀和方便。
并發處理:
Promise可以并行執行多個異步操作,使用Promise.all()等方法可以等待所有異步操作完成。
async/await在處理多個異步操作時,默認是串行執行的,如果需要并行處理,則需要結合Promise使用,如Promise.all([asyncOperation1(), asyncOperation2()])。二、優缺點
Promise:優點:
提供了集中處理錯誤的機制,通過.catch()方法捕獲整個鏈式調用中的任何錯誤。
可以通過Promise.all()等方法并發執行多個異步操作,提高代碼性能。
鏈式調用語法使得代碼在某些情況下可讀性更高(盡管也可能導致代碼復雜)。
缺點:
鏈式調用可能導致代碼結構復雜,多層嵌套難以維護。
錯誤處理可能不夠直觀,需要多個.catch()來捕獲錯誤。
一旦Promise被創建,它將立即執行,無法中途取消。
async/await:優點:
讓異步代碼看起來像同步代碼,減少了回調函數的嵌套,提高了代碼的可讀性和可維護性。
使用try...catch語句處理異步操作中的錯誤,使得錯誤處理更加直觀和方便。
提供了更好的調試體驗,可以像調試同步代碼一樣使用斷點和堆棧跟蹤。
缺點:
在處理多個異步操作時,默認是串行執行的,不如Promise方便處理并發請求。
相對于Promise來說更加新,可能不支持所有版本的JavaScript引擎。
在某些情況下,處理異步操作的取消可能比較困難,需要額外的邏輯來實現。
56、數組中有哪些方法,哪些方法是不改變原數組的
一:改變原數組的方法
push():在數組末尾添加一個或多個元素,并返回新的數組長度。
pop():從數組末尾刪除一個元素,并返回該元素的值。
shift():從數組開頭刪除一個元素,并返回該元素的值。
unshift():在數組開頭添加一個或多個元素,并返回新的數組長度。
splice():通過刪除或替換現有元素或者添加新元素來修改數組,并返回被刪除的元素組成的數組。
sort():對數組的元素進行排序,并返回數組。默認按照字符串的Unicode碼點排序,但可以通過提供比較函數來定制排序順序。
reverse():顛倒數組中元素的順序,并返回數組。二:不改變原數組的方法
concat():用于合并兩個或多個數組,并返回一個新數組。原數組不會被改變。
slice():返回一個新的數組對象,這個新數組包含從開始到結束(不包括結束)選擇的一部分原數組的淺拷貝。原數組不會被改變。
map():創建一個新數組,其結果是原數組中的每個元素都調用一個提供的函數后的返回值。原數組不會被改變。
filter():創建一個新數組,其包含通過所提供函數實現的測試的所有元素。原數組不會被改變。
reduce() 和 reduceRight():對數組中的每個元素執行一個由提供的reducer函數(升序執行或降序執行),將其結果匯總為單個返回值。原數組不會被改變。
forEach():對數組的每個元素執行一次提供的函數。雖然這個方法會對數組的每個元素進行操作,但它并不改變原數組的內容。
some() 和 every():測試數組的某些或每個元素是否通過由提供的函數實現的測試。這些方法不會改變原數組。
find() 和 findIndex():返回數組中滿足提供的測試函數的第一個元素的值(或它的索引),否則返回undefined(或-1)。原數組不會被改變。
includes():判斷一個數組是否包含一個特定的值,并返回true或false。原數組不會被改變。
indexOf() 和 lastIndexOf():返回在數組中可以找到給定元素的第一個(或最后一個)索引,如果不存在,則返回-1。原數組不會被改變。
57、前端如何減少首屏加載時間?
1. 優化資源加載壓縮與合并:使用工具(如Webpack、Gulp)壓縮CSS、JS文件,減少文件大小。合并多個CSS/JS文件,減少HTTP請求數量。圖片優化:使用現代圖片格式(如WebP),相比JPEG/PNG體積更小。延遲加載非關鍵圖片(如懶加載),僅在用戶滾動到視口時加載。字體優化:使用font-display: swap避免字體加載阻塞渲染。僅加載必要的字符集(如Subset)。
2. 代碼拆分與按需加載Tree Shaking:移除未使用的代碼,減少打包體積。動態導入:使用import()按需加載模塊,避免一次性加載所有代碼。路由級代碼拆分:將不同頁面的代碼打包為獨立文件,按需加載。
3. 服務器端優化啟用Gzip/Brotli壓縮:減少傳輸數據量。CDN加速:將靜態資源托管到CDN,加速全球用戶訪問。緩存策略:設置HTTP緩存頭(如Cache-Control)。使用Service Worker緩存關鍵資源。
4. 預加載與預渲染預加載(Preload):通過<link rel="preload">提前加載關鍵資源。預渲染(Prerender):使用工具(如Prerender.io)生成靜態HTML,提升首屏速度。
5. 減少阻塞渲染CSS內聯關鍵路徑:將首屏所需的CSS直接嵌入HTML,避免阻塞渲染。異步加載JS:使用async或defer屬性加載非關鍵JS。
58、如何解決白屏問題?
1. 骨架屏(Skeleton Screen)在資源加載前顯示占位符(如灰色方塊),提升用戶體驗。實現方式:使用CSS繪制占位元素。結合React/Vue組件動態渲染骨架屏。
2. 漸進式加載優先渲染首屏內容:將首屏HTML直接嵌入,避免依賴JS渲染。分步加載:先顯示核心內容,再逐步加載其他部分。
3. 錯誤處理與降級加載失敗時顯示占位內容:使用try-catch捕獲JS錯誤。提供友好的錯誤提示頁面。服務端渲染(SSR):在服務器端生成HTML,減少客戶端渲染時間。
4. 性能監控使用工具(如Lighthouse、WebPageTest)分析白屏原因。監控關鍵指標(如FCP、LCP),定位性能瓶頸。
59、如何解決1px問題?
1. 背景圖方案
使用border-image或background-image繪制1px邊框。
示例:
css
.border {border-image: url('border.png') 2 stretch;
}
2. 偽元素方案使用:before或:after偽元素模擬邊框。
示例:
css
.border {position: relative;
}
.border::after {content: '';position: absolute;bottom: 0;left: 0;width: 100%;height: 1px;background-color: #000;transform: scaleY(0.5); /* 適配Retina屏 */transform-origin: bottom;
}
3. 媒體查詢適配針對高分辨率屏幕(如2x、3x)調整邊框寬度。
示例:
css
.border {border: 0.5px solid #000; /* 部分瀏覽器支持 */
}
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {.border {border-width: 0.25px; /* 進一步適配 */}
}
4. 使用viewport單位使用vw或vh單位動態計算邊框寬度。
示例:
css
.border {border: 0.05vw solid #000;
}
5. 第三方庫使用postcss-px-to-viewport插件,自動將px轉換為vw單位。
總結
減少首屏加載時間:通過資源優化、代碼拆分、服務器端加速等手段。
解決白屏問題:使用骨架屏、漸進式加載、SSR等技術。
解決1px問題:結合背景圖、偽元素、媒體查詢等方法適配高分辨率屏幕。
60、vue3項目可以用哪些懶加載技術提高性能?
在 Vue 3 項目中,可以通過多種懶加載技術來提高性能,減少初始加載時間和資源消耗.
(組件懶加載、路由懶加載、圖片懶加載、數據懶加載、第三方庫懶加載)
1. 組件懶加載
Vue 3 提供了動態組件加載的能力,可以通過 defineAsyncComponent 或 import() 語法實現組件的按需加載。
import { defineAsyncComponent } from 'vue';
// 使用 defineAsyncComponent
const AsyncComponent = defineAsyncComponent(() =>import('./components/MyComponent.vue')
);
// 或直接使用 import()
const AsyncComponent = () => import('./components/MyComponent.vue');2. 路由懶加載
結合 Vue Router,可以對路由組件進行懶加載,使得每個路由對應的組件只有在訪問時才被加載
import { createRouter, createWebHistory } from 'vue-router';
const routes = [{path: '/home',component: () => import('./views/HomeView.vue'), // 路由懶加載},{path: '/about',component: () => import('./views/AboutView.vue'),},
];
const router = createRouter({history: createWebHistory(),routes,
});
export default router;
3. 圖片懶加載
對于頁面中的圖片資源,可以使用懶加載技術,只有當圖片進入可視區域時才加載圖片,減少初始加載的資源消耗。
(1)使用 loading="lazy" 屬性(原生支持):<img src="example.jpg" loading="lazy" alt="Example">
(2)使用第三方庫(如 vue-lazyload)(在main.js配置)npm install vue-lazyloadimport { createApp } from 'vue';import App from './App.vue';import VueLazyload from 'vue-lazyload';const app = createApp(App);app.use(VueLazyload, {preLoad: 1.3,error: 'error.png',loading: 'loading.gif',attempt: 1,});app.mount('#app');<img v-lazy="imageUrl" alt="Lazy Loaded Image">4. 數據懶加載(分頁加載/無限滾動) 對于長列表或大量數據,可以通過懶加載技術分批加載數據,減少初始渲染的數據量。 分頁加載:在用戶切換分頁時,動態請求數據并渲染。無限滾動:結合 IntersectionObserver 或滾動事件,當用戶滾動到頁面底部時,動態加載更多數據。import { ref, onMounted, onBeforeUnmount } from 'vue';export default {setup() {const items = ref([]);let page = 1;let loading = false;const loadMore = () => {if (loading) return;loading = true;// 模擬異步請求setTimeout(() => {const newItems = Array.from({ length: 10 }, (_, i) => `Item ${(page - 1) * 10 + i + 1}`);items.value.push(...newItems);page++;loading = false;}, 1000);};const handleScroll = () => {const { scrollTop, clientHeight, scrollHeight } = document.documentElement;if (scrollTop + clientHeight >= scrollHeight - 10) {loadMore();}};onMounted(() => {loadMore(); // 初始加載window.addEventListener('scroll', handleScroll);});onBeforeUnmount(() => {window.removeEventListener('scroll', handleScroll);});return { items };},}; 5. 第三方庫的懶加載功能一些第三方庫(如 vue-virtual-scroller)提供了內置的懶加載功能,適用于大規模數據渲染npm install vue-virtual-scroller<template><virtual-scroller :items="items" :item-height="50"><template #default="{ item }"><div>{{ item }}</div></template></virtual-scroller></template><script>
import { ref } from 'vue';
import { VirtualScroller } from 'vue-virtual-scroller';
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';export default {components: { VirtualScroller },setup() {const items = ref(Array.from({ length: 10000 }, (_, i) => `Item ${i + 1}`));return { items };},
};
</script>//name="fade" 指定了過渡類名前綴。
//Vue 會自動添加以下類名:
//fade-enter-from:進入前的狀態。
//fade-enter-active:進入時的過渡效果。
//fade-leave-from:離開前的狀態。
//fade-leave-active:離開時的過渡效果。
61、組件緩存keep-alive相關,如何使用
keep-alive 是 Vue.js 提供的一個內置組件,用于緩存動態組件的狀態,避免組件在切換時被銷毀和重新創建。這對于需要保留組件狀態(如表單數據、滾動位置等)的場景非常有用。以下是關于 keep-alive 的詳細使用方法和示例:一:基本用法
keep-alive 通常包裹在動態組件或路由視圖(<router-view>)的外層,用于緩存這些組件。
<template><div><button @click="currentView = 'ViewA'">顯示 ViewA</button><button @click="currentView = 'ViewB'">顯示 ViewB</button><!-- 使用 keep-alive 緩存動態組件 --><keep-alive><component :is="currentView"></component></keep-alive></div>
</template><script>
import ViewA from './ViewA.vue';
import ViewB from './ViewB.vue';export default {data() {return {currentView: 'ViewA', // 默認顯示的組件};},components: {ViewA,ViewB,},
};
</script>二:與路由結合使用
在 Vue Router 中,keep-alive 通常與 <router-view> 結合使用,以緩存路由組件。
<template><div id="app"><router-link to="/home">首頁</router-link><router-link to="/about">關于</router-link><!-- 使用 keep-alive 緩存路由組件 --><keep-alive><router-view></router-view></keep-alive></div>
</template><script>
export default {// 通常不需要額外配置
};
</script>三:條件緩存
keep-alive 提供了 include 和 exclude 屬性,用于條件性地緩存組件。
include:字符串或正則表達式,只有名稱匹配的組件會被緩存。
exclude:字符串或正則表達式,名稱匹配的組件不會被緩存。
<template><div><keep-alive include="ViewA"><component :is="currentView"></component></keep-alive></div>
</template><script>
import ViewA from './ViewA.vue';
import ViewB from './ViewB.vue';export default {data() {return {currentView: 'ViewA', // 默認顯示的組件};},components: {ViewA,ViewB,},
};
</script>四:緩存生命周期鉤子
當組件被 keep-alive 緩存時,會觸發特定的生命周期鉤子:
activated:組件被激活時調用。
deactivated:組件被停用時調用。當 ViewA 被切換到前臺時,會觸發 activated 鉤子。
當 ViewA 被切換到后臺時,會觸發 deactivated 鉤子。
<template><div><p>這是 ViewA</p></div>
</template><script>
export default {activated() {console.log('ViewA 被激活');},deactivated() {console.log('ViewA 被停用');},
};
</script>五:動態組件緩存示例
結合 v-if 和 keep-alive,可以實現更復雜的緩存邏輯。
<template><div><button @click="showComponent = 'A'">顯示組件 A</button><button @click="showComponent = 'B'">顯示組件 B</button><keep-alive><component v-if="showComponent === 'A'" :is="componentA"></component><component v-if="showComponent === 'B'" :is="componentB"></component></keep-alive></div>
</template><script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';export default {data() {return {showComponent: 'A',componentA: 'ComponentA',componentB: 'ComponentB',};},components: {ComponentA,ComponentB,},
};
</script>六:注意事項性能問題:緩存的組件會占用內存,確保只緩存必要的組件。對于大組件或頻繁切換的組件,謹慎使用 keep-alive。組件銷毀:被 keep-alive 緩存的組件不會觸發 beforeDestroy 和 destroyed 鉤子。如果需要清理資源,可以在 deactivated 鉤子中處理。與 Vuex 結合:如果組件狀態依賴 Vuex,可以考慮將狀態存儲在 Vuex 中,而不是依賴 keep-alive。
62、vue2和vue3的生命周期都分別有啥?
一、Vue 2 生命周期鉤子
1. 創建階段beforeCreate:組件實例初始化之后調用,此時數據觀測(data)和事件機制尚未設置。created:組件實例創建完成后調用,此時數據觀測已完成,但 DOM 尚未掛載,$el 不可用。
2. 掛載階段beforeMount:在掛載開始之前調用,此時模板已編譯完成,但尚未渲染到 DOM。mounted:組件掛載到 DOM 后調用,此時可以通過 $el 訪問真實的 DOM 節點。
3. 更新階段beforeUpdate:數據更新時調用,發生在虛擬 DOM 重新渲染和打補丁之前。updated:數據更新導致虛擬 DOM 重新渲染和打補丁之后調用。
4. 銷毀階段beforeDestroy:實例銷毀之前調用,此時實例仍然完全可用。destroyed:實例銷毀后調用,此時所有事件監聽器和子實例均被銷毀。
二、Vue 3 生命周期鉤子1. 選項式 API(與 Vue 2 相同)Vue 3 的選項式 API 生命周期鉤子與 Vue 2 完全一致,包括:beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeDestroy(Vue 3 中已更名為 beforeUnmount)destroyed(Vue 3 中已更名為 unmounted)2. 組合式 API在組合式 API 中,生命周期鉤子以函數形式導入自 vue 包,例如:onBeforeMountonMountedonBeforeUpdateonUpdatedonBeforeUnmountonUnmounted生命周期階段 Vue 2 鉤子 Vue 3 選項式 API 鉤子 Vue 3 組合式 API 鉤子創建前 beforeCreate beforeCreate 不適用(setup 執行前)創建后 created created 不適用(setup 執行后)掛載前 beforeMount beforeMount onBeforeMount掛載后 mounted mounted onMounted更新前 beforeUpdate beforeUpdate onBeforeUpdate更新后 updated updated onUpdated銷毀前 beforeDestroy beforeUnmount onBeforeUnmount銷毀后 destroyed unmounted onUnmounted
四、注意事項beforeCreate 和 created 在組合式 API 中的處理:在組合式 API 中,setup 函數在 beforeCreate 和 created 之間執行,因此無需顯式使用這兩個鉤子。beforeDestroy 和 destroyed 的重命名:Vue 3 中,beforeDestroy 重命名為 beforeUnmount,destroyed 重命名為 unmounted,以更準確地描述生命周期階段。推薦使用組合式 API:組合式 API 提供了更靈活的邏輯組織方式,尤其適合復雜組件或需要共享邏輯的場景。
63、vue2定義數據為什么要在data中?
在 Vue 2 中,組件的數據需要定義在 data 選項中,這是因為 Vue 的響應式系統依賴于對 data 屬性的初始化來追蹤數據的變化。
1. Vue2響應式系統的原理
Vue2的響應式系統基于 Object.defineProperty來劫持對象的屬性訪問和修改。當組件實例化時,Vue2會遍歷data 對象中的所有屬性,并使用 Object.defineProperty 將它們轉換為響應式屬性。(1)為什么需要初始化?Vue 需要在組件實例化時就知道 data 中有哪些屬性,以便為這些屬性設置 getter 和 setter。如果某個屬性沒有在 data 中定義,Vue 無法追蹤它的變化,也就無法實現響應式更新。(2)動態添加屬性的問題如果在組件創建后動態地向 data 對象添加新的屬性,Vue 無法檢測到這些新屬性的變化,因為它們沒有在初始化時被劫持。為了解決這個問題,Vue 提供了 $set 方法(或 Vue.set 全局方法)來顯式地將新屬性添加為響應式屬性。
2. data 必須是一個函數(在組件中)在 Vue 組件中,data 必須是一個返回對象的函數,而不是一個直接的對象。這是因為組件可能會被復用多次,而每個組件實例都需要有自己的獨立數據。(1)為什么是函數?如果 data 是一個對象,那么所有組件實例將共享同一個數據對象,導致數據之間的沖突。通過將 data 定義為一個函數,并讓每個組件實例調用這個函數來返回一個新的對象,可以確保每個實例都有獨立的數據。export default {data() {return {message: 'Hello'};}};
3. 常見問題為什么不能直接在模板中使用未定義的屬性?如果在模板中引用了一個未在 data 中定義的屬性,Vue 會發出警告,因為該屬性不是響應式的。如何動態添加響應式屬性?使用 $set 方法:this.$set(this.someObject, 'newProperty', 'value');
4. Vue3 的改進在 Vue 3 中,響應式系統基于 Proxy 實現,不再依賴于 Object.defineProperty。因此,Vue 3 不再要求 data 必須是一個函數,也不再需要手動使用 $set 來添加響應式屬性。不過,在 Vue 3 的組合式 API 中,通常使用 reactive 或 ref 來定義響應式數據,而不是 data 選項。
64、 v-if和v-show的區別,以及都經歷了哪些生命周期在vue3中
在 Vue 3 中,v-if 和 v-show 都是用于控制元素顯示或隱藏的指令,但它們的工作原理和生命周期觸發機制有所不同。1. 工作原理的區別v-if:基于條件進行真實的 DOM 元素的創建或銷毀。當條件為 false 時,元素不會存在于 DOM 中。當條件為 true 時,元素會被創建并插入 DOM。v-show:基于條件通過設置 CSS 的 display 屬性來控制元素的顯示或隱藏。元素始終存在于 DOM 中,只是通過 display: none 隱藏。
2. 生命周期觸發的區別v-if:當條件從 false 變為 true 時:觸發子組件的 beforeCreate、created、beforeMount、mounted 生命周期鉤子。當條件從 true 變為 false 時:觸發子組件的 beforeUnmount、unmounted 生命周期鉤子。如果父組件的 v-if 條件發生變化,子組件的生命周期鉤子也會被觸發。v-show:元素始終存在于 DOM 中,因此不會觸發子組件的創建或銷毀相關的生命周期鉤子。當條件變化時,僅觸發元素的顯示或隱藏,不會影響生命周期。
3. 性能和適用場景v-if:切換開銷較高:因為涉及 DOM 的創建和銷毀。初始渲染開銷較低:因為只有在條件為 true 時才會渲染。適用場景:條件較少變化時使用,例如表單的某些部分僅在特定條件下顯示。v-show:切換開銷較低:因為僅涉及 CSS 的切換。初始渲染開銷較高:因為元素始終存在于 DOM 中。適用場景:需要頻繁切換顯示狀態時使用,例如模態框的顯示和隱藏。
65、vue2中響應式原理,對哪些是監聽不到的
在 Vue 2 中,響應式系統是基于 Object.defineProperty 實現的。Vue 2 的響應式系統可以監聽對象屬性的讀取和設置操作,
1. 新增屬性
Vue 2 無法檢測到對象屬性的動態添加。如果你在對象初始化后添加新的屬性,Vue 不會使其成為響應式的。
new Vue({el: '#app',data() {return {obj: {existingProperty: 'I exist'}};},mounted() {// 動態添加新屬性,Vue 無法檢測到this.obj.newProperty = 'I am new';console.log(this.obj.newProperty); // 輸出: I am new// 但這個新屬性不會觸發視圖更新}
});// HTML
// <div id="app">
// <p>{{ obj.existingProperty }}</p>
// <p>{{ obj.newProperty }}</p> <!-- 不會更新 -->
// </div>解決方案:使用 Vue.set 方法。
Vue.set(this.obj, 'newProperty', 'I am new');2. 刪除屬性
Vue 2 無法檢測到對象屬性的刪除。
new Vue({el: '#app',data() {return {obj: {property: 'I exist'}};},mounted() {// 刪除屬性,Vue無法檢測到delete this.obj.property;console.log(this.obj.property); // 輸出: undefined// 但這個變化不會觸發視圖更新}
});// HTML
// <div id="app">
// <p>{{ obj.property }}</p> <!-- 不會更新 -->
// </div>解決方案:使用 Vue.delete 方法(實際上是調用 delete 操作符,但確保在 Vue 的響應式上下文中使用)。
Vue.delete(this.obj, 'property');3. 數組索引或長度的修改
Vue 2 對數組的響應式處理有一些特殊情況。直接通過索引修改數組或修改數組的長度不會被檢測到。new Vue({el: '#app',data() {return {items: [1, 2, 3]};},methods: {modifyArray() {// 直接修改索引,Vue 無法檢測到this.items[1] = 42;// 修改數組長度,Vue 無法檢測到this.items.length = 2;}}
});// HTML
// <div id="app">
// <ul>
// <li v-for="(item, index) in items" :key="index">{{ item }}</li>
// </ul>
// <button @click="modifyArray">Modify Array</button>
// </div>解決方案:使用 Vue 提供的數組變異方法,如 splice。
this.items.splice(1, 1, 42); // 替換索引 1 的元素
this.items.splice(2); // 刪除索引 2 及之后的元素4. 通過 Map 或 Set 修改數據
Vue 2 的響應式系統不直接支持 Map 和 Set,因此對這些數據結構的修改不會被檢測到。
66、普通函數和箭頭函數的區別?
在 JavaScript 中,普通函數(通過 function 關鍵字定義的函數)和箭頭函數(通過 => 語法定義的函數)在語法、行為和使用場景上有一些關鍵區別。以下是它們的詳細對比:
1. 語法簡化語法:箭頭函數可以省略 function 關鍵字、大括號(如果只有一行代碼)和 return 關鍵字(隱式返回)。無函數體時:如果箭頭函數沒有參數,必須使用空括號 ()。
2. this 的綁定
普通函數普通函數的 this 指向調用該函數的對象(動態綁定)。如果函數作為方法調用,this 指向調用該方法的對象。如果函數獨立調用(如回調函數),this 通常是 undefined(嚴格模式)或全局對象(非嚴格模式)。function Person() {this.age = 0;setInterval(function() {this.age++; // 這里的 this 指向 setInterval 的上下文(通常是 window 或 undefined)console.log(this.age);}, 1000);}new Person(); // 輸出 NaN(因為 this.age 未定義)箭頭函數箭頭函數不會創建自己的 this,而是繼承其定義時所在上下文的 this(詞法綁定)。適合用作回調函數,避免 this 指向錯誤。function Person() {this.age = 0;setInterval(() => {this.age++; // 這里的 this 繼承自 Person 構造函數console.log(this.age);}, 1000);}new Person(); // 輸出 1, 2, 3...3. arguments 對象
普通函數普通函數有內置的arguments 對象,包含所有傳遞給函數的參數。function sum() {console.log(arguments); // 輸出類似 { '0': 1, '1': 2, '2': 3 }return Array.from(arguments).reduce((a, b) => a + b, 0);}sum(1, 2, 3); // 輸出 6箭頭函數箭頭函數沒有 arguments 對象。如果需要訪問參數,可以使用剩余參數(...args)。const sum = (...args) => args.reduce((a, b) => a + b, 0);sum(1, 2, 3); // 輸出 64. new 關鍵字
普通函數普通函數可以用作構造函數,通過 new 關鍵字創建對象實例。function Person(name) {this.name = name;}const person = new Person('Alice');console.log(person.name); // 輸出 'Alice'
箭頭函數箭頭函數不能用作構造函數,嘗試使用 new 會拋出錯誤。5. 原型方法普通函數有 prototype 屬性,可以為其添加方法。function Person() {}Person.prototype.sayHello = function() {console.log('Hello');};const person = new Person();person.sayHello(); // 輸出 'Hello'箭頭函數箭頭函數沒有 prototype 屬性,不能用于定義原型方法。const Person = () => {};// Person.prototype.sayHello = function() {}; // 無效,箭頭函數沒有 prototype
67、
68、
69、
70、
71、
72、
73、
74、
75、
76、
77、
78、
79、
80、
81、
82、
83、
84、
85、
86、
87、
88、
89、
90、
91、
92、
93、
94、
95、
96、
97、
98、
99、