Vue2的diff算法的触发时机和具体过程

 一、diff算法的触发时机

        首先要知道的是,vue通过数据绑定来修改视图,当某个数据被修改的时候,set方法会让闭包中的Dep调用notify通知所有订阅者Watcher,Watcher通过get方法执行vm._update(vm._render()),hydrating);render()函数中会调用Vnode函数来生成虚拟DOM。

        其次,当生命周期走到created~beforeMounted的时候,会进行编译template模板成render函数,此时就会创建出虚拟DOM。之后当数据发生变化的时候会重新编译生成一个新的虚拟DOM(第一段中提到的,vm._update(vm._render()),hydrating),又会执行render()生成虚拟DOM),之后两个新旧虚拟DOM就会进行比较。

        具体的流程概括起来就是:数据改变 -> setter -> Dep.notify() -> diff算法比较......

二、diff算法的触发过程

        过程:Patch() -> patchVnode() -> updateChildren() -> pathVnode() -> updateChildren().....

        步骤1:比较两个虚拟DON树,对根节点root执行Patch(oldVnode,newVnode)函数,比较两个虚拟DOM的根节点是否相同,如果不同就直接替换

        步骤2:

步骤3:

三、一些diff中的细节点

1.怎么判定两个Vnode节点是同一个还是不是同一个呢

/*
  判断两个VNode节点是否是同一个节点,需要满足以下条件
  key相同
  tag(当前节点的标签名)相同
  isComment(是否为注释节点)相同
  是否data(当前节点对应的对象,包含了具体的一些数据信息,是一个VNodeData类型,可以参考VNodeData类型中的数据信息)都有定义
  当标签是<input>的时候,type必须相同
*/
function sameVnode (a, b) {
  return (
    a.key === b.key &&
    a.tag === b.tag &&
    a.isComment === b.isComment &&
    isDef(a.data) === isDef(b.data) &&
    sameInputType(a, b)
  )
}


//这里补充用vue定义的VNode类
export default class VNode {
  tag: string | void;
  data: VNodeData | void;
  children: ?Array<VNode>;
  text: string | void;
  elm: Node | void;
  ns: string | void;
  context: Component | void; // rendered in this component's scope
  functionalContext: Component | void; // only for functional component root nodes
  key: string | number | void;
  componentOptions: VNodeComponentOptions | void;
  componentInstance: Component | void; // component instance
  parent: VNode | void; // component placeholder node
  raw: boolean; // contains raw HTML? (server only)
  isStatic: boolean; // hoisted static node
  isRootInsert: boolean; // necessary for enter transition check
  isComment: boolean; // empty comment placeholder?
  isCloned: boolean; // is a cloned node?
  isOnce: boolean; // is a v-once node?

  constructor (
    tag?: string,
    data?: VNodeData,
    children?: ?Array<VNode>,
    text?: string,
    elm?: Node,
    context?: Component,
    componentOptions?: VNodeComponentOptions
  ) {
    /*当前节点的标签名*/
    this.tag = tag
    /*当前节点对应的对象,包含了具体的一些数据信息,是一个VNodeData类型,可以参考VNodeData类型中的数据信息*/
    this.data = data
    /*当前节点的子节点,是一个数组*/
    this.children = children
    /*当前节点的文本*/
    this.text = text
    /*当前虚拟节点对应的真实dom节点*/
    this.elm = elm
    /*当前节点的名字空间*/
    this.ns = undefined
    /*编译作用域*/
    this.context = context
    /*函数化组件作用域*/
    this.functionalContext = undefined
    /*节点的key属性,被当作节点的标志,用以优化*/
    this.key = data && data.key
    /*组件的option选项*/
    this.componentOptions = componentOptions
    /*当前节点对应的组件的实例*/
    this.componentInstance = undefined
    /*当前节点的父节点*/
    this.parent = undefined
    /*简而言之就是是否为原生HTML或只是普通文本,innerHTML的时候为true,textContent的时候为false*/
    this.raw = false
    /*静态节点标志*/
    this.isStatic = false
    /*是否作为根节点插入*/
    this.isRootInsert = true
    /*是否为注释节点*/
    this.isComment = false
    /*是否为克隆节点*/
    this.isCloned = false
    /*是否有v-once指令*/
    this.isOnce = false
  }

  // DEPRECATED: alias for componentInstance for backwards compat.
  /* istanbul ignore next */
  get child (): Component | void {
    return this.componentInstance
  }
}

对于VnodeData的定义

/**
 * 表示Vue.js中VNode的数据对象的接口
 */
export interface VNodeData {
  key?: string | number; // VNode的唯一标识
  slot?: string; // VNode所属的插槽名称
  scopedSlots?: { [key: string]: ScopedSlot | undefined }; // VNode的作用域插槽
  ref?: VNodeRef; // VNode的引用信息
  refInFor?: boolean; // 表示VNode是否在v-for指令中
  tag?: string; // VNode的HTML标签名
  staticClass?: string; // VNode的静态CSS类
  class?: any; // VNode的动态CSS类
  staticStyle?: { [key: string]: any }; // VNode的静态内联样式
  style?: StyleValue; // VNode的动态内联样式
  props?: { [key: string]: any }; // 传递给VNode组件的属性
  attrs?: { [key: string]: any }; // VNode元素的属性
  domProps?: { [key: string]: any }; // VNode元素的DOM属性
  hook?: { [key: string]: Function }; // VNode生命周期事件钩子
  on?: { [key: string]: Function | Function[] }; // VNode的事件监听器
  nativeOn?: { [key: string]: Function | Function[] }; // VNode的原生事件监听器
  transition?: object; // VNode的过渡信息
  show?: boolean; // 表示VNode是否应该显示
  inlineTemplate?: {
    render: Function; // 内联模板的渲染函数
    staticRenderFns: Function[]; // 内联模板的静态渲染函数
  };
  directives?: VNodeDirective[]; // 应用于VNode的指令
  keepAlive?: boolean; // 表示VNode是否应该保持活动状态
}

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue 3 中引入了一个全新的 diff 算法,称为 Vue 3 Diff Algorithm(或者叫做 Vue 3 的 diff 算法)。这个算法相较于 Vue 2.x 中使用的 Virtual DOM diff 算法(也称为 vuediff 算法)有一些重要的改进。 Vue 2.x 中的 vuediff 算法是基于 Virtual DOM 的,它会通过比较新旧 Virtual DOM 树的差异来确定需要更新的部分,并且将这些差异应用到实际的 DOM 上。然而,这个算法在某些情况下可能会产生一些性能问题,尤其是在处理大型组件树时。 Vue 3 Diff Algorithm 则采用了一种更高效的算法来比较新旧节点。它利用了一种叫做“静态标记”的技术,通过在编译阶段对模板进行静态分析,将静态节点和动态节点区分开来。在更新过程中,只有动态节点才会进行比较和更新,而静态节点则会被跳过,从而大大提升了更新性能。 此外,Vue 3 还引入了一种叫做“基于 Proxy 的响应式系统”的机制,这也与 diff 算法密切相关。Vue 3 的响应式系统使用了 JavaScript 的 Proxy 对象来实现数据的监听和触发更新,而不再依赖于 Object.defineProperty。这样一来,在进行 diff 比较时,Vue 3 可以更加高效地追踪数据的变化,并且只更新实际发生变化的部分。 总的来说,Vue 3 Diff Algorithm 相对于 Vue 2.x 中的 vuediff 算法在性能上有了很大的提升,尤其是在处理大型组件树时。它利用了静态标记和基于 Proxy 的响应式系统这两个新特性,使得更新过程更加高效和精确。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值