Vue3.0 介紹

源碼組織方式
  • 提升代碼可維護(hù)性,源碼采用 TypeScript 重寫

  • 使用 Monorepo 管理項目結(jié)構(gòu),將獨立模塊提取到不同的包中,每個模塊劃分明確,模塊依賴關(guān)系也很明確,并且每個功能模塊都可以單獨測試、發(fā)布并使用

image-20210412192848527.png

compiler開頭的包都是和編譯相關(guān)的代碼,compiler-core是和平臺無關(guān)的編譯器,compiler-dom是瀏覽器平臺下的編譯器,依賴compiler-corecompiler-sfc(single file component)單文件組件,用于編譯單文件組件,依賴compiler-corecompiler-dom;compiler-ssr是和服務(wù)端渲染相關(guān)的編譯器,依賴compiler-dom

reactivity是數(shù)據(jù)響應(yīng)式系統(tǒng),可單獨使用

runtime開發(fā)的包都是運行時代碼,runtime-core是和平臺無關(guān)的運行時,runtime-dom是針對瀏覽器的運行時,處理原生 DOM API、事件等;runtime-test是為測試而編寫的輕量的運行時,渲染出的 DOM 樹是一個 js 對象,所以這個運行時可以運行在所有的 js 環(huán)境里,用它來測試渲染是否正確,還可以用于序列化 DOM、觸發(fā) DOM 事件以及記錄某次更新中的 DOM 操作

server-renderer是服務(wù)端渲染

shared是 vue 內(nèi)部使用的一些公共 API

size-check是私有包,不會發(fā)布到 npm,用于在 tree-shaking 后檢查包的大小

template-explorer是在瀏覽器里運行的實時編譯組件,會輸出 render 函數(shù)

vue構(gòu)建完整版 vue,依賴于compilerruntime

Vue.js3.0 不同構(gòu)建版本

構(gòu)建不同版本,用于不同的場合,和 vue2.x 不同的是,不再構(gòu)建 umd 模塊方式,umd 模塊方式會讓代碼更加冗余

  • packages/vue 存在所有構(gòu)建版本

    image-20210412193022815.png
  • 說明

    官網(wǎng)不同構(gòu)建版本的解釋

    版本 名稱 說明
    cjs(commonJS 模塊化方式, vue.cjs.js 開發(fā)版,代碼未被壓縮
    完整版 vue,包含運行時和編譯器) vue.cjs.prod.js 生產(chǎn)版本,代碼被壓縮
    global(全局,這 4 個文件都可以在瀏覽器中 vue.global.js 完整版,開發(fā)版
    通過 script 的方式引入,增加全局 vue 對象) vue.global.prod.js 完整版,生產(chǎn)版
    vue.runtime.global.js 運行時版本,開發(fā)版
    vue.runtime.global.props.js 運行時版本,生產(chǎn)版
    browser(esModule 模塊化方式,在瀏覽器中 vue.esm-browser.js 完整版,開發(fā)版
    通過 type="module"的方式來導(dǎo)入) esm-browser.prod.js 完整版,生產(chǎn)版
    vue.runtime.esm-browser.js 運行時版本,開發(fā)版
    vue.runtime.esm-browser.prod.js 運行時版本,生產(chǎn)版
    bundler(需要配合打包工具使用,使用 es Module vue.esm-bundler.js 完整版,還導(dǎo)入 runtime-compiler
    方式,內(nèi)部通過 import 導(dǎo)入 runtime-core) vue.runtime.esm-bundler.js 運行時,通過腳手架創(chuàng)建項目默認(rèn)使用此版本
Composition API

設(shè)計動機(jī)

  • Options API

    包含一個描述對象組件選項(data、methods、props 等)的對象

    Options API 開發(fā)負(fù)責(zé)組件,同一個功能邏輯的代碼被拆分到不同選項中

    export default {
      data() {
        return {
          position: {
            x: 0,
            y: 0,
          },
        }
      },
      created() {
        window.addEventListener(' mousemove ', this.handle)
      },
      destroyed() {
        window.removeEventListener('mousemove ', this.handle)
      },
      methods: {
        handle(e) {
          this.position.x = e.pagexthis.position.y = e.pageY
        },
      },
    }
    
  • Composition API

    Vue.js3.0 中新增的一組 API

    一組基于函數(shù)的 API

    可以更靈活的組織組件的邏輯

    解決超大組件時,使用 Options API 不好拆分和重用問題

    // CompositionAPI
    import { reactive, onMounted, onUnmounted } from 'vue'
    function userMousePosition() {
      const position = reactive({
        x: 0,
        y: 0,
      })
      const update = (e) => {
        position.x = e.pageX
        position.y = e.pageY
      }
      onMounted(() => {
        window.addEventListener('mousemove', update)
      })
      onUnmounted(() => {
        window.removeEventListener('mousemove', update)
      })
      return position
    }
    
    export default {
      setup() {
        const position = useMousePosition()
        return {
          position,
        }
      },
    }
    
    image-20210412200549917.png

