公司做之前项目的时候,遇到了一些比较困惑的问题,后来研究明白了nextTick的用法。

我们先看两种情况:

第一种:

export default {
data () {
return {
msg: 0
}
},
mounted () {
this.msg = 1
this.msg = 2
this.msg = 3
},
watch: {
msg () {
console.log(this.msg)
}
}
}

这段脚本执行我们猜测会依次打印:1、2、3。但是实际效果中,只会输出一次:3。为什么会出现这样的情况?

原因:

当触发update更新的时候,会去执行queueWatcher方法,也就是说,下一个循环开始时调用,此时msg已经变成3了。

保证更新视图操作DOM的动作是在当前栈执行完以后下一个Tick(或者是当前Tick的微任务阶段)的时候调用,大大优化了性能。

第二种情况:

<body>
<div id="main">
<ul class="list">
<li class="item" v-for="item in list">{{ item }}</li>
</ul>
</div> <script>
new Vue({
el: '#main',
data: {
list: [
'AAAAAAAAAA',
'BBBBBBBBBB',
'CCCCCCCCCC'
]
},
mounted: function () {
this.list.push('DDDDD')
}
})
</script>
</body>

随便给了点样式之后,页面是这样的:

看起来似乎一切正常,我们在给数组添加了一条数据之后,页面也确实对应的更新了。可是,当我们在打印这个 ul 元素里 li 的 length 时,问题出现了:

    mounted: function () {
this.list.push('DDDDD')
console.log(this.$el.querySelectorAll('.item').length) //
}

这时候如果我们有需求需要通过 li 的个数来计算出 ul 容器的高度来进行布局,显然就有问题了。

