一文带你从Vue2.x大迈步走进Vue.js 3.0新时代
目錄
- 前言
- 源碼組織方式
- 源碼組織方式的變化
- packages目錄下都是獨立發行的包,可以獨立使用。
- 不同的構建版本
- Composition API的設計動機
- Composition API 最佳學習參考:
- 設計動機
- 總結:
- 性能提升
- Vue 3.0的性能提升主要有以下幾方面:
- 總結
前言
Vue2.x 與及Vue 3.x都已經出來很久了,種種原因,它們的區別一直沒搞太明白,今天就通過這篇文章給大家梳理一下Vue3.0升級過后帶來了哪些新特性以及這些性變化帶來的各方面性能或者代碼質量的提升。
這里占用大家一杯奶茶的時間,反正不花錢,看了沒收獲的評論區懟我便是😁。
源碼組織方式
源碼組織方式的變化
- 源碼采用TypeScript重寫
大型項目的開發都推薦使用類型化的語言 - 使用Monorepo管理項目結構
- 使用一個項目來管理多個包
- 把不同的功能代碼放到不同的packages中管理,功能劃分模塊更明確,模塊間的依賴關系也很明確
- 每個功能模塊都可以單獨進行開發、單獨測試和單獨使用
- packages目錄結構如下:
packages目錄下都是獨立發行的包,可以獨立使用。
-
compiler-* 編譯相關的代碼
- compiler-core: 平臺無關的編譯器
- compiler-dom: 瀏覽器平臺下的編譯器,依賴于compiler-core
- compiler-sfc: 單文件組件的編譯器,依賴于compiler-core和compiler-dom
- compiler-ssr:服務端渲染的編譯器,它依賴與compiler-core和compiler-dom
-
reactivity:數據響應式系統,可以獨立使用。
-
runtime-* 運行時相關的代碼
- runtime-core:與平臺無關的運行時
- runtime-dom:針對瀏覽器的運行時,它處理原生DOM的API、屬性、事件等。
- runtime-test:專門為測試而編寫去的輕量級運行時,這個運行時渲染出來的DOM樹是一個js對象,所以這個運行時可以運行在所有的js環境里。可以使用它來測試渲染是否正確。它還可以用于序列化DOM,觸發DOM事件,以及記錄某次更新中的DOM操作。
-
server-render:用于服務端渲染;
-
shared:vue內部使用的一些公共的API
-
size-check:一個私有的包,不會發布到npm。它的左右時在構建時tree-shaking之后檢查包的大小。
-
template-explorer:在瀏覽器中運行的實時編譯組件,它會輸出render函數,該包里面提供了在線訪問的地址,我們后面會用到的。
-
vue:它是用來構建完整版的vue,依賴與compiler和runtime。
不同的構建版本
與Vue2.x類似,Vue3.0在構建大的時候都構建了不同的版本,可以在不同的場合下使用。
與Vue2.x不同的是,Vue3.0中不再構建umd模塊化的方式。因為umd模塊化的方式會讓代碼有更多的冗余,它要支持多種模塊化的方式。
Vue3.0的構建版本中,把 cjs、ES Module 和 自執行函數(IIFE)的方式分別打包到了不同的文件中。在packages/vue目錄的dist中存放了vue 3的所有構建版本。
可分為四大類:
- cjs: 也就是commonjs 的模塊化方式,都是完整版的,包含運行時和編譯器。vue.cjs.js和生產版本的vue.cjs.prod.js
- global:也就是全局的意思,它們都可以在瀏覽器中直接通過script標簽來導入,導入js之后會增加一個全局的Vue對象。
- vue.global.js 和 vue.global.prod.js 都是完整版的vue,包含運行時和編譯器。
- vue.runtime.global.js和vue.runtime.global.prod.js 它兩是只包含運行時的構建版本。
- browser:文件名都包含esm,也就是 ES Module,瀏覽器原生模塊化的方式。在瀏覽器中可以直接通過 <script type="module" src="xxx.esm-xxx.js">的方式來導入這些模塊。
- vue.esm-browser.js 和 vue.esm-browser.prod.js 都是ESModule模塊化方式的完整版的vue,包含運行時和編譯器。
- vue.runtime.esm-browser.js和vue.runtime.esm-browser.prod.js 它兩是ESModule模塊化方式的只包含運行時的構建版本。
- bundler:文件沒有打包所有的代碼,它們需要配合打包工具來使用,都是使用ESModule模塊化的方式,內部通過import導入runtime-core。
- vue.esm-bundler.js :是完整版的,包含運行時和編譯器的ESModule版本。所有它內部還導入了runtime-compiler。
- vue.runtime.esm-bundler.js:是只包含運行時的構建版本,我們使用vue cli腳手架工具創建的項目中,默認導入使用的就是vue.runtime.esm-bundler.js,也就是它是vue的一個最小版本。在項目開發完畢后,重新打包的時候,只會打包我們使用到的代碼。它可以讓vue的體積更小。
Composition API的設計動機
Composition API 最佳學習參考:
- RFC(Request For Comments)vue2.x 升級到vue3的大的變動,都是通過rfc機制的機制進行確認。也就是官方會給出一些提案,結合社區收集到的反饋并討論,最終確認。 具體參考vuejs的RFC倉庫
- Composition API RFC 該文檔中介紹了 Composition API的使用。
設計動機
vue 2.x在開發中小型項目的時候已經很好用,但是使用vue2.x在開發需要長期迭代和維護的大型項目的時候,也會有一些限制。在大型項目中, 可能會有一些功能比較復雜的組件,我們在看他人開發的組件的時候,可能會很難看懂,原因是vue2.x版本的組件開發采用的是Options API。
-
Options API
- 用包含一個描述組件選項(data、props、methods、provide、injects、mixins、watched、computed、lifehooksxxx等)的對象來創建組件的方式;
- Options API 開發復雜組件,同一個功能邏輯的代碼被拆分到不同選項,我們在看他人寫的代碼的時候為了查看某個功能,可能會需要不停的上下拖動滾動條來找到同一功能對應的代碼。
- 案例:
基本每個選項都相互有所依賴,我們需要上下來回翻看以更好地理解代碼。加入我們想要增加搜索功能,那么我們也要像這樣在data中添加相應的數據,在鉤子函數和方法methods中添加處理邏輯的方法。
另外,使用Options API 還難以提取組件中可重用的邏輯。雖然Vue2.x中有mixins混入的機制,可以把組件當中重復的代碼提取并重用。但是mixins使用的過程也有問題,比如命名沖突或數據來源不清晰。
-
Composition API:
- 它是Vue3.0 中新增的一組API
- 一組基于函數的API
- 可以更靈活的組織組件的邏輯
案例演示:
案例中還是實現上面vue2 options api案例的功能,在Vue3.0中這里將它們數據和處理邏輯封裝到了一個函數中,useMousePosition(){...}其他組件也可以直接使用這個函數所封裝的功能,只需要把該方法useMousePosition(){...}提取到一個模塊中,然后再在其他組件中導入即可。
當我們需要新增功能的時候,例如要添加搜索功能的時候,我們只需要添加一個useSearch(){...}的函數,將來哪個組件需要,只需要在哪個需要的組件的setup函數中來調用這些use*函數。
這樣做的好處是: - 查看某個邏輯的時候,只需要關注具體的函數即可;當前的邏輯代碼都封裝在函數內部。
- 官方給出的效果比對如圖所示:
從圖中我們可以看出,相同顏色塊標示的代碼屬于同一功能的相關代碼,使用Composition API的相同顏色的代碼相對更加集中。更有利于對代碼的提取和重用。
值得注意的是,Vue 3.0中既可以使用Options API 也可以使用 Composition API。
Composition API 對于Vue 3.0來說只是一種新增的API,并沒有把Vue2.x的 Options API 的代碼編寫方式拋棄。 你可以根據自己的喜好以及團隊的約定來選擇使用哪種方式。
如果你開發的組件中有需要提取可復用的邏輯,這個時候可以考慮選用Composition API,會更方便一些。
總結:
- Composition API是vue 3.0新增的,
- 它提供了一種基于函數的API,
- 讓我們可以更靈活地組織組件的邏輯,使用Composition API可以更合理組織組件內部的代碼結構,還可以把一些邏輯功能從組件中提取出來,方便其他組件重用。
其實,這里可以思考個問題,options API是不是本來也可以這樣拆分,再在整體export default 的時候進行object merge?
那么可否認為這個Composition API就是在做的這件事,把代碼按邏輯劃分函數化輸出結果,最后在setup的時候進行合并。
性能提升
Vue 3.0的性能提升主要有以下幾方面:
-
響應式系統升級
- vue 2.x中響應式系統的核心是defineProperty(),初始化的時候給會遍歷data選項中的所有成員,給每個成員屬性及迭代其子對象的每個屬性成員設置getter/setter來監聽數據的變化,通過觀察者以及依賴更新的模式使得數據響應式成為可能,使之成員響應式對象。這里面的data成員即使你沒有使用,也會在初始化的時候進行響應式處理。
- vue3.0使用Proxy對象重寫了響應式系統。
- Proxy對象的性能本身就比defineProperty要好;
- Proxy可以監聽動態新增的屬性,而Vue2.x中需要調用Vue.set()來處理。
- Proxy可以監聽刪除的屬性,而Vue2.x中監聽不到屬性的刪除。
- Proxy可以監聽數組的索引和length屬性,而Vue2.x中監聽不到數組的索引和length屬性的變更。
- Proxy可以監聽數據的一切變更,不需要初始化的時候遍歷所有的屬性;
- 此外,如果有多層屬性嵌套的話,只有訪問某個屬性的時候才會遞歸處理下一級的屬性。
-
編譯優化
通過優化編譯的過程,和重寫虛擬DOM提升了渲染的性能。讓首次渲染和組件更新的的渲染有了大幅度的提升。-
vue2.x中通過標記靜態根節點,優化了diff的過程。而其靜態節點還需要進行diff,這個過程沒有被優化。
-
Vue3.0中標記和提升所有的靜態根節點,diff的時候只需要對比動態節點內容
- 新引入 Fragments(片段特性) (在VSCODE中需要升級vetur插件以解決沒有唯一根節點的錯誤提示),模版中不需要再創建唯一的根節點,模版中可以直接放文本內容,或者多個同級的標簽,沒有唯一根節點時會幫忙自動創建fragment作為根節點,保持組件內部的樹形結構,可以找到唯一的根。
- 靜態提升:編譯時如果開啟了靜態提升,會把靜態根節點(標簽內的內容是純文本的內容)提升到生成的render函數的外部作為const常量定義,這些被提升到的靜態節點只有在初始化的時候被創建一次。當我們再次調用render函數的時候不需要再創建這些靜態節點。因為它們已經被創建過,可以直接重用初始化時創建的這些靜態節點對應的Vnode。
-
patch flag: 將來在diff的時候會跳過靜態根節點,只需要去更新patch flag所指示的動態節點中的內容。這大大提升了diff的性能。
- 緩存事件處理函數:減少了不必要的更新操作。
- 緩存事件處理函數:減少了不必要的更新操作。
總結
-
-
源碼體積優化
通過優化源碼的體積,和更好的tree-shaking的支持,減少了打包的體積。- Vue.js 3.0中移除了一些不常用到的API,例如 inline-template、filter等。可以讓最終代碼的體積變小。移除的filter可以通過methods或者computed來實現。
- 對tree-shaking的支持更好,Tree-shaking依賴ES Module的模塊化語法的靜態結構(export / import),通過編譯階段的靜態分析,找到沒有引入的模塊,在打包到的時候直接過濾掉,讓打包后的代碼體積更小。Vue 3在設計之初就考慮到了Tree-shaking的需要,其內置的組件,例如:transaction、keep-alive以及一些內置的指令,如:v-model等都是按需引入的。除此以外,Vue 3 中的很多API 都是支持Tree-shaking的。所以Vue 3中一些新增的API 如果你沒有使用的話,這部分的代碼是不會打包的,只會打包我們所使用的API。
- 但是默認vue的核心模塊都會被打包。
總結
以上是生活随笔為你收集整理的一文带你从Vue2.x大迈步走进Vue.js 3.0新时代的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 人体十大最佳黄金时间
- 下一篇: 大数据安全: Hadoop安全模型的演进