一、js执行机制
js是单线程语言
单线程:指一个时间只能做一件事,进行一个任务时,不能同时进行其他任务。
疑问:一个时间只能做一件事,不能并行处理,那效率必然很低啊,为什么不能是多线程呢?
原因:是否多线程这个取决于语言的用途,一个很简单的例子,如果同一时间,一个添加了 DOM
,一个删除了 DOM
, 这个时候语言就不知道是该添还是该删了,所以从应用场景来看 JS
只能是单线程。
但是!单线程就意味着我们所有的任务都需要排队,后面的任务必须等待前面的任务完成才能执行,如果前面的任务耗时很长,一些从用户角度上不需要等待的任务就会一直等待,这个从体验角度上来讲是不可接受的,所以JS
中就出现了异步的概念。
注意:我们写完的代码从上至下顺序执行,压入执行栈中,当碰到异步任务(异步任务通常指的是那些不会立即执行完毕的任务,而是在将来的某个时刻完成,像生命周期钩子、定时器、Promise等)时,将异步任务交由浏览器其他线程处理(浏览器是多线程的),任务结束后放入任务队列中。
具体运行机制:
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步
二、nextTick的本质
nextTick的目的是获取更新后的DOM。
实现很简单,完全是基于语言执行机制实现,直接创建一个异步任务,那么nextTick
自然就达到在同步任务后执行的目的。
//nextTick源码
const p = Promise.resolve()
export function nextTick(fn?: () => void): Promise<void> {
return fn ? p.then(fn) : p
}
三、nextTick是vue的更新策略
{{num}}
for(let i=0; i<100000; i++){
num = i
}
根据上面的代码,大家猜一猜vue是怎么更新数据的???
猜想一:每改变一次,视图更新一下,一共要更新100000次
猜想二:等全部改变完成,视图更新,一共更新1次
从性能优化的角度来看是猜想二更优,但是这样会带来一个问题,就是数据更改后不会立即触发视图更新,那要在什么时候触发呢?
原来,当响应式数据改变后,会触发函数queueJob,queueJob调用queueFlush,queueFlush开启异步任务(nextTick)处理flushJobs,flushJobs先对队列进行排序,然后执行queue中的job
所以,简单来说,为了提高性能,Vue 批量异步执行数据变化的更新。当响应式数据变化时,Vue 会将更新延迟到下一个事件循环“tick”,并合并在同一个“tick”中的多个数据变化。
如果没有 nextTick
更新机制,那么 num
每次更新值都会触发视图更新,有了nextTick
机制,只需要更新一次,所以为什么有nextTick
存在,相信大家心里已经有答案了。
四、总结
nextTick是vue中的更新策略,也是性能优化手段,基于JS执行机制实现
vue中我们改变数据时不会立即触发视图,如果需要实时获取到最新的DOM,这个时候可以手动调用nextTick!
最后推荐大家去读读官方文档 https://vue3js.cn/global/nextTick.html