同一色塊代表同一功能,Options API 中相同的代碼被拆分在不同位置,不方便提取重用代碼

Composition API 同一功能代碼不需要拆分,有利于代碼重用和維護(hù)

  • Composition Api vs Options Api

    • 在邏輯組織和邏輯復(fù)用方面,Composition API是優(yōu)于Options API
    • 因為Composition API幾乎是函數(shù),會有更好的類型推斷。
    • Composition APItree-shaking 友好,代碼也更容易壓縮
    • Composition API中見不到this的使用,減少了this指向不明的情況
    • 如果是小型組件,可以繼續(xù)使用Options API,也是十分友好的
性能提升
  • 響應(yīng)式系統(tǒng)升級

    Vue.js2.x 中響應(yīng)式系統(tǒng)核心是 defineProperty,初始化時遍歷所有 data 中的成員,通過 defineProperty 將對象屬性轉(zhuǎn)換為 getter 和 setter,如何 data 中的對象又是對象的話,需要遞歸處理每一個子對象屬性

    Vue.js3.0 中使用 Proxy 對象重寫響應(yīng)式系統(tǒng),可以攔截屬性的訪問、賦值、刪除等操作

    Proxy 好處:

    1. 可以監(jiān)聽動態(tài)新增屬性,vue2.x 需要使用$set
    2. 可以監(jiān)聽刪除的屬性,vue2.x 監(jiān)聽不到
    3. 可以監(jiān)聽數(shù)組的索引和 length 屬性,vue2.x 監(jiān)聽不到
  • 編譯優(yōu)化

    對編譯器進(jìn)行優(yōu)化,重寫虛擬 DOM,首次渲染和 update 性能有了大幅度提升,這也是Vue3性能能夠得到提升的重要原因之一

    <template>
      <div id="app">
        <div>
          static root
          <div>static node</div>
        </div>
        <div>static node</div>
        <div>static node</div>
        <div>{{ count }}</div>
        <button @click="handler">button</button>
      </div>
    </template>
    

    Vue.js2.x 在構(gòu)建過程中需要先編譯為 render 函數(shù),在編譯時通過標(biāo)記靜態(tài)根節(jié)點,優(yōu)化 diff 過程(但是依然需要執(zhí)行 diff 操作),當(dāng)組件發(fā)生變化時,會通知 watcher,觸發(fā) watcher 的 update 方法,最終執(zhí)行虛擬 DOM 的 patch 操作,遍歷所有虛擬節(jié)點找到差異,然后更新到真實 DOM 上;diff 過程中會比較整個虛擬 DOM,先對比新舊的 div,以及它的屬性,再去對比內(nèi)部子節(jié)點;

    Vue.js2.x 中渲染的最小單位是組件

    Vue.js3.0 中標(biāo)記和提升所有靜態(tài)根節(jié)點,diff 時只需要對比動態(tài)節(jié)點內(nèi)容

    • Fragments 片段,模板中不需要在創(chuàng)建唯一的根節(jié)點,需要升級 vetur 插件,查看Vue 3 Template Explorer

      image-20210413082819879.png

首先使用_createBlock給根 div 創(chuàng)建 block,是樹結(jié)構(gòu),然后通過_createVNode創(chuàng)建子節(jié)點,相當(dāng)于h函數(shù),當(dāng)刪除根節(jié)點時,會創(chuàng)建_Fragment片段

image-20210413083059655.png
  • 靜態(tài)提升

    打開hoistStatic靜態(tài)提升選項,可以看到_createBlock中的靜態(tài)節(jié)點都被提升到 render 函數(shù)外邊,這些節(jié)點只有初始化時被創(chuàng)建一次,再次調(diào)用 render 時不會在被創(chuàng)建

    image-20210413083144478.png
  • Patch flag
image-20210413083558220.png

可以看到在動態(tài)節(jié)點<div>{{ count }}</div>通過_createVNode渲染后,最終會有數(shù)字1,這就是 Patch flag。作為一個標(biāo)記,將來在執(zhí)行 diff 時會檢查整個block中帶 Patch flag 標(biāo)記的節(jié)點,如果 Patch flag 值為1,代表文本內(nèi)容時動態(tài)綁定,所以只會比較文本內(nèi)容是否發(fā)生變化

image-20210413084010181.png

此時在給當(dāng)前 div 綁定一個 id 屬性,可以看到 Patch flag 變?yōu)?code>9,代表當(dāng)前節(jié)點的文本和 PROPS 是動態(tài)內(nèi)容,并且記錄動態(tài)綁定的 PROPS 是 id,將來 diff 時只會檢查此節(jié)點的文本和 id 屬性是否發(fā)生變化,從而提升 diff 性能

  • 緩存事件處理函數(shù)
