Skip to content

第2.1节:vnode 到真实 DOM 是如何转变的?

在 Vue.js 3 中,虚拟节点(Virtual Node,简称 vnode)到真实 DOM 的转换是整个渲染系统的核心。本节将深入分析这一过程的实现原理,从 vnode 的创建到最终的 DOM 操作,揭示 Vue 3 高效渲染机制的奥秘。

2.1.1 虚拟节点(VNode)的结构与创建

VNode 的基本结构

在 Vue 3 中,VNode 是一个描述真实 DOM 节点的 JavaScript 对象。让我们先看看 VNode 的核心结构:

typescript
// core/packages/runtime-core/src/vnode.ts
export interface VNode<
  HostNode = RendererNode,
  HostElement = RendererElement,
  ExtraProps = { [key: string]: any },
> {
  __v_isVNode: true
  type: VNodeTypes
  props: (VNodeProps & ExtraProps) | null
  key: string | number | symbol | null
  ref: VNodeNormalizedRef | null
  children: VNodeNormalizedChildren
  component: ComponentInternalInstance | null
  dirs: DirectiveBinding[] | null
  transition: TransitionHooks<HostElement> | null
  
  // DOM 相关
  el: HostNode | null
  anchor: HostNode | null
  target: HostElement | null
  targetStart: HostNode | null
  targetAnchor: HostNode | null
  
  // 优化标记
  shapeFlag: number
  patchFlag: number
  dynamicProps: string[] | null
  dynamicChildren: VNode[] | null
  
  // 应用上下文
  appContext: AppContext | null
  
  // 其他元数据
  scopeId: string | null
  slotScopeIds: string[] | null
  ssrId: number | null
  memo: any[] | null
  isDehydrated?: boolean
  suspense?: SuspenseBoundary | null
  ssrOptimized?: boolean
  ctx?: ComponentInternalInstance | null
  ce?: (instance: ComponentInternalInstance) => void
}

VNode 的创建过程

1. createVNode 函数

createVNode 是创建虚拟节点的主要入口函数:

typescript
// core/packages/runtime-core/src/vnode.ts
export const createVNode = (
  __DEV__ ? createVNodeWithArgsTransform : _createVNode
) as typeof _createVNode

function _createVNode(
  type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
  props: (Data & VNodeProps) | null = null,
  children: unknown = null,
  patchFlag: number = 0,
  dynamicProps: string[] | null = null,
  isBlockNode = false,
): VNode {
  // 类型校验和归一化
  if (!type || type === NULL_DYNAMIC_COMPONENT) {
    if (__DEV__ && !type) {
      warn(`Invalid vnode type when creating vnode: ${type}.`)
    }
    type = Comment
  }

  // 如果已经是 vnode,则克隆
  if (isVNode(type)) {
    const cloned = cloneVNode(type, props, true /* mergeRef: true */)
    if (children) {
      normalizeChildren(cloned, children)
    }
    if (isBlockTreeEnabled > 0 && !isBlockNode && currentBlock) {
      if (cloned.shapeFlag & ShapeFlags.COMPONENT) {
        currentBlock[currentBlock.indexOf(type)] = cloned
      } else {
        currentBlock.push(cloned)
      }
    }
    cloned.patchFlag = patchFlag | PatchFlags.BAIL
    return cloned
  }

  // 类组件归一化
  if (isClassComponent(type)) {
    type = type.__vccOpts
  }

  // props 归一化
  if (props) {
    props = guardReactiveProps(props)!
    let { class: klass, style } = props
    if (klass && !isString(klass)) {
      props.class = normalizeClass(klass)
    }
    if (isObject(style)) {
      if (isProxy(style) && !isArray(style)) {
        style = extend({}, style)
      }
      props.style = normalizeStyle(style)
    }
  }

  // 编码 vnode 类型信息
  const shapeFlag = isString(type)
    ? ShapeFlags.ELEMENT
    : __FEATURE_SUSPENSE__ && isSuspense(type)
      ? ShapeFlags.SUSPENSE
      : isTeleport(type)
        ? ShapeFlags.TELEPORT
        : isObject(type)
          ? ShapeFlags.STATEFUL_COMPONENT
          : isFunction(type)
            ? ShapeFlags.FUNCTIONAL_COMPONENT
            : 0

  return createBaseVNode(
    type,
    props,
    children,
    patchFlag,
    dynamicProps,
    shapeFlag,
    isBlockNode,
    true,
  )
}

