一、定义[nextTick、事件循环]

nextTick的由来:
由于VUE的数据驱动视图更新,是异步的,即修改数据的当下,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。
nextTick的触发时机:
在同一事件循环中的数据变化后,DOM完成更新,立即执行nextTick(callback)内的回调。
应用场景:
需要在视图更新之后,基于新的视图进行操作。
以上出现了事件循环的概念,其涉及到JS的运行机制,包括主线程的执行栈、异步队列、异步API、事件循环的协作,此处不展开之后再总结。大致理解:主线程完成同步环境执行,查询任务队列,提取队首的任务,放入主线程中执行;执行完毕,再重复该操作,该过程称为事件循环。而主线程的每次读取任务队列操作,是一个事件循环的开始。异步callback不可能处在同一事件循环中。
简单总结事件循环:
同步代码执行 -> 查找异步队列,推入执行栈,执行callback1[事件循环1] ->查找异步队列,推入执行栈,执行callback2[事件循环2]...
即每个异步callback,最终都会形成自己独立的一个事件循环。
结合nextTick的由来,可以推出每个事件循环中,nextTick触发的时机:
同一事件循环中的代码执行完毕 -> DOM 更新 -> nextTick callback触发
tips:本文的任务队列、消息队列、异步队列指同一个东西,均指macrotask queue。
事件循环详解:http://www.cnblogs.com/hity-tt/p/6733062.html 

