Vue3.0 從整體來說,既修改了 Vue2.x 時期遺留的一些缺陷,又引入了 組合式 API 、自定義渲染 API 等新特性。這些新特性的引入,不僅為 Vue 的發展注入了更多的活力,同時也塑造了 Vue 使用場景方面更多的可能性。
Vue3.0 的新特性
- Proxy 響應式綁定
- Tree-Shaking Support
- 組合式 API
- Fragment 片段、Teleport、Suspense
- 自定義渲染 API
- 源碼優化
一、Proxy 響應式綁定
Vue2.x 內部是通過 Object.defineProperty
這個 API 去劫持數據的 getter 和 setter 來實現響應式的。因為這個 API 它必須預先知道要攔截的 key 是什么,所以它並不能檢測對象屬性的添加和刪除。直接造成了數組元素的直接修改不會觸發響應式機制,這個很多初學者所謂的 bug。
例如,對象obj的text屬性進行劫持:
const obj = {};
Object.defineProperty(obj, 'text', {
get: function() {
console.log('get val'); 
},
set: function(newVal) {
console.log('set val:' + newVal);
document.getElementById('input').value = newVal;
document.getElementById('span').innerHTML = newVal;
}
});
const input = document.getElementById('input');
input.addEventListener('keyup', function(e){
obj.text = e.target.value;
})
Vue3.0 使用了 Proxy API 做數據劫持,它劫持的是整個對象,自然對於對象的屬性的增加和刪除都能檢測到,自然也不會在存在上述的問題。
將上面例子進行改寫:
const input = document.getElementById('input');
const p = document.getElementById('p');
const obj = {};
const newObj = new Proxy(obj, {
get: function(target, key, receiver) {
console.log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function(target, key, value, receiver) {
console.log(target, key, value, receiver);
if (key === 'text') {
input.value = value;
p.innerHTML = value;
}
return Reflect.set(target, key, value, receiver);
},
});
input.addEventListener('keyup', function(e) {
newObj.text = e.target.value;
});
通過 Proxy 實現雙向響應式綁定,相比 defineProperty 的遍歷屬性的方式效率更高,性能更好,另外 Virtual DOM 更新只 diff 動態部分、事件緩存等,也帶來了性能上的提升。
二、Tree-Shaking Support(搖樹優化)
tree-sharking 即在構建工具構建后消除程序中無用的代碼,來減少包的體積。
相比 Vue2.x 導入整個 Vue 對象,Vue3.0 支持按需導入,只打包需要的代碼。Tree-Shaking 依賴 ES2015 模塊語法的靜態結構(即 import 和 export),通過編譯階段的靜態分析,找到沒有引入的模塊並打上標記。像我們在項目中如果沒有引入 Transition、KeepAlive 等不常用的組件,那么它們對應的代碼就不會打包進去。
三、組合式 API
首先需要明確的是,Vue2.x 中組件傳統的 data,computed,watch,methods 寫法,我們稱之為選項式 API(Options API )。
(1)選項式 API 存在的缺陷
隨着業務復雜度越來越高,代碼量會不斷的加大;由於代碼需要遵循 option 的配置寫到特定的區域,導致后續維護非常的復雜,代碼可復用性也不高。比如,很長的 methods 區域代碼、data變量聲明與方法區域未在一起。
(2)與 mixins 的比較
對於上述提到的問題,我們可能會想到會 mixins 來解決,但是當抽離並引用了大量的 mixins,你就會發現兩個不可避免的問題:命名沖突和數據來源不清晰。
組合式 API 和 mixins 二者的差別:
- 層級不同:組合式 API 與組件是嵌套關系,而mixin與組件是同 層級關系
- 影響面不同:組合式 API 作為組件的被調用方,並且變量邏輯是組件控制,耦合性很低。而 mixins 是耦合在代碼邏輯里,並且存在變量的互相引用,為將來的升級和維護埋下隱患。
(3)與 React Hook 的比較
不可置否,組合式 API 的誕生確實受到了 React Hooks 的啟發。對於使用者而言,使用一些概念時大同小異,這也不用就此貼上抄襲的標簽,因為很多語言上都有相似的概念或者語法,比如 TS 可以規范變量的聲明,但是 Java 中為了方便也出現了 var。事實上,組合式 API 的實現與 React Hook 是截然不同的。
組合式 API 使用上 比 React Hooks簡便了不少:
- 同樣的邏輯組合、組件復用能力
- 只調用一次 setup 方法
更加符合 JS 直覺
沒有閉包變量問題
沒有內存/GC壓力
不存在內聯回調導致子組件永遠更新的問題
(4)組合式 API 的使用
下面簡單概述了組合式 API 的使用,主要是一些 方法增加 和 概念遷移:
-
setup方法:
vue3.0 中,所有的代碼邏輯將在setup方法中實現,包括 data、watchcomputed、methods 等,並且不再有this。
vue3.0 會先執行setup方法,再執行兼容2.x的其他方法。
vue3.0 中setup方法在組件生命周期內只執行一次,不會重復執行。 -
生命周期鈎子:
2.x 中生命周期鈎子放在跟 methods 同級屬性下。
3.x 中需要先導入鈎子,然后在 setup 方法中注冊鈎子回調,並且鈎子命名也跟 React 保持一樣。
3.x 移除了 2.x 中的 beforeCreate 和 created 鈎子,通過 setup 方法代替。
// vue2.x
export default {
data () {
return {}
},
methods: {
...
},
beforeCreate() {},
created() {},
beforeMount() {},
mounted() {},
beforeUpdate() {},
updated() {},
beforeDestroy() {},
destroyed() {}
}
// vue3.x
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted
} from 'vue'
export default {
setup() {
onBeforeMount(() => {
console.log('component is onBeforeMount')
})
onMounted(() => {
console.log('component is onMounted')
})
onBeforeUpdate(() => {
console.log('component is onBeforeUpdate')
})
onUpdated(() => {
console.log('component is onUpdated')
})
onBeforeUnmount(() => {
console.log('component is onBeforeUnmount')
})
onUnmounted(() => {
console.log('component is onUnmounted')
})
}
}
- 響應式數據對象相關方法:reactive 方法、ref 方法、toRef 等方法,數據的只讀屬性,計算屬性,方法的拆解,watchEffect,useStore 等相關方法不在此一一詳解。
四、Fragment 片段、Teleport、Suspense
(1)Fragment 片段
- Vue2.x中,vue template只允許有一個根節點。
- Vue3.0中,vue template支持多個根節點。
// vue2.x
<template>
<div>
<Headers></Headers>
<Main></Main>
<Footer></Footer>
</div>
</template>
// Vue3.0
<template>
<Headers></Headers>
<Main></Main>
<Footer></Footer>
</template>
(2)Teleport
Teleport 是一種能夠將我們的模板渲染至指定 DOM 節點,不受父級 style 、v-show 等屬性影響,但 data、prop 數據依舊能夠共用的技術;類似於 React 的 Portal。
(3)Suspense
<Suspense>
是一個特殊的組件,它將呈現回退內容,而不是對於的組件,直到滿足條件為止,這種情況通常是組件 setup 功能中發生的異步操作或者是異步組件中使用。
使用場景:父組件展示的內容包含異步的子組件,異步的子組件需要一定的時間才可以加載並展示,這時就需要一個組件處理一些占位邏輯或者加載異常邏輯。
// vue2.x
<template>
<div>
<div v-if="!loading">
...
</div>
<div v-if="loading">Loading...</div>
</div>
</template>
// vue3.x
<Suspense>
<template >
<Suspended-component />
</template>
<template #fallback>
Loading...
</template>
</Suspense>
五、自定義渲染 API
自定義渲染 API 將 Virtual DOM(虛擬DOM)和平台相關的渲染分離。
通過 createRendererAPI 我們可以自定義 Virtual DOM 渲染到某一平台中時的所有操作,比如新增、修改、刪除一個“元素”,我們可以這些方法中替換或修改為我們自定義的邏輯,從而打造一個我們自定義的渲染器。
利用這個 API,在 Vue3.0 中我們可以自由方便地去構建 Web(瀏覽器)平台或非 Web 平台的自定義渲染器。
在 Vue 中是利用 runtime-dom 方法提供的一個上層的抽象層,它幫我們完成了 Virtual DOM 渲染到 Web DOM 中的復雜瀏覽器接口編程操作。
通過編寫自定義渲染器,極大豐富了 Vue 的使用場景。
六、源碼優化
(1)使用 monorepo 來管理源碼
- Vue.js 2.x 的源碼托管在 src 目錄,然后依據功能拆分 出了 compiler(模板編譯的相關代碼)、core(與平台無關的通用運行時代碼)、platforms(平台專有代碼)、server(服務端渲染的相關代碼)、sfc(.vue 單文件解析相關代碼)、shared(共享工具代碼)等目錄。
- Vue.js 3.0,整個源碼是通過 monorepo 的方式維護的,根據功能將不同的模塊拆分到 packages 目錄下面不同的子目錄中,每個 package 有各自的 API、類型定義和測試。
(2)使用 Typescript 來開發源碼
- Vue.js 2.x 選用 Flow 做類型檢查,來避免一些因類型問題導致的錯誤,但是 Flow 對於一些復雜場景類型的檢查,支持得並不好。
- Vue.js 3.0 拋棄了 Flow ,使用 TypeScript 重構了整個項目。TypeScript 提供了更好的類型檢查,能支持復雜的類型推導;由於源碼就使用 TypeScript 編寫,也省去了單獨維護 d.ts 文件的麻煩。
Vue3 面試題總結
Vue3.0 源碼分析
- Vue.js 3.0 響應式系統的實現原理?
- 響應式是惰性的
- vue3 新增 Composition API
- Vue 3.0 所采用的 Composition Api 與 Vue 2.x使用的 Options Api 有什么區別?
- vue3 新增內置組件 Teleport
- Props 初始化和更新流程改進
- Vue3 Slot內容分發
- Vue 3.0 在編譯方面有哪些優化?
- Vue3 依賴注入子孫組件如何共享數據
- Vue3 偵聽器實現原理與使用場景
- Vue3 組件實現原理核心源碼解讀
- Vuex 數據流管理方案
- 原生服務端渲染(SSR)的實現、同構開發
- Nuxt.js 集成式 SSR 框架
vue3.0實戰應用
- 說說 Vue2.0 和 Vue3.0 有什么區別
- Vue3 的新特性
- 基於 vite/webpack
- 實現 Vue3 工程化部署
- 掌握 setup 和 10 種響應式系統 API
- 掌握新生命周期函數
- 模板 refs 的使用
- Vue3 中的響應式系統和 dom-diff
需要前端學習筆記、大廠面試題、 Vue3.0 面試題 PDF文檔(含答案解析),加入👉🏻這邊的Q裙:【624369675】免費領取!