一、定义[nextTick、事件循环]
nextTick的由来:
由于VUE的数据驱动视图更新,是异步的,即修改数据的当下,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。
nextTick的触发时机:
在同一事件循环中的数据变化后,DOM完成更新,立即执行nextTick(callback)内的回调。
应用场景:
需要在视图更新之后,基于新的视图进行操作。
以上出现了事件循环的概念,其涉及到JS的运行机制,包括主线程的执行栈、异步队列、异步API、事件循环的协作,此处不展开之后再总结。大致理解:主线程完成同步环境执行,查询任务队列,提取队首的任务,放入主线程中执行;执行完毕,再重复该操作,该过程称为事件循环。而主线程的每次读取任务队列操作,是一个事件循环的开始。异步callback不可能处在同一事件循环中。
简单总结事件循环:
同步代码执行 -> 查找异步队列,推入执行栈,执行callback1[事件循环1] ->查找异步队列,推入执行栈,执行callback2[事件循环2]...
即每个异步callback,最终都会形成自己独立的一个事件循环。
结合nextTick的由来,可以推出每个事件循环中,nextTick触发的时机:
同一事件循环中的代码执行完毕 -> DOM 更新 -> nextTick callback触发
tips:本文的任务队列、消息队列、异步队列指同一个东西,均指macrotask queue。
二、实例理解nextTick的使用,并给出在页面渲染上的优化巧用
(tips:代码的正确阅读方式:看template组成、跳过script代码、看代码后面的用例设计、看之后的代码分析、同时结合回头结合script代码理解)
<template> <div> <ul> <li v-for="item in list1">{{item}}</li> </ul> <ul> <li v-for="item in list2">{{item}}</li> </ul> <ol> <li v-for="item in list3">{{item}}</li> </ol> <ol> <li v-for="item in list4">{{item}}</li> </ol> <ol> <li v-for="item in list5">{{item}}</li> </ol> </div> </template> <script type="text/javascript"> export default { data() { return { list1: [], list2: [], list3: [], list4: [], list5: [] } }, created() { this.composeList12() this.composeList34() this.composeList5() this.$nextTick(function() { // DOM 更新了 console.log('finished test ' + new Date().toString()) console.log(document.querySelectorAll('li').length) }) }, methods: { composeList12() { let me = this let count = 10000<span style="color: rgba(0, 0, 255, 1)">for</span> (let i = 0; i < count; i++<span style="color: rgba(0, 0, 0, 1)">) { Vue.set(me.list1, i, </span>'I am a 测试信息~~啦啦啦' +<span style="color: rgba(0, 0, 0, 1)"> i) } <span style="background-color: rgba(227, 227, 227, 1)">console.log(</span></span><span style="background-color: rgba(227, 227, 227, 1)">'finished list1 ' + <span style="color: rgba(0, 0, 255, 1)">new</span></span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(227, 227, 227, 1)"> Date().toString()</span>) </span><span style="color: rgba(0, 0, 255, 1)">for</span> (let i = 0; i < count; i++<span style="color: rgba(0, 0, 0, 1)">) { Vue.set(me.list2, i, </span>'I am a 测试信息~~啦啦啦' +<span style="color: rgba(0, 0, 0, 1)"> i) } <span style="background-color: rgba(227, 227, 227, 1)">console.log(</span></span><span style="background-color: rgba(227, 227, 227, 1)">'finished list2 ' + <span style="color: rgba(0, 0, 255, 1)">new</span></span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(227, 227, 227, 1)"> Date().toString()</span>) </span><span style="color: rgba(0, 0, 255, 1)">this</span>.$nextTick(<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> DOM 更新了</span> <span style="background-color: rgba(227, 227, 227, 1)">console.log('finished tick1&2 ' + <span style="color: rgba(0, 0, 255, 1)">new</span></span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(227, 227, 227, 1)"> Date().toString()</span>) <span style="background-color: rgba(227, 227, 227, 1)">console.log(document.querySelectorAll(</span></span><span style="background-color: rgba(227, 227, 227, 1)">'li'</span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(227, 227, 227, 1)">).length</span>) }) }, composeList34() { let me </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)"> let count </span>= 10000 <span style="color: rgba(0, 0, 255, 1)">for</span> (let i = 0; i < count; i++<span style="color: rgba(0, 0, 0, 1)">) { Vue.set(me.list3, i, </span>'I am a 测试信息~~啦啦啦' +<span style="color: rgba(0, 0, 0, 1)"> i) } <span style="background-color: rgba(227, 227, 227, 1)">console.log(</span></span><span style="background-color: rgba(227, 227, 227, 1)">'finished list3 ' + <span style="color: rgba(0, 0, 255, 1)">new</span></span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(227, 227, 227, 1)"> Date().toString()</span>) </span><span style="color: rgba(0, 0, 255, 1)">this</span>.$nextTick(<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> DOM 更新了</span> <span style="background-color: rgba(227, 227, 227, 1)">console.log('finished tick3 ' + <span style="color: rgba(0, 0, 255, 1)">new</span></span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(227, 227, 227, 1)"> Date().toString()</span>) <span style="background-color: rgba(227, 227, 227, 1)">console.log(document.querySelectorAll(</span></span><span style="background-color: rgba(227, 227, 227, 1)">'li'</span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(227, 227, 227, 1)">).length</span>) }) setTimeout(me.setTimeout1, </span>0<span style="color: rgba(0, 0, 0, 1)">) }, setTimeout1() { let me </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)"> let count </span>= 10000 <span style="color: rgba(0, 0, 255, 1)">for</span> (let i = 0; i < count; i++<span style="color: rgba(0, 0, 0, 1)">) { Vue.set(me.list4, i, </span>'I am a 测试信息~~啦啦啦' +<span style="color: rgba(0, 0, 0, 1)"> i) } <span style="background-color: rgba(227, 227, 227, 1)">console.log(</span></span><span style="background-color: rgba(227, 227, 227, 1)">'finished list4 ' + <span style="color: rgba(0, 0, 255, 1)">new</span></span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(227, 227, 227, 1)"> Date().toString()</span>) me.$nextTick(</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> DOM 更新了</span> <span style="background-color: rgba(227, 227, 227, 1)">console.log('finished tick4 ' + <span style="color: rgba(0, 0, 255, 1)">new</span></span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(227, 227, 227, 1)"> Date().toString()</span>) <span style="background-color: rgba(227, 227, 227, 1)">console.log(document.querySelectorAll(</span></span><span style="background-color: rgba(227, 227, 227, 1)">'li'</span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(227, 227, 227, 1)">).length</span>) }) }, composeList5() { let me </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)"> let count </span>= 10000 <span style="color: rgba(0, 0, 255, 1)">this</span>.$nextTick(<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> DOM 更新了</span> <span style="background-color: rgba(227, 227, 227, 1)">console.log('finished tick5-1 ' + <span style="color: rgba(0, 0, 255, 1)">new</span></span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(227, 227, 227, 1)"> Date().toString()</span>) <span style="background-color: rgba(227, 227, 227, 1)">console.log(document.querySelectorAll(</span></span><span style="background-color: rgba(227, 227, 227, 1)">'li'</span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(227, 227, 227, 1)">).length</span>) }) setTimeout(me.setTimeout2, </span>0<span style="color: rgba(0, 0, 0, 1)">) }, setTimeout2() { let me </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)"> let count </span>= 10000 <span style="color: rgba(0, 0, 255, 1)">for</span> (let i = 0; i < count; i++<span style="color: rgba(0, 0, 0, 1)">) { Vue.set(me.list5, i, </span>'I am a 测试信息~~啦啦啦' +<span style="color: rgba(0, 0, 0, 1)"> i) } <span style="background-color: rgba(227, 227, 227, 1)">console.log(</span></span><span style="background-color: rgba(227, 227, 227, 1)">'finished list5 ' + <span style="color: rgba(0, 0, 255, 1)">new</span></span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(227, 227, 227, 1)"> Date().toString()</span>) me.$nextTick(</span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">() { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> DOM 更新了</span> <span style="background-color: rgba(227, 227, 227, 1)">console.log('finished tick5 ' + <span style="color: rgba(0, 0, 255, 1)">new</span></span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(227, 227, 227, 1)"> Date().toString()</span>) <span style="background-color: rgba(227, 227, 227, 1)">console.log(document.querySelectorAll(</span></span><span style="background-color: rgba(227, 227, 227, 1)">'li'</span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(227, 227, 227, 1)">).length</span>) }) } }