Vue源码解析——异步更新队列+虚拟dom

Vue源码解析——异步更新队列+虚拟dom

异步更新队列

Vue高效的秘诀是一套批量、异步的更新策略。
Vue源码解析——异步更新队列+虚拟dom_第1张图片
每一个组件对应一个watcher实例,在一个队列中插入watcher,再进行批量操作。

概念

Vue源码解析——异步更新队列+虚拟dom_第2张图片

  • 事件循环:浏览器的一套工作机制。浏览器为了协调事件处理、脚本执行、网络请求和渲染等任务。
  • 宏任务:一系列任务的集合,代表着一个个离散的、独立工作单元。浏览器完成一个宏任务,在下一个宏任务执行开始前,会对页面进行重新渲染。主要包括创建主文档对象、解析HTML、执行主线JS代码以及各种事件如页面加载、输入、网络事件和定时器等。
  • 微任务:代表更小的任务,是当前宏任务执行结束后立即执行的任务。如果存在微任务,浏览器会清空微任务之后再重新渲染。微任务的例子有 promise 回调函数、DOM发生变化等。
vue中的具体实现
  • 异步:只要侦听到数据变化,vue将开启一个队列,并缓冲在用一事件循环中发生所有的数据变更。
  • 批量:如果同一个watcher被多次触发,只会被推入队列中一次。去重对于避免不必要的计算和DOM操作是非常重要的。然后在下一个事件循环“tick”中,vue刷新队列执行实际操作。
  • 异步策略:vue在内部对异常队列尝试使用原声的promise.thenMutationobserverserImmediate,如果执行环境不支持,则会采用宏任务setTimeout(fn,0)代替。
代码部分

update() core\observer\watcher.js

dep.notify()之后watcher执行更新,执行入队操作

queueWatcher(watcher) core\observer\scheduler.js

执行watcher入队操作

nextTick(flushSchedulerQueue) core\util\next-tick.js

nextTick按照特定异步策略执行队列操作

虚拟DOM

概念

虚拟DOM是对DOM的JS抽象表示,它们是JS对象,能够描述DOM的结构和关系。应用 的各种状态变化会作用于虚拟DOM,最终映射到DOM上。Vue源码解析——异步更新队列+虚拟dom_第3张图片

优点
  • 虚拟dom轻量、快速:当它们发生变化时通过新旧虚拟dom对比可以得到最小的dom操作量,从而提升性能。

    patch(vnode, h('div#app', obj.foo))

  • 跨平台:将虚拟dom更新转化为不同运行时特殊操作实现跨平台。(根据不同平台生成不同的patch函数)

    const patch = init([snabbdom_style.default])
    patch(vnode, h('div#app', {
           style:{
           color:'red'}}, obj.foo))
    
  • 兼容性:还可以加入兼容性代码增强操作的兼容性

必要性

vue 1.0中有细粒度的数据变化侦测,它是不需要虚拟DOM的,但是细粒度造成了大量开销,这对于大 型项目来说是不可接受的。因此,vue 2.0选择了中等粒度的解决方案,每一个组件一个watcher实例, 这样状态变化时只能通知到组件,再通过引入虚拟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__是在平台特有代码中指定的

Vue.prototype.__patch__ = inBrowser ? patch : noop

patch获取

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

export const patch: Function = createPatchFunction({
      nodeOps, modules })

platforms\web\runtime\node-ops.js

定义各种原生dom基础操作方法

platforms\web\runtime\modules\index.js

modules 定义了属性更新实现

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

patch实现

首先进行树级别比较,可能有三种情况:增删改。

  • new VNode不存在就删;
  • old VNode不存在就增;
  • 都存在就执行diff执行更新
    Vue源码解析——异步更新队列+虚拟dom_第4张图片
patchVnode

比较两个VNode,包括三种类型操作:

  • 属性更新
  • 文本更新
  • 子节点更新

具体规则如下:

  • 新老节点均有children子节点,则对子节点进行diff操作,调用updateChildren
  • 如果老节点没有子节点而新节点有子节点,先清空老节点的文本内容,然后为其新增子节点。
  • 新节点没有子节点而老节点有子节点的时候,则移除该节点的所有子节点。
  • 新老节点都无子节点的时候,只是文本的替换。

你可能感兴趣的:(Vue,源码分享,vue)