导航

[[深刻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 一个对象
    • 返回值

      • <font color=red>如果没有传入第一个参数函数,并且环境反对promise,则返回一个promise实例对象</font>
      • Vue.nextTick().then() - 因为返回promise所以能够持续调用 .then() 办法
    • 作用

      • 在下次DOM更新循环完结后执行延时回调,在批改数据后立刻执行该办法,获取更新后的DOM
      • 解析:<font color=red>当数据批改后,DOM不是立刻更新的,而是在下一个 TICK 中去更新,即利用异步工作队列的执行机会去实现,依据具体的环境应用微工作或者红工作队列去更新DOM </font>
    • 需要

      • 要在数据更后,立刻获取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 awaitthis.message = "更新了";console.log(this.$refs.messageDom.innerHTML, "DOM - 未用nextTick"); // 未更新await this.$nextTick();console.log(this.$refs.messageDom.innerHTML, "DOM - 用了nextTick"); // 更新了}---- 残缺代码<template><div class="learn-source-code-vue-nextTick">  learn-source-code-vue-nextTick 组件  <div ref="messageDom">message: {{this.message}}</div>  <button @click="updateMessage">更新message</button></div></template><script>export default {name: "LearnSourceCodeVueNextTick",data() {  return {    message: "未更新"  };},methods: {  updateMessage() {    // this.message = "更新了";    // console.log(this.$refs.messageDom.innerHTML, "DOM - 未用nextTick");    // 写法一    // this.$nextTick(() => {    //   console.log(this.$refs.messageDom.innerHTML, "DOM - 用了nextTick");    // });    // 写法二    // this.$nextTick().then(() => {    //   console.log(this.$refs.messageDom.innerHTML, "DOM - 用了nextTick");    // });        this.updateMessage2();  },  async updateMessage2() {    this.message = "更新了";    console.log(this.$refs.messageDom.innerHTML, "DOM - 未用nextTick");    await this.$nextTick();    console.log(this.$refs.messageDom.innerHTML, "DOM - 用了nextTick");  }}};</script>

(3) 宏工作和微工作

  • 微工作

    • promise.then
    • MutationObserver
    • process.nextTick - (Node.js 环境)
  • 宏工作

    • setTimeout
    • setInterval
    • setImmediate - (Node.js 环境)

    (4) MutationObserver

  • 作用

    • 监督DOM的变动,DOM的任何变动,该api都会失去告诉
    • 能够了解为 DOM 产生变动就会触发 Mutation Observer 事件
    • 比方:节点的增减,属性的变动,文本内容的变动通过MutationObserver都会失去告诉
  • 特点

    • 它是 ( 异步触发 ),DOM 的变动并不会马上触发,而是要等到以后所有 DOM 操作都完结才触发
    • 它期待所有脚本工作实现后,才会运行 ( 即异步触发形式 )
    • 它把 DOM 变动记录封装成一个 ( 数组 ) 进行解决,而不是一条条个别解决 DOM 变动
    • 它既能够察看 DOM 的所有类型变动,也能够指定只察看某一类变动
  • 应用实例

    • <font color=red>const obsersver = new MutationObserver(cb) </font>

      • 生成 obsersver 实例
      • 当察看的DOM变动时,触发cb回调
    • <font color=red>obsersver.observe(rootDom, {...}) </font>

      • 察看rootDom节点的dom变动
      • 第二个参数是 配置对象

        • <font color=red>childList</font>:子节点的变动(指新增,删除或者更改)
        • <font color=red>attributes</font>:属性的变动
        • <font color=red>characterData</font>:节点内容或节点文本的变动
        • <font color=red>subtree</font>:布尔值,示意是否将该观察器利用于该节点的所有后辈节点
        • <font color=red>attributeOldValue</font>:布尔值,示意察看attributes变动时,是否须要记录变动前的属性值
        • <font color=red>characterDataOldValue</font>:布尔值,示意察看characterData变动时,是否须要记录变动前的值
        • <font color=red>attributeFilter</font>:数组,示意须要察看的特定属性(比方['class','src'])
    <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><div id="root">root</div><div id="root-sibling">sibling-root</div><div id="button-dom">点击扭转root-DOM</div><div id="button-sibling-dom">点击扭转siblingRoot-DOM</div><script>  const rootDom = document.getElementById('root')  const siblingRootDom = document.getElementById('root-sibling')  const cb = (mutations, observer) => {    console.log(mutations, 'mutations')    console.log(observer, 'observer')    console.log('new MutationObserver(cb)的cb回调触发')    console.log('DOM变动了') // --------------------------------- 2后触发  }  const obsersver = new MutationObserver(cb)   // MutationObserver构造函数,cb是DOM的变动时会触发的回调函数  obsersver.observe(rootDom, {    // observe 是 obsersver实例对象的一个办法    // observe      // 第一个参数:示意须要察看变动的 DOM      // 第二个参数:配置对象    // 这里示意:察看root的变动,同级的其余dom不察看    childList: true, // 察看子节点变动    attributes: true, // 察看属性的变动    attributeOldValue: true, // 察看attributes变动时,是否须要记录变动前的属性值    attributeFilter: ['class','src'], // 示意须要察看的特定属性    characterData: true, // 节点内容或节点文本的变动    characterDataOldValue: true, // 察看characterData变动时,是否须要记录变动前的值    subtree: true, // 是否将该观察器利用于该节点的所有后辈节点  })  document.getElementById('button-dom').addEventListener('click', () => {    rootDom.innerHTML = 'new root'    console.log('click事件触发') // --------------------------------- 1先触发  })  document.getElementById('button-sibling-dom').addEventListener('click', () => {    siblingRootDom.innerHTML = 'new sibling-root'    console.log('click事件触发')  })  // 这里扭转 siblingRootDom 不会触发 new MutationObserver(cb) 的 cb回调,因为 obsersver.observe() 察看的dom是root</script></body></html>

Vue.nextTick 源码

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 数组,赋值给 copies  callbacks.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 = 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 _resolve  callbacks.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=false        timerFunc()    // pending=false 就执行 timerFunc  }  // $flow-disable-line  if (!cb && typeof Promise !== 'undefined') {    // 如果 Vue.nextTick 没有提供回调函数,并且promise存在,就把 _resolve = resolve    // 并且整个 nextTick 函数返回这个 promise 实例    return new Promise(resolve => {      _resolve = resolve    })  }}