<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          [科普文] Vue3 到底更新了什么?

          共 3878字,需瀏覽 8分鐘

           ·

          2022-05-28 00:21

          來(lái)自團(tuán)隊(duì) 王琛 同學(xué)的技術(shù)分享

          Vue3 已經(jīng)發(fā)布一段時(shí)間了,這個(gè)版本從底層實(shí)現(xiàn)到上層 API 設(shè)計(jì)都發(fā)生了非常大的變化,但具體改變了些什么呢?一起簡(jiǎn)單盤(pán)點(diǎn)下:

          一、Composition API

          使用傳統(tǒng)的option配置方法寫(xiě)組件的時(shí)候問(wèn)題,隨著業(yè)務(wù)復(fù)雜度越來(lái)越高,代碼量會(huì)不斷的加大;由于相關(guān)業(yè)務(wù)的代碼需要遵循option的配置寫(xiě)到特定的區(qū)域,導(dǎo)致后續(xù)維護(hù)非常的復(fù)雜,同時(shí)代碼可復(fù)用性不高,而composition-api就是為了解決這個(gè)問(wèn)題而生。

          「1.1 Options API 的問(wèn)題」

          使用傳統(tǒng)OptionsAPI時(shí),新增或者修改一個(gè)需求,就需要分別在data,methods,computed里修改 。當(dāng)業(yè)務(wù)邏輯和功能越來(lái)越多的時(shí)候理解和維護(hù)復(fù)雜組件變得困難。

          「1.2 Composition API 的優(yōu)勢(shì)」

          而Vue3 的組合式 API 將每個(gè)功能點(diǎn)抽成一個(gè)function使我們可以更加優(yōu)雅的組織我們的代碼。讓相關(guān)功能的代碼更加有序的組織在一起。

          「1.3 reactive對(duì)比ref」

          在 vue2.x 中,數(shù)據(jù)都是定義在data中。但是 Vue3.x 可以使用reactiveref來(lái)進(jìn)行數(shù)據(jù)定義。那么ref和reactive他們有什么區(qū)別呢?

          • 從原理角度對(duì)比:
            • ref用來(lái)創(chuàng)建一個(gè)包含響應(yīng)式的數(shù)據(jù)的引用對(duì)象

          接收數(shù)據(jù)可以是:基本數(shù)據(jù)類型、對(duì)象類型

          基本類型的數(shù)據(jù):響應(yīng)式依然是靠object.defineProperty()的get與set完成的

          對(duì)象類型:內(nèi)部求助vue3.0中一個(gè)新函數(shù)reactive函數(shù)通過(guò)proxy實(shí)現(xiàn)

          源碼地址:https://github.com/vuejs/vue-next/blob/master/packages/reactivity/src/ref.ts

            • reactive用來(lái)定義:對(duì)象和數(shù)組通過(guò)使用Proxy來(lái)實(shí)現(xiàn)響應(yīng)式(數(shù)據(jù)劫持), 并通過(guò)Reflect操作源對(duì)象內(nèi)部的數(shù)據(jù)。
          • 從使用角度對(duì)比:

            • ref定義的數(shù)據(jù):操作數(shù)據(jù)需要.value,讀取數(shù)據(jù)時(shí)模板中不需要.value直接使用即可。
            • reactive定義的數(shù)據(jù):操作數(shù)據(jù)與讀取數(shù)據(jù):均不需要.value

          1.4 新增 watchEffect 函數(shù)

          • watch 函數(shù)需要指明監(jiān)視的屬性,并在回調(diào)函數(shù)中執(zhí)行。默認(rèn)情況僅在偵聽(tīng)的源數(shù)據(jù)變更時(shí)才執(zhí)行回調(diào)。也可以加上immediate: true來(lái)使其立即生效
          • watchEffect不用指明監(jiān)視哪個(gè)屬性,監(jiān)視的回調(diào)中用到哪個(gè)屬性,就監(jiān)視哪個(gè)屬性。
          //watchEffect所指定的回調(diào)中用到的數(shù)據(jù)只要發(fā)生變化,則直接重新執(zhí)行回調(diào)。

          watchEffect(()=>{
          ???const?x1?=?sum.value
          ????const?x2?=?person.age
          ????console.log('watchEffect執(zhí)行了回調(diào)')
          })

          二、重寫(xiě) VDOM

          優(yōu)化前Virtual Domdiff算法,需要遍歷所有節(jié)點(diǎn),而且每一個(gè)節(jié)點(diǎn)都要比較舊的props和新的props有沒(méi)有變化。在Vue3.0中,只有帶PatchFlag的節(jié)點(diǎn)會(huì)被真正的追蹤,在后續(xù)更新的過(guò)程中,Vue不會(huì)追蹤靜態(tài)節(jié)點(diǎn),只追蹤帶有PatchFlag的節(jié)點(diǎn)來(lái)達(dá)到加快渲染的效果。

          <div>
          ????<span>vuespan>
          ????<span>{{msg}}span>
          ????<span?:id=?hello??class=?bar?>{{msg}}span>
          div>
          export?function?render(_ctx,_cache,$props,?$setup,$data,$options){
          ????return?(_openBlock(),_createBlock(??span ,null,[
          ????_createVNode(?span ,null,?vue?),
          ????_createVNode(?span ,null,_toDisplayString(_ctx.msg),?1?/*?TEXT?*/),
          ????_createVNode(?span ,{
          ????????id:?_ctx,hello
          ????????class:??bar?
          ????},_toDisplayString(_ctx.msg),9?/*?TEXT,?PROPS?*/,?[?id?])

          }

          上面的源碼中1 /* TEXT */這個(gè)標(biāo)記就是 PatchFlag,Vue只會(huì)追蹤第二個(gè)和第三個(gè)帶有PatchFlag的節(jié)點(diǎn)。

          在第三個(gè)span標(biāo)簽中PatchFlag變成了 9 /* TEXT, PROPS */, [ id ],提示我們這個(gè)dom元素中不僅有TEXT的變化,PROPS也可能會(huì)變化,后邊數(shù)組中的內(nèi)容則是有可能發(fā)生變化的屬性。而靜態(tài)添加的class沒(méi)有被標(biāo)記是因?yàn)?dom 元素的靜態(tài)屬性在渲染的時(shí)候就已經(jīng)創(chuàng)建了,并且是不會(huì)變動(dòng)的。在后面進(jìn)行更新的時(shí)候,diff 算法是不會(huì)去管它的。

          三、響應(yīng)式實(shí)現(xiàn)

          3.1 Vue2.x 的響應(yīng)式

          • vue官方文檔:https://cn.vuejs.org/v2/guide/reactivity.html
          • 實(shí)現(xiàn)原理:
            • 對(duì)象類型:通過(guò)Object.defineProperty()對(duì)屬性的讀取、修改進(jìn)行攔截(數(shù)據(jù)劫持)。
            • 數(shù)組類型:通過(guò)重寫(xiě)更新數(shù)組的一系列方法來(lái)實(shí)現(xiàn)攔截。(對(duì)數(shù)組的變更方法進(jìn)行了包裹)。
          Object.defineProperty(data,?'count',?{
          ????get?()?{},?
          ????set?()?{}
          })
          • 存在問(wèn)題:
            • 新增屬性、刪除屬性, 界面不會(huì)更新。
            • 無(wú)法監(jiān)聽(tīng)數(shù)組下標(biāo)和length長(zhǎng)度的變化。
            • 不支持 Map、Set、WeakMap 和 WeakSet。

          3.2 Vue3.0 的響應(yīng)式

          • 實(shí)現(xiàn)原理:
            • Proxy:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
            • Reflect:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
            • 通過(guò)Proxy(代理): 攔截對(duì)象中任意屬性的變化——屬性值的讀寫(xiě)、屬性的添加、屬性的刪除等。
            • 通過(guò)Reflect(反射): 對(duì)源對(duì)象的屬性進(jìn)行操作。
            • MDN文檔中對(duì)Proxy與Reflect描述:
          new?Proxy(data,?{
          ????????//?攔截讀取屬性值
          ????get?(target,?prop)?{
          ????????????return?Reflect.get(target,?prop)
          ????},
          ????//?攔截設(shè)置屬性值或添加新屬性
          ????set?(target,?prop,?value)?{
          ????????????return?Reflect.set(target,?prop,?value)
          ????},
          ????//?攔截刪除屬性
          ????deleteProperty?(target,?prop)?{
          ????????????return?Reflect.deleteProperty(target,?prop)
          ????}
          })

          proxy.name?=?'tom'???

          四、新的生命周期鉤子

          • 去掉了vue2.0中的 beforeCreate 和 created 兩個(gè)階段,新增了一個(gè)setup。執(zhí)行setup 時(shí),組件實(shí)例尚未被創(chuàng)建。
          • 每個(gè)生命周期函數(shù)必須導(dǎo)入才可以使用,并且所有生命周期函數(shù)需要統(tǒng)一放在 setup 里使用。
          • destroyed 銷毀后被重命名為 unmounted卸載后;beforeDestroy 銷毀前生命周期選項(xiàng)被重命名為 beforeUnmount卸載前。

          五、新的組件

          「5.1 片段(Fragment)」

          • Vue2: 組件必須有一個(gè)根標(biāo)簽
          <template>
          ????<div>
          ????????<span>span>
          ????????<span>span>
          ????div>
          template>
          • Vue3: 組件可以沒(méi)有根標(biāo)簽, 可以直接寫(xiě)多個(gè)根節(jié)點(diǎn),內(nèi)部會(huì)將多個(gè)標(biāo)簽包含在一個(gè)Fragment虛擬元素中
          <template>
          ????<span>span>
          ????<span>span>
          template>
          • 好處: 減少標(biāo)簽層級(jí), 減小內(nèi)存占用,提升了渲染性能

          5.2 Teleport

          Teleport ?就像是一個(gè)「任意門(mén)」,將包裹組件html結(jié)構(gòu)傳送到任何指定的地方。

          例如我們?nèi)粘i_(kāi)發(fā)中經(jīng)常會(huì)使用到彈窗組件,Dialog組件會(huì)被渲染到一層層子組件內(nèi)部,處理樣式、定位都變得十分困難。這時(shí)我們希望將組件掛載在body上面,來(lái)更方便的控制Dialog的樣式。簡(jiǎn)單來(lái)說(shuō),我們既希望繼續(xù)在組件內(nèi)部使用Dialog,又希望渲染的 DOM 結(jié)構(gòu)不嵌套在組件內(nèi)部的 DOM 中。就可以用到, 它可以在「不改變組件內(nèi)部元素父子關(guān)系」的情況下,建立一個(gè)傳送門(mén)將Dialog渲染的內(nèi)容傳送到body上面。

          <teleport?to=?body?>
          <div?v-if=?isShow??class=?dialog?>
          ????<div?class=?dialog?>
          ????????<h3>彈窗h3>
          ????????<button?@click=?isShow?=?false?>關(guān)閉彈窗button>
          ????div>
          div>
          teleport>

          5.3 Suspense

          • 等待異步組件時(shí)渲染一些額外內(nèi)容,讓?xiě)?yīng)用有更好的用戶體驗(yàn)
          • 它提供兩個(gè)template slot, 剛開(kāi)始會(huì)渲染一個(gè) fallback插槽下的內(nèi)容, 直到到達(dá)某個(gè)條件后才會(huì)渲染 default 插槽的正式內(nèi)容, 通過(guò)使用Suspense組件進(jìn)行展示異步渲染更加簡(jiǎn)單。