Vue 3.0 性能提升主要是通過哪幾個方面體現的?

一. 響應式系統升級

1. Vue.js 2.x 中響應式系統的核心是 Object.definePropertry

2. Vue.js 3.x 中使用 Proxy 對象重寫響應式系統

  • 可以一次性監聽所有屬性
  • 可以監聽動態新增的屬性
  • 可以監聽刪除的屬性
  • 可以監聽數組的索引和length屬性 

 

詳細解釋:

Object.defineProperty Vs Proxy

1. Object.defineProperty 無法一次性監聽所有屬性, Proxy 可以

const personInfo = {
  name: 'zhangsan',
  age: 18,
  sex: '男'
}
const proxy = new Proxy(personInfo, {
  get(target, key) { },
  set(target, key, newValue) { }
})

Object.keys(personInfo).forEach(key => {
  Object.defineProperty(personInfo, key, {
    set() { },
    get() { }
  })
})

  

2. Object.defineProperty 無法監聽動態新增的屬性, Proxy 可以

const personInfo = {
  name: 'zhangsan',
  age: 18,
  sex: '男'
}

const proxy = new Proxy(personInfo, {
  get(target, key) {
    console.log('get', key)
  },
  set(target, key, newValue) {
    target[key] = newValue
    return true
  }
})
Object.keys(personInfo).forEach(key => {
  Object.defineProperty(personInfo, key, {
    set() { },
    get() { }
  })
})

personInfo.from = '上海'

console.log(proxy) // Proxy 生效 Object.defineProperty 不生效

  

3. 可以監聽刪除的屬性?

const personInfo = {
  name: 'zhangsan',
  age: 18,
  sex: '男'
}

const proxy = new Proxy(personInfo, {
  get(target, key) {
    console.log('get', key)
  },
  set(target, key, newValue) {
    target[key] = newValue
    return true
  }
})

Object.keys(personInfo).forEach(key => {
  Object.defineProperty(personInfo, key, {
    set() { },
    get() { }
  })
})

delete personInfo.age
console.log(personInfo) // proxy 生效 Object.defineProperty不生效

 

4. 可以監聽數組的索引和length屬性 

const personInfo = [1, 2, 3, 4]

const proxy = new Proxy(personInfo, {
  get(target, key) {
    console.log('get', key)
    return key in target ? target[key] : undefined
  },
  set(target, key, newValue) {
    target[key] = newValue
    return true
  }
})

personInfo.forEach((item, index) => {
  Object.defineProperty(personInfo, index, {
    set() { },
    get() { }
  })
})


personInfo[0] = 8 // 都生效
personInfo[5] = 6 // proxy 生效
personInfo.push(99) // proxy 生效

  

二. 編譯優化

1. Vue.js 2.x 通過標記靜態節點,優化 diff 的過程

2. Vue.js 3.x 通過標記和提升所有的靜態根節點,diff 的時候只需要對比動態節點內容

  • Fragments ( 升級 Vetur 插件 )
  • 靜態提升
  • Patch flag
  • 緩衝事件處理函數

 

詳細解釋:

此處我們用到線上編譯器來查看 vue 2.xvue3.x 的編譯區別~

 

1. 首先看一下,當文件內部,不包含任何內容時,Vue2.x 編譯是空的,Vue 3.x 編譯內部包含 render 函數,返回爲null

 

 

 

2. 我們先放入一個 Dom, 可以看到 vue2.x 和 vue 3.x 編譯的部分完完全全重構了,之前 Vue2.x 採用,_c 的模式創建標籤,_v 爲 Vnode 節點, 而當前的 Vue 3.x 通過 _createBlock 生成 block tree 

- Vue 2.x 數據更新並觸發重新渲染的粒度是組件級的,單個組件內部需要遍歷該組件的整個 vnode 樹

- Vue.js 3.0 做到了通過編譯階段對靜態模板的分析,編譯生成了 Block tree。Block tree 是一個將模版基於動態節點指令切割的嵌套區塊,每個區塊內部的節點結構是固定的。每個區塊只需要追蹤自身包含的動態節點。

 

 

3. 新引入Fragments(片段)特性:Vue 3.x 模板中不需要再創建一個唯一的根節點,模板裏可以直接放文本內容或者很多同級的標籤, Vue2.x 需要唯一的節點

 

 

4.  靜態提升:靜態節點都會被提升到render 的外部,只有初始化時會被創建,再次調用render時不會再次創建,可以直接重用這些靜態節點對應的vnode

 

5. Patch flag

 

6. 緩存事件處理函數減少了不必要的更新操作

三. 源碼體積的優化

1. Vue.js 3.x 移除了一些不常用的API

  • 例如:inline-template, filter 等

2. Tree-shaking

詳細解釋:移除一些不常用的API這裏我們很好解釋,在這裏我們就重點講一下 Tree-shaking, Tree-shaking就是把無用的模塊進行“剪枝”,很多沒有用到的API就不會打包到最後的包裏。

tree-shaking的原理是: 依賴 ES2015 模塊語法的靜態結構(即 import 和 export),通過編譯階段的靜態分析,找到沒有引入的模塊並打上標記。

 

舉例說明一下:

1. import 的引用

// src/assets/index.js

export function testA() {
  console.log('A')
}

export function testB() {
  console.log('B')
}

// src/assets/data.json

{
  "name": "colin",
  "gender": "male"
}

// src/main.js

import { createApp } from 'vue'
import App from './App.vue'

// 測試代碼開始
import { testA } from './assets/js/index'
import { name } from './assets/data.json'

console.log(name)

// 測試代碼結束

createApp(App).mount('#app')

 

以上代碼,我們將 testA 方法引用,但未使用,data.json 引用,同時使用後,npm run build 打包,我們會發現,data.json 文件

內容已被打印,但testA 在打包後的文件內,沒有發現被引用的情況

 

 

修改 main.js

import { createApp } from 'vue'
import App from './App.vue'

// 測試代碼開始

import { testA } from './assets/js/index'
import { name } from './assets/data.json'

console.log(testA())
console.log(name)

// 測試代碼結束

createApp(App).mount('#app')

 

進行再次打包,我們會看到 testA 已經被成功的打印,但在打包的js中,我們並沒有找到 testB 方法,並且也沒有找到  assets/data.json 下的  gender 字段,所以此處我們的按需加載已經完成了,但這只是Tree-sharking的一部分

 

 

2. require 引用

修改 main.js 文件

import { createApp } from 'vue'
import App from './App.vue'

// 測試代碼開始

const test = require('./assets/js/index')

// 測試代碼結束

createApp(App).mount('#app')

  

打包後,我們會發現 我們通過 require 引用的文件,即使未使用,但還是打包在壓縮包內

 

 

從這上面一點,我們發現,Tree-sharking 只支持 import 或者 export 的形式,也就是 依賴ES6 內部的靜態分析

 

所謂靜態分析就是不執行代碼,從字面量上對代碼進行分析,ES6之前的模塊化,比如我們可以動態require一個模塊,只有執行後才知道引用的什麼模塊,這個就不能通過靜態分析去做優化。

 

這是 ES6 modules 在設計時的一個重要考量,也是爲什麼沒有直接採用 CommonJS,正是基於這個基礎上,才使得 tree-shaking 成爲可能,這也是爲什麼 rollup 和 webpack 2 都要用 ES6 module syntax 才能 tree-shaking。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章