天天看點

Vue.js 架構源碼與進階 - Vue.js 3.0 介紹

15.1 源碼組織方式

  • 采用 TypeScript 的方式重寫
    • 為了提升代碼的可維護性,Vue 3.x 的源碼全部采用 TypeScript 編寫
    • 大型項目的開發都推薦使用類型化的語言,在編碼的過程當中幫我們檢查類型的問題
  • 使用 Monorepo 管理項目結構
    • 把獨立的功能子產品都提取到不同的包中,每個功能子產品之間的劃分明确,子產品之間的依賴關系也明确
    • 每個功能子產品都可以單獨測試、單獨釋出以及單獨使用

packages 目錄結構

Vue.js 架構源碼與進階 - Vue.js 3.0 介紹
  • packages 目錄下都是獨立發行的包,可以獨立使用
    • compiler - xxx 跟編譯相關的代碼
      • compiler-core 與平台無關的編譯器
      • compiler-dom 浏覽器平台下的編譯器,依賴于 compiler-core
      • compiler-sfc 用來編譯單檔案元件,依賴于 compiler-core 與 compiler-dom
      • compiler-ssr 服務端渲染的編譯器,依賴于 compiler-dom
    • reactivity 資料響應式系統,可以獨立使用
    • runtime - xxx 跟運作時相關的代碼
      • runtime-core 與平台無關的運作時
      • runtime-dom 針對浏覽器的運作時,處理 原生DOM 的 API、事件等
      • runtime-test 專門為測試編寫的輕量級的運作時,這個運作時渲染出來的DOM樹其實是一個JS對象,可以運作在所有的運作環境
    • server-renderer 用于服務端渲染
    • shared Vue内部使用的一些公共 API
    • size-check 私有的包,不會發不到 Npm,作用是在 Tree-shaking 之後檢查包的大小
    • template-explorer 在浏覽器運作的實時編譯元件,會輸出render函數,README.md 提供線上通路位址
    • vue 用來建構完整版的 Vue,依賴于 compiler 和 runtime

15.2 不同的建構版本

Vue 3.x 在建構時與 Vue 2.x 類似都建構了不同的版本

和 Vue 2.x 不同的是,Vue 3.x 中不再建構 UMD 子產品化的方式,因為其會讓代碼有更多的備援,它要支援多種子產品化的方式

Vue 3.x 的建構版本中把 cjs、ESModule 和 自執行函數 的方式分别打包到了不同的檔案中

  • packages/vue 存放了 Vue 3.x 中的所有建構版本
Vue.js 架構源碼與進階 - Vue.js 3.0 介紹
  • cjs 也就是 Common JS 的子產品化方式,此處兩個檔案都是完整版的 Vue 包含運作時和編譯器
    • vue.cjs.js 開發版本,代碼未被壓縮
    • vue.cjs.prod.js 生産版本,代碼被壓縮過
  • global 全局的意思,這四個檔案都可以在浏覽器直接通過 script 标簽導入,會增加一個全局的 Vue 對象
    • vue.global.js 完整版的 Vue,開發版本
    • vue.global.prod.js 完整版的 Vue,生産版本
    • vue.runtime.global.js 隻包含運作時,開發版本
    • Vue.runtime.global.prod.js 隻包含運作時,生産版本
  • browser 都包含 ES Module,浏覽器的原生子產品化的方式,在浏覽器中可以直接通過

    <script type="module" src=""></script>

    導入
    • vue.esm-browser.js 完整版的 ESM,開發版本
    • vue.esm-browser.prod.js 完整版的 ESM,開發版本
    • vue.runtime.esm-browser.js 隻包含運作時,開發版本
    • vue.runtime.esm-browser.prod.js 隻包含運作時,開發版本
  • bundler 這兩個檔案沒有打包所有的代碼,它們需要配合打包工具使用,使用 ES Module 的子產品化方式,内部通過 import 導入 runtime-core
    • Vue.esm-bundler.js 完整版,内部導入 runtime、compiler,也就是編譯器
    • vue.runtime.esm-bundler.js 使用腳手架建立的項目中的預設導入,這個檔案隻導入了運作時,它是 Vue 的最小版本,在項目開發完畢後重新打包時打包我們使用到的代碼

15.3 Composition API

Vue 3.x 雖然代碼全部重寫,但是 90% 以上的 API 依然相容 2.x,并且根據社群的回報增加了 Composition API 即 組合API

  • RFC(Request For Comments)
    • https://github.com/vuejs/rfcs
  • Composition API RFC
    • https://composition-api.vuejs.org

設計動機

它是用來解決 2.x 在開發大型項目時遇到超大元件使用 Options API 不好拆分重用的問題

  • Options API
    • 包含一個描述元件選項(data、methods、props等)的對象
    • Options API 開發複雜元件,同一個功能邏輯的代碼被拆分到不同選項
  • Options API Demo
Vue.js 架構源碼與進階 - Vue.js 3.0 介紹
  • Composition API
    • Vue.js 3.x 新增的一組 API
    • 一組基于函數的 API
    • 可以更靈活的組織元件的邏輯
  • Composition API Demo
Vue.js 架構源碼與進階 - Vue.js 3.0 介紹

相對于 Options API 這樣做的好處:檢視某個邏輯時隻需關注具體的函數即可,目前的邏輯代碼都封裝在函數内部,不像 Options API 時擷取滑鼠位置的邏輯代碼分散在不同的位置,檢視這部分代碼還需要上下拖動滾動條

