官术网_书友最值得收藏!

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源碼目錄結構(右)

主站蜘蛛池模板: 蒲江县| 稷山县| 兴海县| 昂仁县| 商城县| 天祝| 资源县| 大名县| 四川省| 屏南县| 获嘉县| 洪江市| 华坪县| 巴彦县| 漠河县| 金寨县| 全州县| 永兴县| 甘德县| 太谷县| 通化市| 安义县| 曲靖市| 栖霞市| 陆丰市| 澄迈县| 台山市| 益阳市| 辰溪县| 饶阳县| 藁城市| 盖州市| 临猗县| 盖州市| 尚志市| 沁水县| 定西市| 开江县| 安庆市| 阜新| 上思县|