一、基礎概念與核心特性
1. Vue3 相比 Vue2 的改進(通俗版)
問題:Vue3 比 Vue2 好在哪?
答案:
- 更快:
- Proxy 代理:Vue2 的響應式像“逐個監聽保險箱”(每個屬性單獨監聽),Vue3 的 Proxy 像“直接監控整個房間”(監聽整個對象變化)。
- 編譯優化:Vue3 在編譯階段標記哪些是動態內容(如
{{ count }}
),更新時跳過靜態內容(如純文字)。
- 更小:通過 Tree-shaking(搖樹優化),只打包你用到的功能,減少代碼體積。
- 更好用:
- Composition API:像搭積木一樣組合邏輯(比如把“計數器邏輯”抽成函數,多個組件復用)。
- 新組件:
<Teleport>
:把組件渲染到任意位置(比如彈窗放到 body 下,避免被父組件樣式影響)。<Suspense>
:優雅處理異步加載(比如數據加載時顯示 Loading 動畫)。
2. Composition API vs Options API(場景對比)
問題:為什么要用 Composition API?
答案:
-
Options API(Vue2 風格):
- 把代碼按類型分塊(data、methods、生命周期),適合簡單組件。
- 缺點:邏輯分散,比如一個“搜索功能”的 data、methods 可能分布在多處。
// Options API 示例 export default { data() { return { keyword: '' } }, methods: { search() { ... } }, mounted() { this.search() } }
-
Composition API(Vue3 風格):
- 在
setup()
中,按功能組織代碼(比如把搜索相關的數據、方法寫在一起)。 - 優點:邏輯復用更方便(類似 React Hooks)。
// Composition API 示例 export default { setup() { const keyword = ref(''); const search = () => { ... }; onMounted(search); return { keyword, search }; } }
- 在
二、響應式原理(手繪理解)
3. Vue3 的響應式原理
問題:Vue3 如何實現數據變化自動更新視圖?
答案:
-
Proxy 代理對象:
- 當你修改數據時,Proxy 會“攔截”操作(比如
obj.a = 1
),通知視圖更新。 - 對比 Vue2:Vue2 使用
Object.defineProperty
,無法監聽新增屬性和數組下標變化(必須用this.$set
)。
- 當你修改數據時,Proxy 會“攔截”操作(比如
-
代碼模擬(簡化版):
function reactive(obj) { return new Proxy(obj, { get(target, key) { console.log('讀取了', key); return Reflect.get(target, key); }, set(target, key, value) { console.log('更新了', key); return Reflect.set(target, key, value); } }); } const obj = reactive({ a: 1 }); obj.a = 2; // 觸發 set 攔截,更新視圖
4. ref
和 reactive
的區別(買菜比喻)
問題:什么時候用 ref
?什么時候用 reactive
?
答案:
-
ref
:- 用于包裝 基本類型(數字、字符串等),因為 Proxy 無法直接監聽基本類型。
- 使用方式:必須通過
.value
訪問(就像買菜用袋子裝,取菜要打開袋子)。
const count = ref(0); console.log(count.value); // 0 count.value++;
-
reactive
:- 用于包裝 對象/數組,可以直接訪問屬性(就像直接拿菜籃子,不用拆包裝)。
const user = reactive({ name: '張三' }); console.log(user.name); // 張三 user.name = '李四';
-
總結:
- 簡單值用
ref
,復雜對象用reactive
。 - 如果不想寫
.value
,可以用toRefs
解構對象(見下文)。
- 簡單值用
toRefs
是 Vue 3 中用于處理響應式對象的重要工具函數,主要用于將 reactive
對象轉換為普通對象,同時確保每個屬性都保持響應性。這在解構響應式對象或將其屬性傳遞給子組件時非常有用。
使用場景
- 解構響應式對象:直接解構
reactive
對象會失去響應性,而使用toRefs
可以避免這一問題。 - 組件間通信:通過
toRefs
將響應式數據傳遞給子組件,確保數據在傳遞過程中仍能保持響應性。
基本用法
import { reactive, toRefs } from 'vue';const state = reactive({foo: 1,bar: 2,
});const stateRefs = toRefs(state);
// stateRefs 的每個屬性都是 ref 對象,修改它們的值會觸發視圖更新stateRefs.foo.value++; // 視圖會自動更新
示例代碼
解構并保持響應性
<template><div><p>Foo: {{ foo }}</p><p>Bar: {{ bar }}</p><button @click="incrementFoo">Increment Foo</button></div>
</template><script>
import { reactive, toRefs } from 'vue';export default {setup() {const state = reactive({foo: 1,bar: 2,});const { foo, bar } = toRefs(state);function incrementFoo() {foo.value++;}return {foo,bar,incrementFoo,};},
};
</script>
在組合式 API 中使用
import { reactive, toRefs } from 'vue';function useCounter() {const state = reactive({count: 0,});function increment() {state.count++;}return {...toRefs(state),increment,};
}
注意事項
- 訪問方式:返回的對象屬性是
ref
對象,在 JavaScript 中需通過.value
訪問;模板中則無需.value
。 - 適用范圍:僅適用于
reactive
對象,不支持普通對象或ref
對象。 - 性能影響:大量屬性可能帶來一定性能開銷。
總結而言,toRefs
提供了一種便捷的方式來處理響應式對象,尤其在需要解構或傳遞響應式數據時,能夠有效簡化邏輯并保持數據的響應性。
三、進階 API 與實戰技巧
5. watch
和 watchEffect
(場景區分)
問題:監聽數據變化用哪個?
答案:
-
watch
:- 明確監聽某個數據,適合精確控制(比如監聽搜索關鍵詞變化,觸發請求)。
watch( keyword, (newVal) => { fetchData(newVal) }, { immediate: true } // 立即執行一次 );
-
watchEffect
:- 自動追蹤依賴,適合副作用操作(比如根據多個數據變化更新 DOM)。
watchEffect(() => { console.log('關鍵詞和頁碼變化了:', keyword.value, page.value); fetchData(); });
6. 組件通信:Provide/Inject(跨層級傳參)
問題:爺爺組件如何直接傳數據給孫子組件?
答案:
- 步驟:
- 爺爺組件用
provide
提供數據。 - 孫子組件用
inject
獲取數據。
- 爺爺組件用
- 代碼示例:
// 爺爺組件 import { provide } from 'vue'; setup() { provide('theme', 'dark'); // 提供數據 } // 孫子組件 import { inject } from 'vue'; setup() { const theme = inject('theme', 'light'); // 第二個參數是默認值 return { theme }; }
四、性能優化(通俗策略)
7. 如何讓 Vue3 應用更快?
答案:
-
代碼層面:
- 使用
v-once
標記靜態內容(只渲染一次)。 - 用
v-memo
緩存動態組件(比如表格行,只有 ID 變化時才重新渲染)。
<div v-for="item in list" :key="item.id" v-memo="[item.id]"> {{ item.name }} </div>
- 使用
-
打包優化:
- 按需引入組件庫(比如 Element Plus 只導入用到的 Button、Input)。
- 使用異步組件(懶加載),減少首屏代碼體積。
// 異步加載組件 const AsyncComponent = defineAsyncComponent(() => import('./MyComponent.vue'));
五、高頻面試代碼片段
8. 自定義指令:點擊外部關閉彈窗
場景:點擊彈窗外部區域關閉彈窗。
代碼:
// 全局指令 v-click-outside
app.directive('click-outside', { mounted(el, { value: callback }) { el.handler = (e) => { if (!el.contains(e.target)) callback(); }; document.addEventListener('click', el.handler); }, unmounted(el) { document.removeEventListener('click', el.handler); }
}); // 使用
<template> <div v-click-outside="closeModal">彈窗內容</div>
</template>
六、項目經驗(回答技巧)
9. 如何回答“封裝通用組件”?
示例:
- 場景:封裝一個表單組件,支持校驗和提交。
- 步驟:
- 通過
props
接收表單配置(如字段規則)。 - 使用
v-model
綁定每個輸入項的值。 - 暴露
validate()
方法供父組件調用。 - 使用插槽(slot)允許自定義布局。
<template> <form @submit.prevent="submit"> <slot></slot> <button type="submit">提交</button> </form> </template> <script> export default { methods: { validate() { /* 校驗邏輯 */ }, submit() { this.$emit('submit'); } } } </script>
- 通過
總結
以上內容通過通俗比喻、實際場景和代碼示例,拆解了 Vue3 的核心知識點。建議邊學邊寫代碼實踐,結合 Vue3 官方文檔 查漏補缺!