异步更新队列

vue高效的秘诀是一套批量,异步的更新策略

概念解释

WechatIMG13.png

  • 事件循环机制Event Lop:浏览器为了协调事件处理,脚本执行,网络请求,和渲染等任务而制定的工作机制
  • 宏任务Task:代表一个个离散的,独立的工作单元。浏览器完成一个完成宏任务,在下一个宏任务执行开始之前,会对页面进行重新渲染。主要包括包括创建文档对象,解析html,执行主线js代码以及各种事件如页面加载,输入,网络事件,定时器等。
  • 微任务:微任务是更小的任务,是在当前宏任务执行后立即执行的任务,如果存在微任务,浏览器会清空微任务之后再进行重新渲染。微任务的例子有promise回调函数,DOM变化。

Vue中的具体实现
WechatIMG14.png

  • 异步:只要监听到数据变化,Vue开启一个队列,并缓冲同一个事件循环中发生的所有数据变更。
  • 批量:如果同一个watcher被多次触发,只会被推入到队列中一次,去重对于避免不必要的计算和dom操作都是非常重要的。然后在下一个事件循环tick中,vue刷新队列执行实际操作,
  • 异步Vue在内部对异步队列尝试使用原生的Promise.then(),MutationObserver,setImmediate,如果执行环境都不支持,则会采用setimeout

    1. <br />**update() core\observer\watcher.js **<br />dep.notify()之后watcher执行更新,执行入队操作 <br /> <br />**queueWatcher(watcher) core\observer\scheduler.js **<br />执行watcher入队操作<br /> <br />**nextTick(flushSchedulerQueue) core\util\next-tick.js **<br />nextTick按照特定异步策略执行队列操作 <br />

    虚拟dom

    虚拟dom(virtual dom)是对dom的js抽象表示,他们是js对象。能够描述dom结构和关系,应用的各种状态变化会作用于虚拟dom上,最终映射到dom
    WechatIMG15.png

    体验虚拟dom

    vue中虚拟dom基于snabbdom实现,安装snabbdom并体验。

  1. //导入patch的工厂init,h是产生vnode的工厂
  2. const {init ,h} = snabbdom
  3. //获取patch函数
  4. const patch = init([]);
  5. //上次vnode由patch返回
  6. let vnode
  7. //更新函数,将数据操作转化为dom操作,返回新vnode
  8. function update(){
  9. if(!vnode){
  10. //初始化 没有上次vnode则是第一次更新,传入宿主元素与vnode
  11. vnode = path(app,render());
  12. }else{
  13. //更新,传入新旧vnode进行对比
  14. vnode = patch(vnode,render());
  15. }
  16. }
  17. // 渲染函数
  18. function render(){
  19. return h('div',obj.foo);
  20. }

优点

  • 虚拟dom轻量,快速:当它们发生时,通过对比新旧虚拟dom可以得到最小的dom操作量 ,配合异步刷新策略减少刷新策略,提升性能
  • 跨平台:将虚拟dom更新转换为不同运行时特殊操作实现跨平台

必要性
mountComponent() core/instance/lifecycle.js
// 定义更新函数
const updateComponent = () => {
// 实际调用是在lifeCycleMixin中定义的_update和renderMixin中定义的_render
vm._update(vm._render(), hydrating) }

_render core/instance/render.js
生成虚拟dom

_update core/instance/lifecycle.js
update负责更新dom,转换vnode为dom

patch() platforms/web/runtime/index.js
patch是在平台特有代码中指定的

  1. Vue.prototype._patch_ = inBrowser ? patch : noop

patch获取

patch是createPatchFunction的返回值,传递nodeOps和modules是web平台特别实现

  1. export const patch:Function = createPatchFunction({nodeOps,modules})
  1. <br />**platforms\web\runtime\node-ops.js **<br />定义各种原生dom基础操作方法

platforms\web\runtime\modules\index.js
modules 定义了属性更新实现

watcher.run() => componentUpdate() => render() => update() => patch()

patch实现

patch core\vdom\patch.js
首先进行树级别比较,可能就三种情况:增删改

  • new Vnode不存在就删
  • old Vnode不存在就增
  • 都存在就执行diff,执行更新

WechatIMG16.png
patchVnode
比较两个VNode,包括三种类型操作:属性更新,文本更新,子节点更新
具体比较规则:

  1. 新老节点均有children节点,则对子节点进行diff操作,调用updateChildren
  2. 如果新节点有子节点而老节点没有子节点,先清空老节点的文本内容,然后为其新增子节点
  3. 当新节点没有子节点而老节点有子节点,则移除所有子节点
  4. 当新老节点都无子节点时,单纯的文本替换


  1. <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br /> <br />
  2. <br /> <br /> <br /> <br /> <br />
  3. <br /> <br /> <br /> <br />
  4. <br /> <br /> <br /> <br /> <br />