官方介紹
在下次 DOM 更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào)或悲。在修改數(shù)據(jù)之后立即使用這個方法账胧,獲取更新后的 DOM悄但。
// 修改數(shù)據(jù)
vm.msg = 'Hello'
// DOM 還沒有更新
Vue.nextTick(function () {
// DOM 更新了
})
// 作為一個 Promise 使用 (2.1.0 起新增麻掸,詳見接下來的提示)
Vue.nextTick()
.then(function () {
// DOM 更新了
})
原理
Vue(2.6+) 在內(nèi)部對異步隊列嘗試使用原生的 Promise.then梭域、MutationObserver 和 setImmediate斑举,如果執(zhí)行環(huán)境不支持,則會采用 setTimeout(fn, 0) 代替病涨;
前兩者微任務(wù)富玷,后兩者宏任務(wù);
流程演示
Vue中,this.a="22222",數(shù)據(jù)被修改赎懦,簡易版流程(微任務(wù)版):
- a被修改雀鹃;
- 觸發(fā)a的setter,通知訂閱了a屬性的所有Watcher铲敛;
- Watcher收到通知褐澎,把該Watcher放到更新數(shù)組中;(過濾掉已存在數(shù)組中的相同Watcher伐蒋,保證同一個的Watcher在同一個tick只被執(zhí)行一次)
- 執(zhí)行其他同步代碼工三;
- 遍歷第三步收集到的更新數(shù)組,執(zhí)行里面所有Watcher的更新Dom方法先鱼;
- 執(zhí)行nextTick產(chǎn)生的微任務(wù)俭正,此時就能獲取到更新后的Dom;
- 渲染Dom焙畔,進(jìn)入下一個任務(wù)隊列;
nextTick.gif
測試代碼
<i id="a">{{a}}</i>
data() {
return {
a: 11
}
},
mounted() {
let time_start = Date.now()
consoleData('初始狀態(tài)')
// 兩秒后修改a的值
setTimeout(() => {
console.log('(2s后修改a的值)')
this.a = 22222
consoleData('修改后馬上獲取')
syncAwait()
this.$nextTick(function () {
consoleData('nextTick里獲取')
console.log('(此時更新了Dom宏多,但視圖未渲染)')
syncAwait()
consoleData('執(zhí)行完nextTick')
console.log('(執(zhí)行完該任務(wù)隊列,渲染視圖)')
})
}, 2000)
// 打印元素的內(nèi)容與寬度
function consoleData(title) {
console.log(`${Date.now() - time_start}ms,${title}:值為${$('#a').text()},寬度為${$('#a').width()}`);
}
// 模擬兩秒的同步代碼執(zhí)行
function syncAwait(time = 2000) {
console.log('執(zhí)行2s的同步代碼')
const startTime = Date.now();
while (true) {
if (Date.now() - startTime > time) { break; }
}
}
},
0ms,初始狀態(tài):值為11,寬度為16
(2s后修改a的值)
2006ms,修改后馬上獲取:值為11,寬度為16
執(zhí)行2s的同步代碼
4008ms,nextTick里獲取:值為22222,寬度為42
(此時更新了Dom肾请,但視圖未渲染)
執(zhí)行2s的同步代碼
6010ms,執(zhí)行完nextTick:值為22222,寬度為42
(執(zhí)行完該任務(wù)隊列,渲染視圖)
注意此時dom更新了但未渲染
dom更新了未渲染.JPG
其他
微任務(wù)與宏任務(wù)
- 常見的微任務(wù):Promise更胖、MutationObserver铛铁、Object.observe(廢棄),以及nodejs中的process.nextTick饵逐;
- 常見的宏任務(wù):setTimeout、setInterval倍权、setImmediate、I/O捞烟;
setImmediate
- 該方法可能不會被批準(zhǔn)成為標(biāo)準(zhǔn)账锹,目前只有最新版本的 Internet Explorer 和Node.js 0.10+實現(xiàn)了該方法。它遇到了 Gecko(Firefox) 和Webkit (Google/Apple) 的阻力坷襟;
- node.js里面setTimeout(fn, 0)會被強制改為setTimeout(fn, 1)奸柬,HTML 5里面setTimeout最小的時間限制是4ms婴程;
MutationObserver
- 會在指定的DOM發(fā)生變化時被調(diào)用;
- vue用的不是MutationObserver的DOM監(jiān)聽功能,而是用其生成微任務(wù)蒸绩;
Vue nextTick 實現(xiàn)順序修改歷史
- 第一版的 nextTick 實現(xiàn)順序為 MutationObserver, setImmediate,setTimeout
- 第一次修改是將 MutationObserver 替換為 postMessage
- 第二次修改順序變?yōu)?Promise, MutationObserver, setTimeout.
- 2.5.2+ Promise,setImmediate, Messagechannel, setTimeout
- 2.6+ Promise患亿、MutationObserver押逼、setImmediate步藕、setTimeout
參考
vue nextTick原理
Vue.nextTick挑格,了解一下?
Vue nextTick 變遷史
setTimeout和setImmediate到底誰先執(zhí)行漂彤,本文讓你徹底理解Event Loop
掘金小冊-剖析 Vue.js 內(nèi)部運行機制