「深入淺出」主流前端框架更新批處理方式
一 背景
大家好,我是 alien ,一提到更新,是前端框架中一個(gè)老生常談的問(wèn)題,這些知識(shí)也是在面試中,面試官比較喜歡問(wèn)的,那么在不同的技術(shù)框架背景下,處理更新的手段各不相同,今天我們來(lái)探討一下,主流的前端框架批量處理的方式,和其內(nèi)部的實(shí)現(xiàn)原理。
通過(guò)今天的學(xué)習(xí),你將收獲這些內(nèi)容:
?主流前端框架的批量更新方式。 ?vue 和 react 批量更新的實(shí)現(xiàn)。 ?宏任務(wù)和微任務(wù)的特點(diǎn)。
1 一次 vue 案例
首先來(lái)想一個(gè)問(wèn)題。比如在 vue 中一次更新中。
<template>
???<div>
???????姓名:?{{ name }}
???????年齡:?{{ age }}
???????<button?@click="handleClick"?>點(diǎn)擊button>
???div>
template>
<script>
export?default?{
????data(){
????????return?{
????????????age:0,
????????????name:''
????????}
????},
????methods:{
????????handleClick(){
????????????this.name?=?'alien'
????????????this.age?=?18
????????}
????}
}
script>
如上是一個(gè)非常簡(jiǎn)單的邏輯代碼,點(diǎn)擊按鈕,會(huì)觸發(fā) name 和 age 的更新。那么首先想一個(gè)問(wèn)題就是:
正常情況下,vue 的數(shù)據(jù)層是通過(guò)響應(yīng)式處理的,那么比如 age 和 name 可以理解成做了一層屬性代理,字符串模版 template 里面的屬性 ( name 和 age ) 的 get 會(huì)和組件的渲染 watcher ( vue3.0 里面的 effect )建立起關(guān)聯(lián)。
一次重新賦值會(huì)觸發(fā) set ,那么根據(jù)響應(yīng)式,會(huì)觸發(fā)渲染 watcher 重新執(zhí)行,然后就會(huì)重新更新組件,渲染視圖。
那么暴露的問(wèn)題就是,我們?cè)?handleClick 中,同時(shí)改變了 name 和 age 屬性,那么按照正常情況下,會(huì)分別觸發(fā) name 和 age 的 set,那么如果不做處理,那么會(huì)讓渲染 watcher 執(zhí)行兩次,結(jié)果就是組件會(huì) update 兩次,但是結(jié)果是這樣的嗎?
結(jié)果是:vue 底層通過(guò)批量處理,只讓組件 update 一次。
2 一次 react 案例
上面介紹了在 vue 中更新批處理的案例之后,我們來(lái)看一下在 react 中的批量更新處理。把上述案例用 react 來(lái)實(shí)現(xiàn)一下:
function?Index(){
????const?[?age?,?setAge?]?=?React.useState(0)
????const?[?name,?setName?]?=?React.useState('')
????return?<div>
???????姓名:?{name}
???????年齡:?{age}
???????<button?onClick={()=>{
??????????setAge(18)
??????????setName('alien')
???????}}
???????>點(diǎn)擊button>
????div>
}
點(diǎn)擊按鈕,觸發(fā)更新,會(huì)觸發(fā)兩次 useState 的更新函數(shù)。那么 React 的更新流程大致是這樣的。
首先會(huì)找到 fiberRoot 。 然后進(jìn)行調(diào)和流程。執(zhí)行 Index 組件,得到新的 element。 diff fiber,得到 effectList。 執(zhí)行 effect list,得到最新的 dom ,并進(jìn)行渲染繪制。
那么按常理來(lái)說(shuō),Index 組件會(huì)執(zhí)行兩次??墒聦?shí)是只執(zhí)行一次 render。
3 批量處理意義
通過(guò)上面的案例說(shuō)明在主流框架中,對(duì)于更新都采用批處理。一次上下文中的 update 會(huì)被合并成一次更新。那么為什么要進(jìn)行更新批處理呢?
批處理主要是出于對(duì)性能方面的考慮,這里拿 react 為例子,看一下批處理前后的對(duì)比情況:
??例子一:假設(shè)沒(méi)有批量更新:
/ ------ js 層面 ------
第一步:發(fā)生點(diǎn)擊事件觸發(fā)一次宏任務(wù)。 第二步:執(zhí)行 setAge ,更新 fiber 狀態(tài)。 第三步:進(jìn)行 render 階段,Index 執(zhí)行,得到新的 element。得到 effectlist. 第四步:進(jìn)行 commit 階段,更新 dom。 第五步:執(zhí)行 setName ,更新 fiber 狀態(tài)。 第六步:重復(fù)執(zhí)行第三步,第四步。
/ ------ 瀏覽器渲染 ------
js 執(zhí)行完畢,渲染真實(shí)的 dom 元素。
我們可以看到如果沒(méi)有批量更新處理,那么會(huì)多走很多步驟,包括 render 階段 ,commit 階段,dom 的更新等,這些都會(huì)造成性能的浪費(fèi),接下來(lái)看一下有批量更新的情況。
??例子二:存在批量更新。
/ ------ js 層面 ------
第一步:發(fā)生點(diǎn)擊事件觸發(fā)一次宏任務(wù)。 第二步:setAge 和 setName 批量處理 ,更新 fiber 狀態(tài)。 第三步:進(jìn)行 render 階段,Index 執(zhí)行,得到新的 element。得到 effectlist. 第四步:進(jìn)行 commit 階段,更新 dom。
/ ------ 瀏覽器渲染 ------
js 執(zhí)行完畢,渲染真實(shí)的 dom 元素。
從上面可以直觀看到更新批處理的作用了,本質(zhì)上在 js 的執(zhí)行上下文上優(yōu)化了很多步驟,減少性能開(kāi)銷(xiāo)。
二 簡(jiǎn)述宏任務(wù)和微任務(wù)
在正式講批量更新之前,先來(lái)溫習(xí)一下宏任務(wù)和微任務(wù),這應(yīng)該算是前端工程師必須掌握的知識(shí)點(diǎn)。
所謂宏任務(wù),我們可以理解成, 感谢您访问我们的网站,您可能还对以下资源感兴趣:
