[vue源码06]|[vue源码06] - Vue.nextTick 和 VM.$nextTick
导航
[[深入01] 执行上下文](https://juejin.im/post/684490...)
[[深入02] 原型链](https://juejin.im/post/684490...)
[[深入03] 继承](https://juejin.im/post/684490...)
[[深入04] 事件循环](https://juejin.im/post/684490...)
[[深入05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490...)
[[深入06] 隐式转换 和 运算符](https://juejin.im/post/684490...)
[[深入07] 浏览器缓存机制(http缓存机制)](https://juejin.im/post/684490...)
[[深入08] 前端安全](https://juejin.im/post/684490...)
[[深入09] 深浅拷贝](https://juejin.im/post/684490...)
[[深入10] Debounce Throttle](https://juejin.im/post/684490...)
[[深入11] 前端路由](https://juejin.im/post/684490...)
[[深入12] 前端模块化](https://juejin.im/post/684490...)
[[深入13] 观察者模式 发布订阅模式 双向数据绑定](https://juejin.im/post/684490...)
[[深入14] canvas](https://juejin.im/post/684490...)
[[深入15] webSocket](https://juejin.im/post/684490...)
[[深入16] webpack](https://juejin.im/post/684490...)
[[深入17] http 和 https](https://juejin.im/post/684490...)
[[深入18] CSS-interview](https://juejin.im/post/684490...)
[[深入19] 手写Promise](https://juejin.im/post/684490...)
[[深入20] 手写函数](https://juejin.im/post/684490...)
[[react] Hooks](https://juejin.im/post/684490...)
[[部署01] Nginx](https://juejin.im/post/684490...)
[[部署02] Docker 部署vue项目](https://juejin.im/post/684490...)
[[部署03] gitlab-CI](https://juejin.im/post/684490...)
[[源码-webpack01-前置知识] AST抽象语法树](https://juejin.im/post/684490...)
[[源码-webpack02-前置知识] Tapable](https://juejin.im/post/684490...)
[[源码-webpack03] 手写webpack - compiler简单编译流程](https://juejin.im/post/684490...)
[[源码] Redux React-Redux01](https://juejin.im/post/684490...)
[[源码] axios ](https://juejin.im/post/684490...)
[[源码] vuex ](https://juejin.im/post/684490...)
[[源码-vue01] data响应式 和 初始化渲染 ](https://juejin.im/post/684490...)
[[源码-vue02] computed 响应式 - 初始化,访问,更新过程 ](https://juejin.im/post/684490...)
[[源码-vue03] watch 侦听属性 - 初始化和更新 ](https://juejin.im/post/684490...)
[[源码-vue04] Vue.set 和 vm.$set ](https://juejin.im/post/684490...)
[[源码-vue05] Vue.extend ](https://juejin.im/post/684490...)
[[源码-vue06] Vue.nextTick 和 vm.$nextTick ](https://juejin.im/post/684790...)
前置知识
(1) 一些单词
(2) Vue.nextTick() - api使用
- Vue.nextTick([callback, context] )
- 参数
- callback 一个函数
- context 一个对象
- 返回值
- 如果没有传入第一个参数函数,并且环境支持promise,则返回一个promise实例对象
Vue.nextTick().then()
- 因为返回promise所以可以继续调用 .then() 方法
- 作用
- 在下次DOM更新循环结束后执行延时回调,在修改数据后立即执行该方法,获取更新后的DOM
- 解析:当数据修改后,DOM不是立即更新的,而是在下一个 TICK 中去更新,即利用异步任务队列的执行时机去实现,根据具体的环境使用微任务或者红任务队列去更新DOM
- 需求
- 要在数据更后,立即获取DOM,而此时DOM并没有更新,需要拿到最新的DOM怎么办?
- 就可以使用 Vue.nextTick() 或者 VM.$nextTick() 在更新数据后获取更新后的DOM
- 官网链接 https://cn.vuejs.org/v2/api/#...
- 实例
// 修改数据 vm.msg = 'Hello' // DOM 还没有更新 Vue.nextTick(function () {// ----------------------------------------------- 使用方法1 - callback // DOM 更新了,可以获取更新后的dom // 比如: // console.log(this.$refs.messageDom.innerHTML, "DOM - 用了nextTick"); })// 作为一个 Promise 使用 (2.1.0 起新增,详见接下来的提示) Vue.nextTick() .then(function () { // ----------------------------------------------------- 使用方法2 - promise // DOM 更新了 })async updateMessage2() { // -------------------------------------------------- 使用方法3 - async await this.message = "更新了"; console.log(this.$refs.messageDom.innerHTML, "DOM - 未用nextTick"); // 未更新 await this.$nextTick(); console.log(this.$refs.messageDom.innerHTML, "DOM - 用了nextTick"); // 更新了 }---- 完整代码 learn-source-code-vue-nextTick 组件 message: {{this.message}}
- 参数
- 微任务
- promise.then
- MutationObserver
- process.nextTick - (Node.js 环境)
- 宏任务
- setTimeout
- setInterval
- setImmediate - (Node.js 环境)
- 作用
- 监视DOM的变化,DOM的任何变动,该api都会得到通知
- 可以理解为 DOM 发生变动就会触发 Mutation Observer 事件
- 比如:节点的增减,属性的变动,文本内容的变动通过MutationObserver都会得到通知
- 特点
- 它是 ( 异步触发 ),DOM 的变动并不会马上触发,而是要等到当前所有 DOM 操作都结束才触发
- 它等待所有脚本任务完成后,才会运行 ( 即异步触发方式 )
- 它把 DOM 变动记录封装成一个 ( 数组 ) 进行处理,而不是一条条个别处理 DOM 变动
- 它既可以观察 DOM 的所有类型变动,也可以指定只观察某一类变动
- 使用实例
- const obsersver = new MutationObserver(cb)
- 生成 obsersver 实例
- 当观察的DOM变化时,触发cb回调
- obsersver.observe(rootDom, {...})
- 观察rootDom节点的dom变化
- 【[vue源码06]|[vue源码06] - Vue.nextTick 和 VM.$nextTick】第二个参数是 配置对象
- childList:子节点的变动(指新增,删除或者更改)
- attributes:属性的变动
- characterData:节点内容或节点文本的变动
- subtree:布尔值,表示是否将该观察器应用于该节点的所有后代节点
- attributeOldValue:布尔值,表示观察attributes变动时,是否需要记录变动前的属性值
- characterDataOldValue:布尔值,表示观察characterData变动时,是否需要记录变动前的值
- attributeFilter:数组,表示需要观察的特定属性(比如['class','src'])
Document - 锐客网 root sibling-root点击改变root-DOM 点击改变siblingRoot-DOM
- const obsersver = new MutationObserver(cb)
import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'export let isUsingMicroTask = false
// 标志位,是否使用 微任务队列const callbacks = []
// callbacks 搜集 包装过后的传入 Vue.nextTick 的
// 是这样的一个数组
// 1. Vue.nextTick 中的第一个参数cb存在--------------------> [() => {cb.call(ctx)}]
// 2. Vue.nextTick 中的第一个参数cb不存在,但支持promise -----> [() => {_resolve(ctx)}]let pending = false
// pending
// 标志位,同一时间只能执行 timerFunc 函数一次function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
// 浅拷贝 callbacks 数组,赋值给 copiescallbacks.length = 0
// 拷贝后,将原来的 callbacks 数组清空for (let i = 0;
i < copies.length;
i++) {
copies[i]()
// 遍历并调用 copies 数组中的 ( 成员函数 )
}
}let timerFuncif (typeof Promise !== 'undefined' && isNative(Promise)) {
// ---------------------------------------------- 如果 prommise 并且 原生就支持promise
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks)
if (isIOS) setTimeout(noop)
// noop 是空函数
}
isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
// ---------------------------------------------- 不支持 promise 就使用 MutationObserver
// ---------------------------------------------- 如果不是ie环境,并且 MutationObserver构造函数存在,原生支持,类型是MutationObserverConstructor
let counter = 1
const observer = new MutationObserver(flushCallbacks)
// 当DOM变化时,触发 flushCallbacks
const textNode = document.createTextNode(String(counter)) // 生成文本节点
observer.observe(textNode, {
// 观察 textNode 的变化,当节点内容或节点文本的变动时触发 flushCallbacks
characterData: true, //节点内容或节点文本的变动
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = https://www.it610.com/article/String(counter)
}
isUsingMicroTask = true
} else if (typeof setImmediate !=='undefined' && isNative(setImmediate)) {
// --------------------------------------------------- 不支持 微任务:promise
// ---------------------------------------------- 降级:不支持 微任务:MutationObserver
// ---------------------------------------------- 降级:宏任务:setImmediate
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
// Fallback to setTimeout.
// ---------------------------------------------- 以上都不支持,降级到宏任务 setTimeout
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}export function nextTick (cb?: Function, ctx?: Object) {
// nextTick
// 参数
// cb:DOM改变触发的回调
// ctx:上下文环境对象,即this的指向
let _resolvecallbacks.push(() => {
if (cb) {
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')
}
// try catch 是为了保证 callbacks数组中一个函数出现错误,不会终止整个代码的执行
} else if (_resolve) {
_resolve(ctx)// new Promise(resolve => {
//_resolve = resolve
// })}
})
// 向 callbacks 数组中push函数
// 1. cb存在,() => { cb.call(ctx) },只不过用 try catch 包裹了下,捕获错误
// 2. cb不存在,() => { _resolve(ctx) }if (!pending) {
pending = true
// 下一次在 timerFunc 没有执行完的时候,就不会再进入执行 timerFunc
// 在 timerFunc => flushCallbacks => 再把pending=falsetimerFunc()
// pending=false 就执行 timerFunc
}// $flow-disable-line
if (!cb && typeof Promise !== 'undefined') {
// 如果 Vue.nextTick 没有提供回调函数,并且promise存在,就把 _resolve = resolve
// 并且整个 nextTick 函数返回这个 promise 实例
return new Promise(resolve => {
_resolve = resolve
})
}
}
推荐阅读
- vue-cli|vue-cli 3.x vue.config.js 配置
- 2020-04-07vue中Axios的封装和API接口的管理
- Android事件传递源码分析
- VueX--VUE核心插件
- Quartz|Quartz 源码解析(四) —— QuartzScheduler和Listener事件监听
- [源码解析]|[源码解析] NVIDIA HugeCTR,GPU版本参数服务器---(3)
- ffmpeg源码分析01(结构体)
- Java程序员阅读源码的小技巧,原来大牛都是这样读的,赶紧看看!
- vue组件中为何data必须是一个函数()
- 用npm发布一个包的教程并编写一个vue的插件发布