vue异步更新dom的实现浅析

目录
  • Vue异步更新DOM的原理
    • 1 什么时候能获取到真正的DOM元素?
    • 2 为什么Vue需要通过nextTick方法才能获取最新的DOM?
    • 3 为什么this.$nextTick 能够获取更新后的DOM?
  • 总结:vue异步更新的原理

Data对象:vue中的data方法中返回的对象;

Dep对象:每一个Data属性都会创建一个Dep,用来搜集所有使用到这个Data的Watcher对象;

Watcher对象:主要用于渲染DOM

Vue异步更新DOM的原理

Vue中的数据更新是异步的,意味着我们在修改完Data之后并不能立刻获取修改后的DOM元素。

1 什么时候能获取到真正的DOM元素?

  在Vue的nextTick回调中。

2 为什么Vue需要通过nextTick方法才能获取最新的DOM?

2.1 vue在调用Watcher更新视图时,并不会直接进行更新,而是把需要更新的Watcher加入到Queue队列里,然后把具体的更新方法flushSchedulerQueue 传给nexTick 进行调用。

// src > core > observer > watcher.js + scheduler.js// 当一个 Data 更新时,会依次执行以下代码
// 1. 触发 Data.set
// 2. 调用 dep.notify
// 3. Dep 会遍历所有相关的 Watcher 执行 update 方法
class Watcher {
  // 4. 执行更新操作
  update() {
    queueWatcher(this);
  }
}

const queue = [];

function queueWatcher(watcher: Watcher) {
  // 5. 将当前 Watcher 添加到异步队列
  queue.push(watcher);
  // 6. 执行异步队列,并传入回调
  nextTick(flushSchedulerQueue);
}

// 更新视图的具体方法
function flushSchedulerQueue() {
  let watcher, id;
  // 排序,先渲染父节点,再渲染子节点
  // 这样可以避免不必要的子节点渲染,如:父节点中 v-if 为 false 的子节点,就不用渲染了
  queue.sort((a, b) => a.id - b.id);
  // 遍历所有 Watcher 进行批量更新。
  for (index = 0; index < queue.length; index++) {
    watcher = queue[index];
    // 更新 DOM
    watcher.run();
  }
}

2.2 nextTick -- 将传入的flushSchedulerQueue 添加到callbacks 数组中,然后执行了timerFunc 方法。

const callbacks = [];
let timerFunc;

function nextTick(cb?: Function, ctx?: Object) {
  let _resolve;
  // 1.将传入的 flushSchedulerQueue 方法添加到回调数组
  callbacks.push(() => {
    cb.call(ctx);
  });
  // 2.执行异步任务
  // 此方法会根据浏览器兼容性,选用不同的异步策略
  timerFunc();
}

2.3 timerFunc方法 -- 是根据浏览器兼容性创建的一个异步方法,执行完该方法就会调用flushSchedulerQueue 方法进行具体的DOM更新。

let timerFunc;
// 判断是否兼容 Promise
if (typeof Promise !== "undefined") {
  timerFunc = () => {
    Promise.resolve().then(flushCallbacks);
  };
  // 判断是否兼容 MutationObserver
  // https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
} else if (typeof MutationObserver !== "undefined") {
  let counter = 1;
  const observer = new MutationObserver(flushCallbacks);
  const textNode = document.createTextNode(String(counter));
  observer.observe(textNode, {
    characterData: true,
  });
  timerFunc = () => {
    counter = (counter + 1) % 2;
    textNode.data = String(counter);
  };
  // 判断是否兼容 setImmediate
  // 该方法存在一些 IE 浏览器中
} else if (typeof setImmediate !== "undefined") {
  // 这是一个宏任务,但相比 setTimeout 要更好
  timerFunc = () => {
    setImmediate(flushCallbacks);
  };
} else {
  // 如果以上方法都不知道,使用 setTimeout 0
  timerFunc = () => {
    setTimeout(flushCallbacks, 0);
  };
}

// 异步执行完后,执行所有的回调方法,也就是执行 flushSchedulerQueue
function flushCallbacks() {
  for (let i = 0; i < copies.length; i++) {
    callbacks[i]();
  }
}

2.4 完善逻辑判断

2.4.1 判断has 标识,避免在一个Queue 中添加相同的Watcher;

2.4.2 判断waiting 标识,让所有的Watcher 都在一个tick 内进行更新;

2.4.3 判断flushing 标识,处理Watcher 渲染时,可能产生的新Watcher。

  如触发了v-if 条件,新增的Watcher 渲染。

tip:nextTick 只是单纯通过Promise、setTimeout等方法模拟的异步任务。

3 为什么this.$nextTick 能够获取更新后的DOM?

  调用this.$nextTick 其实就是调用图中的nextTick 方法,在异步队列中执行回调函数。根据先进先出原则,修改Data 触发的更新异步队列会先得到执行,执行完成后就生成了新的DOM,接下来执行this.$nextTick 的回调函数时,能获取到更新后的DOM元素了。

// 我们使用 this.$nextTick 其实就是调用 nextTick 方法
Vue.prototype.$nextTick = function (fn: Function) {
  return nextTick(fn, this);
};

总结:vue异步更新的原理

修改 Vue 中的 Data 时,就会触发所有和这个 Data 相关的 Watcher 进行更新。首先,会将所有的 Watcher 加入队列 Queue。然后,调用 nextTick 方法,执行异步任务。在异步任务的回调中,对 Queue 中的 Watcher 进行排序,然后执行对应的 DOM 更新。