Vue.js 架構源碼與進階 - Vue.js 3.0 介紹

再來看一下官方提供的這張圖來感受 Options API 和 Composition API 的差別:

Options API 中同一色塊代表同一功能,我們可以看到相同功能的代碼被拆分在不同位置,當元件的功能比較複雜,統一邏輯的代碼被拆分在不同位置,我們就需要不停拖動滾動條來找到我們需要的代碼,且不友善提取重用代碼

Composition API 也是使用相同色塊代表同一功能,我們可以看到相同功能的代碼不需要拆分,有利于對代碼的提取和重用

在 Vue.js 3.x 中你即可以使用 Options API,也可以使用 Composition API

15.4 性能提升

  • 響應式系統更新
  • 編譯更新
  • 源碼體積的優化

在性能方面 Vue.js 3.x 又大幅度提升,使用代理對象 Proxy 重寫了響應式的代碼并且對編譯器做了優化,重寫了虛拟DOM,進而讓渲染和Update的性能都有了大幅度的提升

另外,官方介紹服務端渲染的性能也提升兩到三倍

響應式系統的更新

  • Vue.js 2.x 中響應式系統的核心 defineProperty
    • 初始化時周遊 data 中的所有成員,通過 defineProperty 把對象的屬性轉換成 getter 和 setter,如果 data 中的屬性又是對象的話,需要遞歸處理每一個子對象的屬性。這些都是初始化時進行的,如果你未使用這些屬性也會進行響應式的處理
  • Vue.js 3.x 中使用 Proxy 對象重寫響應式系統
    • Proxy 的性能本身就比 defineProperty 好,且代理對象可以攔截屬性的通路、指派、删除等操作,不需要初始化時周遊所有的屬性,如果有多層屬性嵌套隻有通路某個屬性時才會遞歸處理下一級屬性
    • 使用 Proxy 對象預設可以監聽動态新增的屬性,而 Vue.js 2.x 想要動态添加響應式屬性需要調用 Vue.set 方法來處理
    • Vue.js 2.x 監聽不到屬性的删除
    • Vue.js 2.x 對數組的索引和 length 屬性也監聽不到

除了響應式系統的更新,Vue.js 3.x 通過優化編譯的過程和重寫虛拟 DOM 讓首次渲染和更新的性能有了大幅度提升

編譯優化

Vue.js 架構源碼與進階 - Vue.js 3.0 介紹

Vue.js 2.x 模闆首先需要編譯成 render 函數,這個過程一般在建構時完成的,在編譯時會編譯靜态根節點和靜态節點,靜态根節點要求節點中必須有一個靜态子節點

當元件的狀态發生變化後會通知 watcher 觸發 update 去執行 虛拟DOM 的 patch 操作,周遊所有的虛拟節點找到差異更新到 真實DOM 上,diff 的過程中會去比較整個 虛拟DOM,先對比新舊 div 以及它的屬性再對比内部子節點

Vue.js 2.x 中渲染最小的機關是元件,diff 的過程會跳過靜态根節點,因為靜态根節點的内容不會發生變化,即

  • Vue.js 2.x 中通過标記靜态根節點,優化 diff 的過程,但是靜态節點還需要進行 diff,沒有被優化
  • Vue.js 3.x 中标記和提升所有靜态根節點,diff 的時候隻需要對比動态節點内容
    • Fragments(VS Code需要更新 vetur 插件):模闆中不需要再建立唯一的根節點,可以直接放文本内容或者多個同級标簽
    • 靜态提升
    • Patch flag
    • 緩存事件處理函數

優化打包體積

  • Vue.js 3.x 中移除了一些不常用的 API
    • 例如:inline-template、filter 等
  • Tree-shaking

15.5 Vite

随着 Vue 3.x 的釋出,官方還提供了一個開發工具 Vite,使用 Vite 在開發階段測試項目的時候不需要打包直接運作項目,提升了開發的效率

ES Module

  • 現代浏覽器都支援 ES Module(IE 不支援)
  • 通過下面的方式加載子產品
    • <script type="module" src=""></script>

  • 支援子產品的 script 預設延遲加載
    • 類似于 script 标簽設定 defer
    • 在文檔解析完成後,觸發 DOMContentLoaded 事件前執行

Vite as Vue-CLI 開發環境

  • Vite 在開發模式下不需要打包可以直接運作

在開發模式下,Vite 使用浏覽器原生支援的 ES Module 加載子產品,也就是通過 import 導入子產品,支援 ES Module 的現代浏覽器通過

<script type="module" src=""></script>

加載子產品代碼

因為 Vite 不需要打包項目,是以其在開發模式下打開頁面是秒開的

  • Vue-CLI 在開發模式下必須對項目打包才可以運作

Vue-CLI 在開發環境下會打包整個項目,如果項目比較大速度會特别慢

Vue 會開啟一個測試的伺服器,它會攔截浏覽器發送的請求,浏覽器會向伺服器發送請求擷取相應的子產品,Vite 會對浏覽器未識别的子產品進行處理,使用這種方式讓 Vite 有以下特點:

Vite 特點

  • 快速冷啟動
  • 按需編譯
  • 子產品熱更新

Vite as Vue-CLI 生産環境

  • Vite 在生産環境下使用 Rollup 打包
    • 基于 ES Module 的方式打包
  • Vue-CLI 使用 Webpack 打包

Vite 建立項目

  • Vite 建立項目
npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev
           
  • 基于模闆建立項目
npm init vite-app --template react
npm init vite-app --template preact
           

繼續閱讀