2. createBaseVNode 函数

createBaseVNode 负责创建基础的 VNode 对象:

typescript
function createBaseVNode(
  type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
  props: (Data & VNodeProps) | null = null,
  children: unknown = null,
  patchFlag = 0,
  dynamicProps: string[] | null = null,
  shapeFlag = type === Fragment ? 0 : ShapeFlags.ELEMENT,
  isBlockNode = false,
  needFullChildrenNormalization = false,
) {
  const vnode = {
    __v_isVNode: true,
    __v_skip: true,
    type,
    props,
    key: props && normalizeKey(props),
    ref: props && normalizeRef(props),
    scopeId: currentScopeId,
    slotScopeIds: null,
    children,
    component: null,
    suspense: null,
    ssrId: null,
    ssrOptimized: false,
    dirs: null,
    transition: null,
    el: null,
    anchor: null,
    target: null,
    targetStart: null,
    targetAnchor: null,
    staticCount: 0,
    shapeFlag,
    patchFlag,
    dynamicProps,
    dynamicChildren: null,
    appContext: null,
    ctx: currentRenderingInstance,
    memo: null,
    isDehydrated: false,
    ce: null,
  } as VNode

  // 子节点归一化
  if (needFullChildrenNormalization) {
    normalizeChildren(vnode, children)
    // 归一化 suspense children
    if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
      ;(type as typeof SuspenseImpl).normalize(vnode)
    }
  } else if (children) {
    // 编译生成的 vnode 接收到的 children 已经归一化
    vnode.shapeFlag |= isString(children)
      ? ShapeFlags.TEXT_CHILDREN
      : ShapeFlags.ARRAY_CHILDREN
  }

  // 验证 key
  if (__DEV__ && vnode.key !== vnode.key) {
    warn(`VNode created with invalid key (NaN). VNode type:`, vnode.type)
  }

  // 跟踪 vnode 用于块树优化
  if (
    isBlockTreeEnabled > 0 &&
    // 避免块节点跟踪自己
    !isBlockNode &&
    // 有当前父块
    currentBlock &&
    // vnode 需要 patch
    (vnode.patchFlag > 0 || shapeFlag & ShapeFlags.COMPONENT) &&
    // 过滤掉静态节点
    vnode.patchFlag !== PatchFlags.NEED_HYDRATION
  ) {
    currentBlock.push(vnode)
  }

  if (__COMPAT__) {
    convertLegacyVModelProps(vnode)
    defineLegacyVNodeProperties(vnode)
  }

  return vnode
}

2.1.2 Patch 算法:新旧 VNode 的对比与更新

Patch 函数的核心逻辑

patch 函数是 Vue 3 渲染系统的核心,负责对比新旧 VNode 并更新 DOM:

