ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[toc] ### 1.$nextTick使用场景 在vue中,如果改变了dom内部绑定的值,立即获取dom的innerHTML,会出现拿到的还是原来的值,这个时候需要用this.$nextTick方法执行回调函数 ```js <div id="app">{{a}}</div> let vm = new Vue({ el: '#app', data: { a: 1 }, mounted() { this.a = 2 console.log('dom节点内容:', app.innerHTML) // 打印出来的还是1 } }) ``` ```js mounted() { this.a = 2 this.$nextTick(() => { console.log('dom节点内容:', app.innerHTML) // 这样打印出来的就是2 }) } ``` ### 2.原理 $nextTick当中的操作不会立即执行,而是会等数据、和dom更新之后再执行。 如何实现? 1.$nextTick内部的方法,会被放到一个callbacks数组中 2.当vue中的数据发生更改时,会触发set方法,set会调用dep上的notify方法,通知watcher执行update方法 3.update方法会触发queueWatcher方法,queueWatcher会调用nextTick,并添加一个flushSchedulerQueue方法 4.最后会依次执行callbacks数组中存入的nextTick回调方法,但是这个方法会被promise.then或者Mutation.observer包裹起来,或者setTimeout(根据浏览器是否支持判断),实现了一个异步的调用 ```js update () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true } else if (this.sync) { this.run() } else { queueWatcher(this) // 异步更新逻辑 } } ``` ```js // queueWatcher nextTick(flushSchedulerQueue) ``` ```js function flushCallbacks () { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } } ``` ----- 把回调函数放入callbacks等待执行 将执行函数放到微任务或者宏任务中 事件循环到了微任务或者宏任务,执行函数依次执行callbacks中的回调 但是nextTick是同步执行的,同步的把里面的值放到异步队列里了 ### 3.watcher去重 ```js this.a = 100 this.a = 200 //代码执行了两次,但是视图只更新了一次 ``` 因为vue的watcher会去重。 数据发生变化之后,vue的dom并不一定马上发生变化,vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变,如果同一个watcher被多次触发,只会被推入到队列一次。 ```js addDep (dep: Dep) { const id = dep.id if (!this.newDepIds.has(id)) { this.newDepIds.add(id) this.newDeps.push(dep) if (!this.depIds.has(id)) { dep.addSub(this) } } } ``` 原理:添加dep的时候,会看这个dep的id,如果已经有了,那么就不会往里面push了