到此这篇关于vue异步更新dom的实现浅析的文章就介绍到这了,更多相关vue异步更新dom内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Vue批量更新dom的实现步骤

    目录 场景介绍 深入响应式 触发getter 寻找Dep.target getter setter 总结 场景介绍 在一个SFC(single file component,单文件组件)中,我们经常会写这样的逻辑: <template> <div> <span>{{ a }}</span> <span>{{ b }}</span> </div> </template> <script type="

  • 详解从Vue.js源码看异步更新DOM策略及nextTick

    写在前面 因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出. 文章的原地址:https://github.com/answershuto/learnVue. 在学习过程中,为Vue加上了中文的注释https://github.com/answershuto/learnVue/tree/master/vue-src,希望可以对其他想学习Vue源码的小伙伴有所帮助. 可能会有理解存在偏差的地方,欢迎提issue指出,

  • VUE异步更新DOM - 用$nextTick解决DOM视图的问题

    VUE异步更新DOM 首先,Vue 在更新 DOM 时是异步执行的! 所以只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更.如果同一个 watcher 被多次触发,只会被推入到队列中一次.这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的.然后,在下一个的事件循环"tick"中,Vue 刷新队列并执行实际 (已去重的) 工作.Vue 在内部对异步队列尝试使用原生的 Promise.then.MutationObserver 和

  • vue异步更新dom的实现浅析

    目录 Vue异步更新DOM的原理 1 什么时候能获取到真正的DOM元素? 2 为什么Vue需要通过nextTick方法才能获取最新的DOM? 3 为什么this.$nextTick 能够获取更新后的DOM? 总结:vue异步更新的原理 Data对象:vue中的data方法中返回的对象: Dep对象:每一个Data属性都会创建一个Dep,用来搜集所有使用到这个Data的Watcher对象: Watcher对象:主要用于渲染DOM Vue异步更新DOM的原理 Vue中的数据更新是异步的,意味着我们在

  • Vue异步更新机制及$nextTick原理的深入讲解

    目录 前言 Vue的异步更新 DOM更新是异步的 DOM更新还是批量的 事件循环 执行过程 源码深入 异步更新队列 nextTick $nextTick 总结 一般更新DOM是同步的 既然更新DOM是个同步的过程,那为什么Vue却需要借用$nextTick来处理呢? 为什么优先使用微任务? 总结 前言 相信很多人会好奇Vue内部的更新机制,或者平时工作中遇到的一些奇怪的问题需要使用$nextTick来解决,今天我们就来聊一聊Vue中的异步更新机制以及$nextTick原理 Vue的异步更新 可能

  • 详解Vue的异步更新实现原理

    最近面试总是会被问到这么一个问题:在使用vue的时候,将for循环中声明的变量i从1增加到100,然后将i展示到页面上,页面上的i是从1跳到100,还是会怎样?答案当然是只会显示100,并不会有跳转的过程. 怎么可以让页面上有从1到100显示的过程呢,就是用setTimeout或者Promise.then等方法去模拟. 讲道理,如果不在vue里,单独运行这段程序的话,输出一定是从1到100,但是为什么在vue中就不一样了呢? for(let i=1; i<=100; i++){ console.

  • vue源码之批量异步更新策略的深入解析

    vue异步更新源码中会有涉及事件循环.宏任务.微任务的概念,所以先了解一下这几个概念. 一.事件循环.宏任务.微任务 1.事件循环Event Loop:浏览器为了协调事件处理.脚本执行.网络请求和渲染等任务而定制的工作机制. 2.宏任务Task: 代表一个个离散的.独立的工作单位.浏览器完成一个宏任务,在下一个宏任务开始执行之前,会对页面重新渲染.主要包括创建文档对象.解析HTML.执行主线JS代码以及各种事件如页面加载.输入.网络事件和定时器等. 3.微任务:微任务是更小的任务,是在当前宏任务

  • vue的状态更新方式(异步更新解决)

    目录 状态更新(异步更新解决) 解决方案 异步更新及nexttick 为什么需要异步更新 nextTick 原理 状态更新(异步更新解决) 在vue中状态更新是异步的,这一点和react中的setstate类似. 解决方案 非组件解决方案: <div id="example">{{message}}</div> var vm = new Vue({   el: '#example',   data: {     message: '123'   } }) vm.

  • vue中的任务队列和异步更新策略(任务队列,微任务,宏任务)

    目录 事件循环 任务队列 如何理解微任务和宏任务? 深究Vue异步更新策略原理 事件循环 JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事. 为了协调事件.用户交互.脚本.UI 渲染和网络处理等行为,防止主线程的不阻塞,Event Loop 的方案应用而生. Event Loop 包含两类: 一类是基于 Browsing Context 一种是基于 Worker 二者的运行是独立的,也就是说,每一个 JavaScript 运行的"线程环境"都有一个独立的

  • 关于Vue.nextTick()的正确使用方法浅析

    本文主要给大家介绍了关于Vue.nextTick()的正确使用,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 什么是Vue.nextTick() 官方文档解释如下: 在下次 DOM 更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法,获取更新后的 DOM. 我理解的官方文档的这句话的侧重点在最后那半句获取更新后的DOM,获取更新后的DOM言外之意就是什么操作需要用到了更新后的DOM而不能使用之前的DOM或者使用更新前的DOM或出问题,所以就衍生出了这个获取更新后的D

随机推荐