二、实例理解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 = thislet count = 10000for (let i = 0; i < count; i++) {Vue.set(me.list1, i, 'I am a 测试信息~~啦啦啦' + i)}console.log('finished list1 ' + new Date().toString())for (let i = 0; i < count; i++) {Vue.set(me.list2, i, 'I am a 测试信息~~啦啦啦' + i)}console.log('finished list2 ' + new Date().toString())this.$nextTick(function() {// DOM 更新了console.log('finished tick1&2 ' + new Date().toString())console.log(document.querySelectorAll('li').length)})},composeList34() {let me = thislet count = 10000for (let i = 0; i < count; i++) {Vue.set(me.list3, i, 'I am a 测试信息~~啦啦啦' + i)}console.log('finished list3 ' + new Date().toString())this.$nextTick(function() {// DOM 更新了console.log('finished tick3 ' + new Date().toString())console.log(document.querySelectorAll('li').length)})setTimeout(me.setTimeout1, 0)},setTimeout1() {let me = thislet count = 10000for (let i = 0; i < count; i++) {Vue.set(me.list4, i, 'I am a 测试信息~~啦啦啦' + i)}console.log('finished list4 ' + new Date().toString())me.$nextTick(function() {// DOM 更新了console.log('finished tick4 ' + new Date().toString())console.log(document.querySelectorAll('li').length)})},composeList5() {let me = thislet count = 10000this.$nextTick(function() {// DOM 更新了console.log('finished tick5-1 ' + new Date().toString())console.log(document.querySelectorAll('li').length)})setTimeout(me.setTimeout2, 0)},setTimeout2() {let me = thislet count = 10000for (let i = 0; i < count; i++) {Vue.set(me.list5, i, 'I am a 测试信息~~啦啦啦' + i)}console.log('finished list5 ' + new Date().toString())me.$nextTick(function() {// DOM 更新了console.log('finished tick5 ' + new Date().toString())console.log(document.querySelectorAll('li').length)})}}
}</script>

2.1、用例设计
用例1:通过list1、2、3验证,处在同步代码中的DOM更新情况及nextTick的触发时机;
用例2:通过list3、list4验证,同步代码及异步代码中Dom更新及nextTick触发的区别;
用例3:通过list4、list5对比验证,多个异步代码中nextTick触发的区别;

用例4:通过在视图更新后获取DOM中<li>的数量,判断nextTick序列渲染的时间点。
2.2、代码分析
函数执行步骤:
事件循环1:
step1: this.composeList12() -> update list1, update list2 -> 绑定tick’1&2’
step2: this.composeList34() -> update list3, 设置异步1setTimeout1 -> 绑定tick’3’
step3: this.composeList5() -> 绑定tick’5-1’ -> 设置异步2setTimeout2 
step4: 绑定tick’test’
事件循环2:
将setTimeout1的callback推入执行栈 -> update list4 -> 绑定tick’4’
事件循环3:
将setTimeout2的callback推入执行栈 -> update list5 -> 绑定tick’5’
2.3、推断输出消息
由于同一事件循环中的tick按执行顺序,因此消息输出为即:
[同步环境]update list1 -> update list2 -> update list3 -> tick‘1&2’ -> tick‘3’ -> tick’5-1’ -> tick’test'
[事件循环1]->update list4 -> tick’4’ 
[事件循环2] ->update list5 -> tick’5’
2.4、实际运行结果如下图
该demo中,设置了5个size为10000的数组,从而能从时间及消息输出两个维度来了解nextTick的执行情况。另外,额外增加了一个参数,即更新后的视图中<li>的数量,从这个数量,可以考察出同一事件循环中的nextTick执行情况。由运行结果图可以看出实际的输出与推导的输出结果相符合。
2.5、总结
从用例1得出:
a、在同一事件循环中,只有所有的数据更新完毕,才会调用nextTick;
b、仅在同步执行环境数据完全更新完毕,DOM才开始渲染,页面才开始展现;
c、在同一事件循环中,如果存在多个nextTick,将会按最初的执行顺序进行调用;
从用例1+用例4得出:
d、从同步执行环境中的四个tick对应的‘li’数量均为30000可看出,同一事件循环中,nextTick所在的视图是相同的;
从用例2得出:
e、只有同步环境执行完毕,DOM渲染完毕之后,才会处理异步callback
从用例3得出:
f、每个异步callback最后都会处在一个独立的事件循环中,对应自己独立的nextTick;
从用例1结论中可得出:
g、这个事件环境中的数据变化完成,在进行渲染[视图更新],可以避免DOM的频繁变动,从而避免了因此带来的浏览器卡顿,大幅度提升性能;
从b可以得出:
h、在首屏渲染、用户交互过程中,要巧用同步环境及异步环境;首屏展现的内容,尽量保证在同步环境中完成;其他内容,拆分到异步中,从而保证性能、体验。
tips:
1、可产生异步callback的有:promise(microtask queue)、setTimeout、MutationObserver、DOM事件、Ajax等;
2、 vue DOM的视图更新实现,,使用到了ES6的Promise及HTML5的MutationObserver,当环境不支持时,使用setTimeout(fn, 0)替代。上述的三种方法,均为异步API。其中MutationObserver类似事件,又有所区别;事件是同步触发,其为异步触发,即DOM发生变化之后,不会立刻触发,等当前所有的DOM操作都结束后触发。关于异步API、事件循环将在以后补充。
事件循环、任务队列详解:http://www.cnblogs.com/hity-tt/p/6733062.html 
原文:https://www.cnblogs.com/hity-tt/p/6729118.html#4099338

转载于:https://www.cnblogs.com/kelly-sunshine/p/10795083.html

vue nextTick深入理解-vue性能优化、DOM更新时机、事件循环机制相关推荐

  1. 深入理解 vue DOM 更新时机

    vue3 官方文档中对 DOM 更新时机 的解释是 当你更改响应式状态后,DOM 也会自动更新.然而,你得注意 DOM 的更新并不是同步的.相反,Vue 将缓冲它们直到更新周期的 "下个时机 ...

  2. 浏览器事件循环机制与Vue nextTick的实现

    浏览器事件循环机制 先上一段简单的代码 console.log('aa'); setTimeout(() => { console.log('bb')}, 0); Promise.resolve ...

  3. 10个问题带你全面理解Linux性能优化

    10个问题带你全面理解Linux性能优化 • Feiskyhttps://feisky.xyz/posts/2020-06-06-linux-perf/本文整理自极客时间"10个问题带你全面 ...

  4. mysql优化十:从架构角度全局理解mysql性能优化

    从架构角度全局理解mysql性能优化 MySQL性能优化其实是个很大的课题,在优化上存在着一个调优金字塔的说法: 很明显从图上可以看出,越往上走,难度越来越高,收益却是越来越小的.比如硬件和 OS调优 ...

  5. 对JavaScript事件循环机制的理解

    前言: 这次主要整理一下自己对 Js事件循环机制,同步,异步任务,宏任务,微任务的理解,大概率暂时还有些偏差或者错误.如果有,十分欢迎各位纠正我的错误! 一.事件循环和任务队列产生的原因: 首先,JS ...

  6. js的事件循环机制:同步与异步任务(setTimeout,setInterval)宏任务,微任务(Promise,process.nextTick)...

    javascript是单线程,一切javascript版的"多线程"都是用单线程模拟出来的,通过事件循环(event loop)实现的异步. javascript事件循环 事件循环 ...

  7. vue延迟渲染组件_性能优化之组件懒加载: Vue Lazy Component 介绍

    这篇文章分享了从遇到前端业务性能问题,到分析.解决并且梳理出通用的Vue 2.x 组件级懒加载解决方案(Vue Lazy Component )的过程. 初始加载资源过多 问题起源于我们的一个页面,下 ...

  8. Vue底层判断标签的性能优化方法

    在vue中,如果写div.span等正常的html标签,vue会解析成传统的html标签,但当写不是这些标签的时候,vue会认为他是一个组件,例如:.是如何做到这种判断的呢,首先自己来实现一个这样的判 ...

  9. 从架构师角度全局理解Mysql性能优化

    MySQL性能优化其实是个很大的课题,在优化上存在着一个调优金字塔的说法: 很明显从图上可以看出,越往上走,难度越来越高,收益却是越来越小的. 所以在进行优化时,首先需要关注和优化的应该是架构,如果架 ...

最新文章

  1. 为什么不应该重写service方法?
  2. DBoW2 cmake代码注释
  3. java中改变字符串编码
  4. 再来一顿贺岁宴 | 从K-Means到Capsule
  5. 【感想文】感情经历,是否给你我带来的些许提升?我想,有。
  6. iOS 时间戳的转换
  7. java编写斐波那契数列,实战案例
  8. admin客户管理系统html5模板
  9. 【AI+餐饮】人工智能进军餐饮:AI 调酒,越喝越有
  10. 数据仓库ETL(二)基本概念
  11. IntelliJ Idea中使用Java8新特性lambda表达式
  12. 吴恩达机器学习作业Python实现(三):多类分类和前馈神经网络
  13. 若依如何修改数据监控登录账户密码?
  14. 没有钱的男人和有钱的男人区别在哪里?
  15. 负载均衡调度算法追溯
  16. creo外观库_Proe/Creo外观着色与贴图
  17. java导出excel锁定状态_填报表导出excel后不可写的单元格处于锁定状态
  18. JSON在线序列化网站
  19. 0元参会丨第十届数据技术嘉年华精彩抢先速览
  20. 软件开发人员如何自我学习?

热门文章

  1. 详解java二叉排序树_java实现二叉排序树
  2. java web宿舍管理系统源码_JavaWeb宿舍管理系统(附 演示、源码下载地址)
  3. 适合pythonpandas的软件_11个Python Pandas小技巧让你的工作更高效(附代码实例)
  4. 第七章节 类的抽象(抽象类)
  5. 第六章节 多态 (多态的概述)
  6. Lc101对称二叉树
  7. Problem L. Graph Theory Homework
  8. Unity3D之UGUI基础5:Toggle复选框
  9. python 中的堆栈 用列表实现
  10. C++ STL bitset类常用函数的使用