typescript
// core/packages/runtime-core/src/renderer.ts
const patch: PatchFn = (
  n1,
  n2,
  container,
  anchor = null,
  parentComponent = null,
  parentSuspense = null,
  namespace = undefined,
  slotScopeIds = null,
  optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren,
) => {
  // 如果新旧节点相同,直接返回
  if (n1 === n2) {
    return
  }

  // 如果新旧节点类型不同,卸载旧节点
  if (n1 && !isSameVNodeType(n1, n2)) {
    anchor = getNextHostNode(n1)
    unmount(n1, parentComponent, parentSuspense, true)
    n1 = null
  }

  // 如果有 BAIL 标记,关闭优化
  if (n2.patchFlag === PatchFlags.BAIL) {
    optimized = false
    n2.dynamicChildren = null
  }

  const { type, ref, shapeFlag } = n2
  
  // 根据节点类型进行不同的处理
  switch (type) {
    case Text:
      processText(n1, n2, container, anchor)
      break
    case Comment:
      processCommentNode(n1, n2, container, anchor)
      break
    case Static:
      if (n1 == null) {
        mountStaticNode(n2, container, anchor, namespace)
      } else if (__DEV__) {
        patchStaticNode(n1, n2, container, namespace)
      }
      break
    case Fragment:
      processFragment(
        n1,
        n2,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        namespace,
        slotScopeIds,
        optimized,
      )
      break
    default:
      if (shapeFlag & ShapeFlags.ELEMENT) {
        // 处理普通元素
        processElement(
          n1,
          n2,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          namespace,
          slotScopeIds,
          optimized,
        )
      } else if (shapeFlag & ShapeFlags.COMPONENT) {
        // 处理组件
        processComponent(
          n1,
          n2,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          namespace,
          slotScopeIds,
          optimized,
        )
      } else if (shapeFlag & ShapeFlags.TELEPORT) {
        // 处理 Teleport
        ;(type as typeof TeleportImpl).process(
          n1 as TeleportVNode,
          n2 as TeleportVNode,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          namespace,
          slotScopeIds,
          optimized,
          internals,
        )
      } else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
        // 处理 Suspense
        ;(type as typeof SuspenseImpl).process(
          n1,
          n2,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          namespace,
          slotScopeIds,
          optimized,
          internals,
        )
      } else if (__DEV__) {
        warn('Invalid VNode type:', type, `(${typeof type})`)
      }
  }

  // 设置 ref
  if (ref != null && parentComponent) {
    setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, !n2)
  }
}

元素节点的处理:processElement

对于普通的 HTML 元素,Vue 使用 processElement 函数进行处理:

typescript
const processElement = (
  n1: VNode | null,
  n2: VNode,
  container: RendererElement,
  anchor: RendererNode | null,
  parentComponent: ComponentInternalInstance | null,
  parentSuspense: SuspenseBoundary | null,
  namespace: ElementNamespace,
  slotScopeIds: string[] | null,
  optimized: boolean,
) => {
  if (n1 == null) {
    // 挂载新元素
    mountElement(
      n2,
      container,
      anchor,
      parentComponent,
      parentSuspense,
      namespace,
      slotScopeIds,
      optimized,
    )
  } else {
    // 更新现有元素
    patchElement(
      n1,
      n2,
      parentComponent,
      parentSuspense,
      namespace,
      slotScopeIds,
      optimized,
    )
  }
}

2.1.3 组件的挂载与更新

组件处理:processComponent

组件的处理逻辑相对复杂,需要考虑组件实例的创建、初始化和更新:

typescript
const processComponent = (
  n1: VNode | null,
  n2: VNode,
  container: RendererElement,
  anchor: RendererNode | null,
  parentComponent: ComponentInternalInstance | null,
  parentSuspense: SuspenseBoundary | null,
  namespace: ElementNamespace,
  slotScopeIds: string[] | null,
  optimized: boolean,
) => {
  n2.slotScopeIds = slotScopeIds
  if (n1 == null) {
    // 挂载新组件
    if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
      // KeepAlive 组件的激活
      ;(parentComponent!.ctx as KeepAliveContext).activate(
        n2,
        container,
        anchor,
        namespace,
        optimized,
      )
    } else {
      // 普通组件挂载
      mountComponent(
        n2,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        namespace,
        optimized,
      )
    }
  } else {
    // 更新现有组件
    updateComponent(n1, n2, optimized)
  }
}

组件挂载:mountComponent

mountComponent 函数负责挂载新的组件实例:

typescript
const mountComponent: MountComponentFn = (
  initialVNode,
  container,
  anchor,
  parentComponent,
  parentSuspense,
  namespace: ElementNamespace,
  optimized,
) => {
  // 2.x 兼容模式
  const compatMountInstance =
    __COMPAT__ && initialVNode.isCompatRoot && initialVNode.component
  
  // 创建组件实例
  const instance: ComponentInternalInstance =
    compatMountInstance ||
    (initialVNode.component = createComponentInstance(
      initialVNode,
      parentComponent,
      parentSuspense,
    ))

  if (__DEV__ && instance.type.__hmrId) {
    registerHMR(instance)
  }

  if (__DEV__) {
    pushWarningContext(initialVNode)
    startMeasure(instance, `mount`)
  }

  // 注入 renderer internals 用于 KeepAlive
  if (isKeepAlive(initialVNode)) {
    ;(instance.ctx as KeepAliveContext).renderer = internals
  }

  // 解析 props 和 slots 用于 setup() 上下文
  if (!(__COMPAT__ && compatMountInstance)) {
    if (__DEV__) {
      startMeasure(instance, `init`)
    }
    setupComponent(instance)
    if (__DEV__) {
      endMeasure(instance, `init`)
    }
  }

  // 设置异步依赖的 suspense
  if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
    parentSuspense && parentSuspense.registerDep(instance, setupRenderEffect, optimized)

    // 给异步组件一个占位符
    if (!initialVNode.el) {
      const placeholder = (instance.subTree = createVNode(Comment, null, 'async'))
      processCommentNode(null, placeholder, container, anchor)
    }
    return
  }

  setupRenderEffect(
    instance,
    initialVNode,
    container,
    anchor,
    parentSuspense,
    namespace,
    optimized,
  )

  if (__DEV__) {
    popWarningContext()
    endMeasure(instance, `mount`)
  }
}

组件实例创建:createComponentInstance

组件实例是组件运行时的核心数据结构:

typescript
export function createComponentInstance(
  vnode: VNode,
  parent: ComponentInternalInstance | null,
  suspense: SuspenseBoundary | null,
): ComponentInternalInstance {
  const type = vnode.type as ConcreteComponent
  // 继承父组件的应用上下文,或者从根 vnode 采用
  const appContext =
    (parent ? parent.appContext : vnode.appContext) || emptyAppContext

  const instance: ComponentInternalInstance = {
    uid: uid++,
    vnode,
    type,
    parent,
    appContext,
    root: null!, // 将立即设置
    next: null,
    subTree: null!, // 将在创建后同步设置
    effect: null!,
    update: null!, // 将在创建后同步设置
    job: null!,
    scope: new EffectScope(true /* detached */),
    render: null,
    proxy: null,
    exposed: null,
    exposeProxy: null,
    withProxy: null,

    provides: parent ? parent.provides : Object.create(appContext.provides),
    ids: parent ? parent.ids : ['', 0, 0],
    accessCache: null!,
    renderCache: [],

    // 本地解析的资源
    components: null,
    directives: null,

    // 解析的 props 和 emits 选项
    propsOptions: normalizePropsOptions(type, appContext),
    emitsOptions: normalizeEmitsOptions(type, appContext),

    // emit
    emit: null!, // 将立即设置
    emitted: null,

    // props 默认值
    propsDefaults: EMPTY_OBJ,

    // inheritAttrs
    inheritAttrs: type.inheritAttrs,

    // 状态
    ctx: EMPTY_OBJ,
    data: EMPTY_OBJ,
    props: EMPTY_OBJ,
    attrs: EMPTY_OBJ,
    slots: EMPTY_OBJ,
    refs: EMPTY_OBJ,
    setupState: EMPTY_OBJ,
    setupContext: null,

    // suspense 相关
    suspense,
    suspenseId: suspense ? suspense.pendingId : 0,
    asyncDep: null,
    asyncResolved: false,

    // 生命周期钩子
    isMounted: false,
    isUnmounted: false,
    isDeactivated: false,
    bc: null,
    c: null,
    bm: null,
    m: null,
    bu: null,
    u: null,
    um: null,
    bum: null,
    da: null,
    a: null,
    rtg: null,
    rtc: null,
    ec: null,
    sp: null,
  }
  
  if (__DEV__) {
    instance.ctx = createDevRenderContext(instance)
  } else {
    instance.ctx = { _: instance }
  }
  instance.root = parent ? parent.root : instance
  instance.emit = emit.bind(null, instance)

  // 应用自定义元素特殊处理
  if (vnode.ce) {
    vnode.ce(instance)
  }

  return instance
}

组件初始化:setupComponent

setupComponent 函数负责初始化组件实例:

typescript
export function setupComponent(
  instance: ComponentInternalInstance,
  isSSR = false,
  optimized = false,
): Promise<void> | undefined {
  isSSR && setInSSRSetupState(isSSR)

  const { props, children } = instance.vnode
  const isStateful = isStatefulComponent(instance)
  initProps(instance, props, isStateful, isSSR)
  initSlots(instance, children, optimized || isSSR)

  const setupResult = isStateful
    ? setupStatefulComponent(instance, isSSR)
    : undefined

  isSSR && setInSSRSetupState(false)
  return setupResult
}

