Vue3 的批量渲染機(jī)制的兩種方法
兩種機(jī)制分別指:
- 在 1次 回調(diào)里觸發(fā) 多次
computed/effect,其只會(huì)執(zhí)行 1次 - 在 1次 回調(diào)里 多次 更新與視圖相關(guān)的
ref,視圖更新也只執(zhí)行 1次
舉兩個(gè)例子:
// effectA
effect(() => {
console.log('A:', a.value)
if (a.value > 5) {
// 2、再觸發(fā)第2次 effectB
b.value = a.value * 2
}
})
// effectB
effect(() => {
console.log('B:', b.value)
})
// 1、更新dep, 觸發(fā)1次 effectA 和 1次 effectB
a.value = 6
// 實(shí)際 effectB 只會(huì)執(zhí)行 1 次,而不是 2 次
btn.onclick = () => {
count.value++
count.value++
count.value++
count.value++
}
return () => {
console.log('render call')
return h('div', `count: ${count.value}`)
}
// 點(diǎn)擊按鈕,實(shí)際 'render call' 只會(huì)被打印1次
如何實(shí)現(xiàn)的?簡(jiǎn)寫源碼 來(lái)說(shuō)明: PS: 不熟悉響應(yīng)式基礎(chǔ)的,請(qǐng)移步往期文章
effect的批處理
修改響應(yīng)式變量的值后,會(huì)觸發(fā) setter 里的函數(shù) trigger,之后會(huì)沿著鏈表通知所有 effect:
export function traggerRef(dep: RefImpl) {
......
propagate(dep.subs)
......
}
/** 傳播更新 */
export function propagate(subs: Link) {
// 鏈表節(jié)點(diǎn)
let link = subs
// 收集 effect
const queuedEffect = []
while (link) {
const sub = link.sub
// 標(biāo)記 dirty,防止重復(fù)觸發(fā) effect
if (!sub.tracking && !sub.dirty) {
sub.dirty = true
// ......
queuedEffect.push(sub)
// ......
}
// 遍歷鏈表
link = link.nextSub
}
// 通知 effect 執(zhí)行
queuedEffect.forEach(effect => effect.notify())
}
可以看到 sub 里有一個(gè) dirty 屬性,如果同一次回調(diào)函數(shù)中,多次觸發(fā) sub,它只會(huì)被放入待執(zhí)行列表 1 次,也就是不會(huì)多次執(zhí)行。
注意,dirty 標(biāo)志位會(huì)等 Effect 真正執(zhí)行完成后才重置。
異步渲染render
mount組件的流程是:使用VNode創(chuàng)建組件實(shí)例instance -> 掛載到DOM -> 更新,組件實(shí)際上就是創(chuàng)建了一個(gè) Effect 來(lái)訂閱更新:
const mountComponent = (vnode, container, anchor) => {
/**
* 1、創(chuàng)建組件實(shí)例
* 2、初始化狀態(tài)
* 3、掛載到DOM
*/
// 1 實(shí)例化
const instance = createComponentInstance(vnode)
// 2 初始化
setupComponent(instance)
const componentUpdateFn = () => {
// 首次掛載
if (!instance.isMounted) {
// 得到 Virtual DOM
const subTree = instance.render()
// 3 掛載
patch(null, subTree, container, anchor)
// 保存當(dāng)前 V-DOM
instance.subTree = subTree
// 修改標(biāo)志位
instance.isMounted = true
} else {
// 更新
const preSubTree = instance.subTree
// 獲取新的 V-DOM
const subTree = instance.render()
// 對(duì)比新舊 VNode,更新
patch(preSubTree, subTree, container, anchor)
instance.subTree = subTree
}
}
const effect = new ReactiveEffect(componentUpdateFn)
const update = effect.run.bind(effect)
instance.update = update
effect.scheduler = () => {
queueJob(update)
}
effect.run()
}
但假如有如下例子,假如點(diǎn)擊 1次 按鈕,將打印 4次 effect execute 和 1次 render call:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
</head>
<body>
<div id="app"></div>
<button id="btn">+++</button>
<script type="module">
import { createApp, h, ref, effect, computed } from 'vue'
const rootComp = {
setup() {
const count = ref(0)
btn.onclick = () => {
count.value++
count.value++
count.value++
count.value++
}
effect(() => {
console.log('effect execute', count.value)
})
return () => {
console.log('render call')
return h('div', `count1: ${count.value}`)
}
},
}
createApp(rootComp).mount('#app')
</script>
</body>
</html>
按理說(shuō) render call 也應(yīng)該打印 4次,Why? 因?yàn)榇a里利用 Effect.scheduler 做了 異步更新,即重寫了scheduler:
const componentUpdateFn = () => {
//......
const update = effect.run.bind(effect)
instance.update = update
effect.scheduler = () => {
queueJob(update)
}
//......
}
function queueJob(job) {
Promise.resolve().then(() => {
job()
})
}
此時(shí),每次 ref 更新后,不立即重置 dirty,而是等所有同步任務(wù)執(zhí)行完后,再執(zhí)行渲染,BINGO
到此這篇關(guān)于Vue3 的批量渲染機(jī)制的兩種方法的文章就介紹到這了,更多相關(guān)Vue3 批量渲染內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue如何實(shí)現(xiàn)el-select下拉選項(xiàng)的懶加載
這篇文章主要介紹了vue如何實(shí)現(xiàn)el-select下拉選項(xiàng)的懶加載,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04
vue3如何利用自定義指令實(shí)現(xiàn)下拉框分頁(yè)懶加載
下拉框一開(kāi)始請(qǐng)求第一頁(yè)的內(nèi)容,滾動(dòng)到最后的時(shí)候,請(qǐng)求第二頁(yè)的內(nèi)容,如此反復(fù),直到所有數(shù)據(jù)加載完成,這篇文章主要介紹了vue3如何利用自定義指令實(shí)現(xiàn)下拉框分頁(yè)懶加載,需要的朋友可以參考下2024-07-07
element表格數(shù)據(jù)部分模糊的實(shí)現(xiàn)代碼
這篇文章給大家介紹了element表格數(shù)據(jù)模糊的實(shí)現(xiàn)代碼,文中有詳細(xì)的效果展示和實(shí)現(xiàn)代碼供大家參考,具有一定的參考價(jià)值,需要的朋友可以參考下2024-01-01
關(guān)于vue2使用element?UI中Descriptions組件的遍歷問(wèn)題詳解
最近element-ui遇到了很多坑,下面這篇文章主要給大家介紹了關(guān)于vue2使用element?UI中Descriptions組件的遍歷問(wèn)題,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
vue通過(guò)過(guò)濾器實(shí)現(xiàn)數(shù)據(jù)格式化
這篇文章主要介紹了vue通過(guò)過(guò)濾器實(shí)現(xiàn)數(shù)據(jù)格式化的方法,文中講解非常細(xì)致,幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07
Vue3.0?axios跨域請(qǐng)求代理服務(wù)器配置方式
這篇文章主要介紹了Vue3.0?axios跨域請(qǐng)求代理服務(wù)器配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04
vue 地圖可視化 maptalks 篇實(shí)例代碼詳解
這篇文章主要介紹了vue 地圖可視化 maptalks 篇,本文分步驟通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2019-05-05
IDEA中Debug調(diào)試VUE前端項(xiàng)目調(diào)試JS只需兩步
這篇文章主要為大家介紹了在IDEA中Debug調(diào)試VUE前端項(xiàng)目,只需要兩步就可以調(diào)試JS的實(shí)現(xiàn)方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-02-02