image-20210413084318107.png

開啟緩存后,首次渲染時會生成新的函數(shù),并將函數(shù)緩存到_cache對象中,將來再次調(diào)用 render 時,會從緩存中獲取

  • 源碼體積優(yōu)化

    Vue.js3.0 移除一些不常用 API,如:inline-template、filter 等

    Tree-shaking 支持更好,因為 Tree-shaking 依賴 ES Module,也就是 ES6 的模塊化語法結(jié)構(gòu)importexport,通過編譯階段的靜態(tài)分析,找到?jīng)]有引入的模塊,在打包的時候直接過濾掉,從而減少打包體積。Vue.js3.x 的內(nèi)置組件 keepAlive、Trasition 和一些內(nèi)置指令都是按需引入,并且 Vue.js3.x 中的很多 API 都是支持 Tree-shaking,沒有使用是不會進(jìn)行打包的

Vite

學(xué)習(xí) Vite 前,先需要了解 ES Module

  • 除 IE 外,現(xiàn)代瀏覽器都支持 ES Moduie

  • 加載模塊通過在 script 標(biāo)簽中 type="module"即可

    <script type="module" src="..."></script>
    
  • 支持模塊的 script 默認(rèn)延遲加載

    類似于 script 標(biāo)簽設(shè)置 defer

    在文檔解析完成后,觸發(fā)DOMContentLoaded事件前執(zhí)行

案例項目地址

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">Hello world!</div>
    <script>
      window.addEventListener('DOMContentLoaded', () => {
        console.log('DOMContentLoaded')
      })
    </script>
    <script type="module" src="./modules/index.js"></script>
  </body>
</html>
// modules/index.js
import { forEach } from './utils.js'

const app = document.querySelector('#app')
console.log(app.innerHTML)

const arr = [1, 2, 3]
forEach(arr, (item) => {
  console.log(item)
})

type="module"方式引入時需要在服務(wù)器中運行項目,在 vsCode 中安裝插件live-server,右鍵啟動項目

image-20210413103509503.png

打開瀏覽器控制臺,可以看到輸出結(jié)果如下所示,可以看到index.js模塊在文檔解析完成后,觸發(fā) DOMContentLoaded 事件前執(zhí)行

image-20210413085620461.png

Vite vs Vue-Cli

  • Vite 在開發(fā)模式下不需要打包可以直接運行,因為 vite 在開發(fā)模式下使用瀏覽器支持的 es Module 加載模塊,通過<script type="module"></script>的方式加載代碼,提升開發(fā)效率;vite 會開啟測試服務(wù)器,攔截瀏覽器發(fā)送請求,對瀏覽器不識別的模塊進(jìn)行處理,比如當(dāng) import 單文件組件時,會先進(jìn)行編譯,把編譯的結(jié)果發(fā)送給瀏覽器
  • Vue-Cli 開發(fā)模式下必須對項目打包才可以運行
  • Vite 在生成環(huán)境下使用 Rollup 打包,基于 ES Module 的方式打包,不再需要使用 babel 把 import 轉(zhuǎn)換為 require,因此打包體積會小于 webpack 體積
  • Vue-Cli 使用 Webpack 打包

Vite 特點

  • 快速冷啟動(不需要打包)
  • 按需編譯(代碼加載時才會進(jìn)行編譯)
  • 模塊熱更新

使用 Vite 創(chuàng)建基于 vue3 項目

npm install create-vite-app -g
npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev

基于模板創(chuàng)建項目

npm init vite-app --template react
npm init vite-app --template preact

案例項目地址

通過 create-vite-app 創(chuàng)建完項目之后,App.vue 會有 eslint 語法錯誤,原因是 Vetur 插件還沒有更新

image-20210413085824316.png

解決:文件 --> 首選項 --> 設(shè)置 --> 搜索 eslint-plugin-vue --> Vetur ? Validation: Template 取消勾選

image-20210413095847513.png

通過 npm run dev 啟動項目

開發(fā)環(huán)境下,vite 開啟 web 服務(wù)器后,會劫持.vue 結(jié)尾的文件,將.vue 文件轉(zhuǎn)換為 js 文件,并將響應(yīng)中的 content-type 設(shè)置為 application/javascript,告訴瀏覽器是 js 腳本

image-20210413113538441.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會,身份的轉(zhuǎn)變要...
    余生動聽閱讀 10,798評論 0 11
  • 彩排完,天已黑
    劉凱書法閱讀 4,452評論 1 3
  • 表情是什么,我認(rèn)為表情就是表現(xiàn)出來的情緒。表情可以傳達(dá)很多信息。高興了當(dāng)然就笑了,難過就哭了。兩者是相互影響密不可...
    Persistenc_6aea閱讀 129,442評論 2 7

友情鏈接更多精彩內(nèi)容