function setupStatefulComponent(
  instance: ComponentInternalInstance,
  isSSR: boolean,
) {
  const Component = instance.type as ComponentOptions

  if (__DEV__) {
    if (Component.name) {
      validateComponentName(Component.name, instance.appContext.config)
    }
    if (Component.components) {
      const names = Object.keys(Component.components)
      for (let i = 0; i < names.length; i++) {
        validateComponentName(names[i], instance.appContext.config)
      }
    }
    if (Component.directives) {
      const names = Object.keys(Component.directives)
      for (let i = 0; i < names.length; i++) {
        validateDirectiveName(names[i])
      }
    }
    if (Component.compilerOptions && isRuntimeOnly()) {
      warn(
        `"compilerOptions" is only supported when using a build of Vue that ` +
          `includes the runtime compiler. Since you are using a runtime-only ` +
          `build, the options should be passed via your build tool config instead.`,
      )
    }
  }
  
  // 0. 创建渲染代理属性访问缓存
  instance.accessCache = Object.create(null)
  // 1. 创建公共实例 / 渲染代理
  instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers)
  if (__DEV__) {
    exposePropsOnRenderContext(instance)
  }
  
  // 2. 调用 setup()
  const { setup } = Component
  if (setup) {
    pauseTracking()
    const setupContext = (instance.setupContext =
      setup.length > 1 ? createSetupContext(instance) : null)
    const reset = setCurrentInstance(instance)
    const setupResult = callWithErrorHandling(
      setup,
      instance,
      ErrorCodes.SETUP_FUNCTION,
      [
        __DEV__ ? shallowReadonly(instance.props) : instance.props,
        setupContext,
      ],
    )
    const isAsyncSetup = isPromise(setupResult)
    resetTracking()
    reset()

    if ((isAsyncSetup || instance.sp) && !isAsyncWrapper(instance)) {
      // async setup / serverPrefetch,标记为异步边界用于 useId()
      markAsyncBoundary(instance)
    }

    if (isAsyncSetup) {
      setupResult.then(unsetCurrentInstance, unsetCurrentInstance)
      if (isSSR) {
        // 返回 promise 以便服务器渲染器可以等待它
        return setupResult
          .then((resolvedResult: unknown) => {
            handleSetupResult(instance, resolvedResult, isSSR)
          })
          .catch(e => {
            handleError(e, instance, ErrorCodes.SETUP_FUNCTION)
          })
      } else if (__FEATURE_SUSPENSE__) {
        // async setup 返回 Promise
        // 在这里退出并等待重新进入
        instance.asyncDep = setupResult
        if (__DEV__ && !instance.suspense) {
          const name = Component.name ?? 'Anonymous'
          warn(
            `Component <${name}>: setup function returned a promise, but no ` +
              `<Suspense> boundary was found in the parent component tree. ` +
              `A component with async setup() must be nested in a <Suspense> ` +
              `in order to be rendered.`,
          )
        }
      } else if (__DEV__) {
        warn(
          `setup() returned a Promise, but the version of Vue you are using ` +
            `does not support it yet.`,
        )
      }
    } else {
      handleSetupResult(instance, setupResult, isSSR)
    }
  } else {
    finishComponentSetup(instance, isSSR)
  }
}

2.1.4 DOM 操作:nodeOps 的设计与实现

nodeOps 接口设计

Vue 3 通过 nodeOps 对象抽象了所有的 DOM 操作,使得渲染器可以跨平台工作:

typescript
// core/packages/runtime-dom/src/nodeOps.ts
const doc = (typeof document !== 'undefined' ? document : null) as Document

const templateContainer = doc && /*#__PURE__*/ doc.createElement('template')