而这时候 Vue 的 nextTick 就可以帮助我们解决这个问题:

    mounted: function () {
this.list.push('DDDDD')
Vue.nextTick(function() {
console.log(this.$el.querySelectorAll('.item').length) //
// ... 计算
})

关于 Vue 的异步更新队列,官网是这么说的:
当你设置 vm.someData = 'new value' ,该组件不会立即重新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新。多数情况我们不需要关心这个过程,但是如果你想在 DOM 状态更新后做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们确实要这么做。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。
 
简单说,因为 DOM 至少会在当前线程里面的代码全部执行完毕再更新。所以不可能做到在修改数据后并且 DOM 更新后再执行,要保证在 DOM 更新以后再执行某一块代码,就必须把这块代码放到下一次事件循环里面,比如 setTimeout(fn, 0),这样 DOM 更新后,就会立即执行这块代码。

js 是单线程语言

我们都知道,js 执行的所有任务都需要排队,一个任务必须要等它前面的一个任务执行完之后才能执行。如果前一个任务需要花费大量的时间来计算,那么后一个任务就必须一直等它执行完才会轮到它执行,这就是单线程的特性。 而 js 的任务分为两种,同步任务和异步任务:

  • 同步任务就是按照顺序一个一个的执行任务,后一个任务要执行必须等它前一个任务完成
  • 异步任务(比如回调)不会占用主线程,会被塞到一个任务队列,等主线程的任务执行完毕,就会把这个异步任务队列里的任务放回主线程依次执行

用一个丑但易懂的图来表示:

所以结果输出是这样就很好理解了:

Event Loop(事件循环)

被称作事件循环的原因在于,同步的任务可能会生成新的任务,因此它一直在不停的查找新的事件并执行。一次循环的执行称之为 tick,在这个循环里执行的代码被称作 task,而整个过程是不断重复的。

console.log(1);

setTimeout(()=>{
console.log(2);
},1000); while (true){}

上面代码在输出 1 之后(谨慎使用!我的浏览器就被卡死了~),定时器被塞到任务队列里,然后主线程继续往下执行,碰到一个死循环,导致任务队列里的任务永远不会被执行,因此不会输出 2

事件队列

除了我们的主线程之外,任务队列分为 microtaskmacrotask,通常我们会称之为微任务和宏任务。 microtask 这一名词在js中是个比较新的概念,我们通常是在学习 ES6 的 Promise 时才初次接触到。

  • 执行优先级上,主线程任务 > microtask > macrotask。
  • 典型的 macrotask 有 setTimeout 和 setInterval,以及只有 IE 支持的 setImmediate,还有 MessageChannel等,ES6的 Promise 则是属于 microtask
console.log(1)

setTimeout(function(){
console.log(2)
}) Promise.resolve().then(function(){
console.log('promise1')
}).then(function(){
console.log('promise2')
}) console.log(4)

根据执行顺序,上面代码的输出结果很容易就能得出了:

nextTick

让我们回到上面的主题,Vue 的 nextTick方法,

源码 不难发现,Vue 在内部尝试对异步队列使用原生的setImmediateMessageChannel和 Promise.then

如果当前执行环境不支持,就采用setTimeout(fn, 0)代替。

 

什么时候需要用Vue.nextTick():

  • 你在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中。原因是什么呢,原因是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题 。
  • 在数据变化后要执行的某个操作,当你设置 vm.someData = 'new value'DOM并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM更新。如果此时你想要根据更新的 DOM 状态去做某些事情,就会出现问题。。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。
  • mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted
mounted: function () {
this.$nextTick(function () { // Code that will run only after the
// entire view has been rendered
})
}

Vue的nextTick是什么?的更多相关文章

  1. vue中nextTick

    vue中nextTick可以拿到更新后的DOM元素 如果在mounted下不能准确拿到DOM元素,可以使用nextTick 在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue ...

  2. vue的nextTick的实现

    vue的nextTick是用浏览器支持的方法模拟nodejs的process.nextTick 老版本的vue用如下方法来模拟 Promise.thenMutationObserver(Mutatio ...

  3. Vue中nextTick()解析

    最近,在开发的时候遇到一个问题,让我对vue中nextTick()的用法加深了了解- 下面是在组件中引用的一个拖拽的组件: <vue-draggable-resizable class=&quo ...

  4. 【vue】nextTick源码解析

    1.整体入手 阅读代码和画画是一样的,忌讳一开始就从细节下手(比如一行一行读),我们先将细节代码折叠起来,整体观察nextTick源码的几大块. 折叠后代码如下图 整体观察代码结构 上图中,可以看到: ...

  5. Vue中$nextTick的理解

    Vue中$nextTick的理解 Vue中$nextTick方法将回调延迟到下次DOM更新循环之后执行,也就是在下次DOM更新循环结束之后执行延迟回调,在修改数据之后立即使用这个方法,能够获取更新后的 ...

  6. 基于源码分析Vue的nextTick

    摘要:本文通过结合官方文档.源码和其他文章整理后,对Vue的nextTick做深入解析.理解本文最好有浏览器事件循环的基础,建议先阅读上文<事件循环Event loop到底是什么>. 一. ...

  7. vue之nextTick全面解析

    vue的第一篇文章,介绍一下简单的nextTick方法的实现原理 简介 vue是非常流行的框架,他结合了angular和react的优点,从而形成了一个轻量级的易上手的具有双向数据绑定特性的mvvm框 ...

  8. vue中nextTick和$nextTick的差别

    <ul id="demo">     <li v-for="item in list">{{item}}</div> < ...

  9. Vue.js $nextTick

    最近在学习vue.js.了解1.x的基础上再学习2.x的vue.两个版本的确是不会像angular这样1.x和2.x相差甚远.所以学习起来其实还是有很大的关联.但是,终归来说.两者还是有语法上的细微差 ...

随机推荐

  1. Java-GC 垃圾收集算法

    程序计数器.虚拟机栈.本地方法栈随线程而生,随线程而灭. 栈帧随着方法的开始而入栈,随着方法的结束而出栈. 这几个区域的内存分配和回收都具有确定性,在这几个区域内不需要过多考虑回收的问题,因为方法结束 ...

  2. 内置对象(Date String Math Array)

    什么是对象 JavaScript 中的所有事物都是对象,如:字符串.数值.数组.函数等,每个对象带有属性和方法. 对象的属性:反映该对象某些特定的性质的,如:字符串的长度.图像的长宽等: 对象的方法: ...

  3. Eclipse IDE for C/C++ Developers和MinGW安装配置C/C++开发学习环境详解

    Eclipse IDE for C/C++ Developers和MinGW安装配置C/C++开发学习环境详解 操作系统:Windows 7 JDK版本:1.6.0_33 Eclipse版本:Juno ...

  4. LC 676. Implement Magic Dictionary

    Implement a magic directory with buildDict, and search methods. For the method buildDict, you'll be ...

  5. 算法试题 - 找出最小 k 个数

    题目 题目:输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. 解析 思路1 这一题应用堆排序算法复杂度只有O(nlog k), ...

  6. 史上最全SVN 教程

    以下博文引用<https://blog.csdn.net/u013067756/article/details/73302758>,再此仅供学习和参考. Svn是什么? SVN(全称Sub ...

  7. linux常用命令(16)locate命令

    locate 让使用者可以很快速的搜寻档案系统内是否有指定的档案.其方法是先建立一个包括系统内所有档案名称及路径的数据库,之后当寻找时就只需查询这个数据库,而不必实际深入档案系统之中了.在一般的 di ...

  8. split切割.号的字符串

    excel中的日期为下图所示,利用io读取到后,调试发现值为“12.10.2019”,需要将其转换为“2019-10-12” 用split方法以.号切割时,需要用转移字符“\\.”,代码如下 pack ...

  9. mybatis-generator自动生成代码时,只生成insert方法

    今天使用mybatis-generator自动生成代码时,发现只能生成insert方法, 以前所有的方法都是可以生成的,查看网上解决办法和检查数据库表结构后, 发现2种可以解决的办法: 1.修改myb ...

  10. IntelliJ IDEA 2018 for Mac使用技巧

    IntelliJ IDEA 2018 for Mac是一个综合性的Java编程环境,被许多开发人员和行业专家誉为市场上最好的IDE,它提供了一系列最实用的的工具组合:智能编码辅助和自动控制,支持J2E ...