書名: Vue.js 3應用開發(fā)與核心源碼解析作者名: 呂鳴本章字數(shù): 2183字更新時間: 2023-08-31 19:30:30
1.4.1 更快、更小、更易于維護
1.更快
更快主要體現(xiàn)在Vue 3在性能方面的提升,以及在源碼層面的改動,主要包括以下方面:
· 重構虛擬DOM。
· 事件緩存。
· 基于Proxy的響應式對象。
(1)重構虛擬DOM
Vue 3重寫了虛擬DOM的實現(xiàn)方法,使得初始渲染/更新可以提速達100%,對于Vue 2.x版本的虛擬DOM來說,Vue會遍歷<template>模板中的所有內容,并根據(jù)這些標簽生成對應的虛擬DOM(虛擬DOM一般指采用key/value對象來保存標簽元素的屬性和內容),當有內容改變時,遍歷虛擬DOM來找到變化前后不同的內容,我們稱這個過程叫作diff(different),并找到針對這些變化的內容所對應的DOM節(jié)點,并改變其內部屬性。例如下面這段代碼:
<template> <div class="content"> <p>number1</p> <p>number2</p> <p>number3</p> <p>{{count}}</p> </div> </template>
當觸發(fā)響應式時,遍歷所有的<div>標簽和<p>標簽,找到{{count}}變量對應的<p>標簽的DOM節(jié)點,并改變其內容。對于那些純靜態(tài)<p>標簽的節(jié)點進行diff其實是比較浪費資源的,當節(jié)點的數(shù)量很少時,表現(xiàn)并不明顯,但是一旦節(jié)點的數(shù)量過大,在性能上就會慢很多。對此,Vue 3在此基礎上進行了優(yōu)化,主要有:
· 標記靜態(tài)內容,并區(qū)分動態(tài)內容(靜態(tài)提升)。
· 更新時只diff動態(tài)的部分。
針對上面的代碼,Vue 3中首先會區(qū)分出{{count}}這部分動態(tài)的節(jié)點,在進行diff時,只針對這些節(jié)點進行,從而減少資源浪費,提升性能。
(2)事件緩存
我們知道在Vue 2.x中,在綁定DOM事件時,例如@click,這些事件被認為是動態(tài)變量,所以每次更新視圖的時候都會追蹤它的變化,然后每次觸發(fā)都要重新生成全新的函數(shù)。在Vue 3中,提供了事件緩存對象cacheHandlers,當cacheHandlers開啟的時候,@click綁定的事件會被標記成靜態(tài)節(jié)點,被放入cacheHandlers中,這樣在視圖更新時也不會追蹤,當事件再次觸發(fā)時,就無須重新生成函數(shù),直接調用緩存的事件回調方法即可,在事件處理方面提升了Vue的性能。
未開啟cacheHandlers編譯后的代碼如下:
<div @click="hi">Hello World</div> // 編譯后 export function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createElementBlock("div", { onClick: _ctx.hi }, "Hello World1", 8 /* PROPS */, ["onClick"])) }
開啟cacheHandlers編譯后的代碼如下:
<div @click="hi">Hello World</div> // 編譯后 export function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createElementBlock("div", { onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.hi && _ctx.hi(...args))) }, "Hello World1")) }
可以看到主要區(qū)別在于onClick那一行,直接從緩存中讀取了回調函數(shù)。
(3)基于Proxy的響應式對象
在Vue 2.x中,使用Object.defineProperty()來實現(xiàn)響應式對象,對于一些復雜的對象,需要循環(huán)遞歸地給每個屬性增加getter/setter監(jiān)聽器,這使得組件的初始化非常耗時,而Vue 3中,引入了一種新的創(chuàng)建響應式對象的方法reactive,其內部就是利用ES 6的Proxy API來實現(xiàn)的,這樣就可以不用針對每個屬性來一一進行添加,以減少開銷,提升性能。我們會在后續(xù)章節(jié)具體講解Vue 3的響應式和Proxy API。
2.更小
更小主要體現(xiàn)在包所占容量的大小,我們知道,前端資源一般都屬于靜態(tài)資源,例如JavaSript文件、HTML文件等,這些資源都托管在服務器上,用戶在使用瀏覽器訪問時,會將這些資源下載下來,所以精簡文件包大小是提升頁面性能的重要因素。Vue 3在這方面可以讓開發(fā)者打包構建出來的資源更小,從而提升性能。
Tree Shaking是一個術語,通常用于描述移除JavaScript上下文中的未引用代碼(dead-code),就像一棵大樹,將那些無用的葉子都剪掉。它依賴于ES 6模塊語法的靜態(tài)結構特性,例如import和export,這個術語和概念在打包工具Rollup和Webpack中普及開來。例如下面這段ES 6代碼:
import {get} from './api.js' let doSome = ()=>{ get() } doSome() // api.js let post = ()=>{ console.log('post') } export post let get = ()=>{ console.log('get') } export get
上面的代碼中,api.js代碼中的post方法相關內容是沒有被引入和使用的,有了Tree Shaking之后,這部分內容是不會被打包的,這就在一定程度上減少了資源的大小。使用Tree Shaking的原理是引入了ES 6的模塊靜態(tài)分析,這就可以在編譯時正確判斷到底加載了什么代碼,但是要注意import和export是ES 6原生的,而不是通過Babel或者Webpack轉化的。
在Vue 3中,對代碼結構進行了優(yōu)化,讓其更加符合Tree Shaking的結構,這樣使用相關的API時,就不會把所有的都打包進來,只會打包用戶用到的API,例如:
<!-- vue 2.x --> import Vue from 'vue' new Vue() Vue.nextTick(() => {}) const obj = Vue.observable({}) <!-- vue 3.x --> import { nextTick, observable,createApp } from 'vue' nextTick(() => {}) const obj = observable({}) createApp({})
同理,例如<keep-alive>、<transition>和<teleport>等內置組件,如果沒有使用,也不會被打包到資源中。
3.更易于維護
(1)從Flow遷移到TypeScript
TypeScript是微軟開發(fā)的一個開源的編程語言,通過在JavaScript的基礎上添加靜態(tài)類型定義構建而成,其通過TypeScript編譯器或Babel轉譯為JavaScript代碼,可運行在任何瀏覽器和操作系統(tǒng)上。TypeScript引入了很多新的特性,例如類型監(jiān)測、接口等,這些特性在框架源碼的維護上有很大的提升。
在Vue 3的源碼結構層面,從Flow改成了TypeScript來編寫,F(xiàn)low是一個靜態(tài)類型檢測器,有了它就可以在JavaScript運行前找出常見的變量類型的bug,類似于Java語言中給變量強制指定類型,它的功能主要包括:
· 自動類型轉換。
· null引用。
· 處理undefined is not a function。
例如:
// @flow function foo(x: number): number { return x + 10 } foo('hi') // 參數(shù)x須為number類型,否則會報錯 錯誤信息: '[flow] string (This type is incompatible with number See also: function call)'
上面這段代碼采用了Flow后,如果類型不對就會報錯。一般來說,對于JavaScript源碼框架,引入類型檢測是非常重要的,不僅可以減少bug的產生,還可以規(guī)范一些接口的定義,這些特性和TypeScript非常吻合,所以在Vue 3中直接采用了TypeScript來進行重寫,從源碼層面來提升項目的可維護性。
(2)源代碼目錄結構遵循Monorepo
Monorepo是一種管理代碼的方式,它的核心觀點是所有的項目在一個代碼倉庫中,但是代碼分割到一個個小的模塊中,而不是都放在src這個目錄下。這樣的分割,使得每個開發(fā)者大部分時間只是工作在少數(shù)的幾個文件夾內,并且也只會編譯自己負責的模塊,不會導致一個IDE打不開太大的項目之類的事情,這樣很多事情就簡單了很多。Monorepo的結構如圖1-3所示。

圖1-3 Monorepo的結構
目前很多大型的框架(例如Babel、React、Angular、Ember、Meteor、Jest等)都采用了Monorepo這種方式來進行源碼的管理,當然在自己的業(yè)務項目中,也可以使用Monorepo來管理代碼。我們可以看一下Vue.js在采用Monorepo前后的源碼結構對比,如圖1-4所示。

圖1-4 Vue 2.x源碼目錄結構(左)和Vue 3.x源碼目錄結構(右)
- Kubernetes實戰(zhàn)
- OpenCV實例精解
- Hands-On Data Structures and Algorithms with JavaScript
- 跟老齊學Python:輕松入門
- Learning SAP Analytics Cloud
- The Computer Vision Workshop
- Apache Mesos Essentials
- 量化金融R語言高級教程
- Spring Boot進階:原理、實戰(zhàn)與面試題分析
- 編程可以很簡單
- Python計算機視覺和自然語言處理
- 30天學通C#項目案例開發(fā)
- Learning GraphQL and Relay
- JavaWeb入門經(jīng)典
- 軟件測試實驗實訓指南