export const nodeOps: Omit<RendererOptions<Node, Element>, 'patchProp'> = {
  // 插入节点
  insert: (child, parent, anchor) => {
    parent.insertBefore(child, anchor || null)
  },

  // 移除节点
  remove: child => {
    const parent = child.parentNode
    if (parent) {
      parent.removeChild(child)
    }
  },

  // 创建元素
  createElement: (tag, namespace, is, props): Element => {
    const el =
      namespace === 'svg'
        ? doc.createElementNS(svgNS, tag)
        : namespace === 'mathml'
          ? doc.createElementNS(mathmlNS, tag)
          : is
            ? doc.createElement(tag, { is })
            : doc.createElement(tag)

    if (tag === 'select' && props && props.multiple != null) {
      ;(el as HTMLSelectElement).setAttribute('multiple', props.multiple)
    }

    return el
  },

  // 创建文本节点
  createText: text => doc.createTextNode(text),

  // 创建注释节点
  createComment: text => doc.createComment(text),

  // 设置文本内容
  setText: (node, text) => {
    node.nodeValue = text
  },

  // 设置元素文本内容
  setElementText: (el, text) => {
    el.textContent = text
  },

  // 获取父节点
  parentNode: node => node.parentNode as Element | null,

  // 获取下一个兄弟节点
  nextSibling: node => node.nextSibling,

  // 查询选择器
  querySelector: selector => doc.querySelector(selector),

  // 设置作用域 ID
  setScopeId(el, id) {
    el.setAttribute(id, '')
  },

  // 插入静态内容
  insertStaticContent(content, parent, anchor, namespace, start, end) {
    // 缓存插入
    const beforeNode = anchor ? anchor.previousSibling : parent.lastChild
    // 避免 innerHTML 时 leading whitespace 被移除
    if (start && (start === end || start.nextSibling)) {
      while (true) {
        parent.insertBefore(start!.cloneNode(true), anchor)
        if (start === end || !(start = start!.nextSibling)) break
      }
    } else {
      // 新鲜插入
      templateContainer.innerHTML =
        namespace === 'svg'
          ? `<svg>${content}</svg>`
          : namespace === 'mathml'
            ? `<math>${content}</math>`
            : content

      const template = templateContainer.content
      if (namespace === 'svg' || namespace === 'mathml') {
        // 移除外层的 svg/math 包装器
        const wrapper = template.firstChild!
        while (wrapper.firstChild) {
          template.appendChild(wrapper.firstChild)
        }
        template.removeChild(wrapper)
      }
      parent.insertBefore(template, anchor)
    }
    return [
      // 第一个
      beforeNode ? beforeNode.nextSibling! : parent.firstChild!,
      // 最后一个
      anchor ? anchor.previousSibling! : parent.lastChild!,
    ]
  },
}

DOM 操作的优化策略

  1. 批量操作:Vue 3 通过调度器将多个 DOM 操作合并到一个微任务中执行
  2. 静态提升:编译时将静态内容提升,避免重复创建
  3. Block Tree:通过块级优化减少不必要的遍历
  4. Patch Flags:精确标记需要更新的属性,避免全量对比

2.1.5 渲染流程时序图

2.1.6 性能优化要点

1. 编译时优化

  • 静态提升:将静态节点提升到渲染函数外部
  • Patch Flag 生成:编译时生成精确的更新标记
  • Block Tree 优化:减少运行时的树遍历

2. 运行时优化

  • 形状标记(Shape Flags):快速判断节点类型
  • 动态子节点跟踪:只对比动态部分
  • 组件更新优化:精确的组件更新判断

3. 内存优化

  • 对象复用:复用 VNode 对象减少 GC 压力
  • 弱引用:适当使用 WeakMap 避免内存泄漏
  • 及时清理:组件卸载时清理相关引用

2.1.7 总结

Vue 3 的 vnode 到真实 DOM 的转换过程体现了现代前端框架的设计精髓:

  1. 分层设计:通过 VNode 抽象层将逻辑与平台解耦
  2. 精确更新:通过 Patch Flags 和 Block Tree 实现精确的差异更新
  3. 性能优化:编译时和运行时的双重优化策略
  4. 可扩展性:通过 nodeOps 抽象支持跨平台渲染

这套设计不仅保证了 Vue 3 的高性能,也为未来的扩展和优化奠定了坚实的基础。理解这一过程对于深入掌握 Vue 3 的工作原理和进行性能优化具有重要意义。


微信公众号二维码