vue3源码解析(三)

33 阅读9分钟

vue3源码解析(二)

6.processComponent 组件处理

通过 n1 是否存在判断挂载/更新阶段

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  // 设置作用域插槽ID
  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)
  }
}

1.mountComponent 组件挂载

这是vue组件挂载的入口函数

函数接收多个参数,包括初始VNode、容器、锚点、父组件等,然后,创建组件实例的部分,使用了createComponentInstance函数,并将实例赋值给initialVNode.component

处理props和slots的部分,调用了setupComponent,这是Vue 3组合式API的核心,负责初始化组件的props、slots以及执行setup函数

const mountComponent: MountComponentFn = (
initialVNode,      // 初始虚拟节点
container,         // 挂载容器
anchor,            // 锚点位置
parentComponent,   // 父组件实例
parentSuspense,    // 父级Suspense边界
namespace: ElementNamespace, // 命名空间(svg/mathml)
optimized          // 是否启用优化模式
) => {
// 1. 兼容Vue2处理
const compatMountInstance = 
  __COMPAT__ && initialVNode.isCompatRoot && initialVNode.component

// 2. 创建组件实例
const instance: ComponentInternalInstance =
  compatMountInstance ||
  (initialVNode.component = createComponentInstance(
    initialVNode,
    parentComponent,
    parentSuspense,
  ))

// 3. HMR注册(开发模式)
if (__DEV__ && instance.type.__hmrId) {
  registerHMR(instance) // 热更新注册
}

// 4. 开发模式性能追踪
if (__DEV__) {
  pushWarningContext(initialVNode) // 错误上下文
  startMeasure(instance, `mount`)  // 性能监控开始
}

// 5. KeepAlive特殊处理
if (isKeepAlive(initialVNode)) {
  ;(instance.ctx as KeepAliveContext).renderer = internals // 注入渲染器
}

// 6. 初始化组件状态
if (!(__COMPAT__ && compatMountInstance)) {
  setupComponent(instance) // 核心初始化逻辑:
  // - 解析props
  // - 初始化slots
  // - 执行setup()
}

// 7. 异步组件处理
if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
  parentSuspense?.registerDep(instance, setupRenderEffect) // 注册到Suspense
  
  // 创建占位注释节点
  if (!initialVNode.el) {
    const placeholder = (instance.subTree = createVNode(Comment))
    processCommentNode(null, placeholder, container!, anchor)
  }
} else {
  // 8. 同步渲染执行
  setupRenderEffect(
    instance,
    initialVNode,
    container,
    anchor,
    parentSuspense,
    namespace,
    optimized,
  )
}

// 9. 开发模式清理
if (__DEV__) {
  popWarningContext()      // 弹出错误上下文
  endMeasure(instance, `mount`) // 性能监控结束
}
}

组件实例生命周期

graph TD
A[创建实例] --> B[HMR注册]
B --> C[KeepAlive处理]
C --> D[初始化props/slots]
D --> E{异步组件?}
E -->|是| F[注册Suspense]
E -->|否| G[同步渲染]
F --> H[创建占位节点]

1.创建组件实例

组件实例的含义如下

export function createComponentInstance(
vnode: VNode,                  // 组件对应的虚拟节点
parent: ComponentInternalInstance | null, // 父组件实例
suspense: SuspenseBoundary | null,       // Suspense边界状态
) {
// 核心逻辑分4个阶段:

// 1. 确定组件类型和应用上下文
const type = vnode.type as ConcreteComponent
const appContext = (parent ? parent.appContext : vnode.appContext) || emptyAppContext

// 2. 创建组件实例对象
// 核心属性矩阵
const instance: ComponentInternalInstance = {
// 基础标识类属性
uid: uid++,                // 组件唯一递增ID (每次创建+1)
vnode,                     // 关联的虚拟节点对象
type,                      // 组件类型(选项式/函数式组件)
parent,                    // 父组件实例引用

// 应用上下文相关
appContext,                // 应用全局配置(包含全局组件/指令等)
root: null!,               // 组件树根实例(初始化后立即设置)

// 渲染相关
subTree: null!,            // 当前组件渲染的虚拟DOM树
effect: null!,             // 响应式effect实例
update: null!,             // 组件更新函数(由scheduler调度)

// 响应式系统
scope: new EffectScope(/*detached*/true), // 独立的响应式作用域
provides: parent ? parent.provides : ..., // 依赖注入容器(原型链继承父级)

// 性能优化
accessCache: null!,        // 代理属性访问缓存(减少hasOwnProperty调用)
renderCache: [],           // 渲染函数结果缓存(优化重复渲染)

// 组件配置
propsOptions: normalizePropsOptions(...), // 标准化后的props配置
emitsOptions: normalizeEmitsOptions(...), // 标准化后的emits配置
inheritAttrs: type.inheritAttrs, // 是否继承原生属性

// 状态管理
ctx: EMPTY_OBJ,            // 模板执行上下文(开发环境增强)
setupState: EMPTY_OBJ,     // setup()返回的状态对象
setupContext: null,        // setup上下文(attrs/slots/emit)

// 异步处理
suspense,                  // 所属Suspense边界
asyncDep: null,            // 异步依赖(用于Suspense)
asyncResolved: false,      // 异步加载完成标记

// 生命周期状态
isMounted: false,          // 是否已挂载
isUnmounted: false,        // 是否已卸载
isDeactivated: false,      // 是否被keep-alive停用

// 生命周期钩子缩写(数组存储多个钩子):
bc: null, // beforeCreate
c: null,  // created
bm: null, // beforeMount
m: null,  // mounted
// ...其他钩子类似
}

// 3. 开发模式特殊处理
if (__DEV__) {
  instance.ctx = createDevRenderContext(instance) // 调试用上下文
}

// 4. 建立组件树关系
instance.root = parent ? parent.root : instance // 设置根实例
instance.emit = emit.bind(null, instance)       // 绑定emit方法
}

2.setupComponent

setupComponent函数接收一个组件实例instance和一个标志isSSR,用于判断是否是服务端渲染。函数内部处理了props、slots,并根据是否是状态式组件调用不同的逻辑

graph TD
A[开始] --> B[初始化Props]
B --> C[初始化Slots]
C --> D{状态式组件?}
D -->|是| E[初始化状态式组件]
D -->|否| F[结束]
export function setupComponent(
instance: ComponentInternalInstance,
isSSR = false,
) {
isSSR && setInSSRSetupState(isSSR) // 设置SSR状态

const { props, children } = instance.vnode // 从虚拟节点解构属性
const isStateful = isStatefulComponent(instance) // 判断是否状态式组件
initProps(instance, props, isStateful, isSSR) // 标准化props配置,建立响应式绑定
initSlots(instance, children) // 初始化slots

const setupResult = isStateful
  ? setupStatefulComponent(instance, isSSR) // 执行setup函数,创建代理对象
  : undefined // 函数式组件直接返回undefined

isSSR && setInSSRSetupState(false) // 重置SSR状态
return setupResult
}
有状态组件和无状态组件

有状态组件:能维护和自身状态的组件,一般包含props、state,有自己的生命周期

无状态组件:纯函数组件,主要负责接收props绘制ui,没有任何副作用

3.初始化有状态组件 setupStatefulComponent

setupStatefulComponent是Vue 3中处理状态式组件的核心函数,负责执行setup函数并设置组件的响应式代理

核心代理创建PublicInstanceProxyHandlers,使用Proxy实现模板中 this 的代理访问

instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers)
graph TD
A[开始] --> B{开发模式?}
B -->|是| C[验证组件名]
C --> D[验证子组件]
D --> E[验证指令]
E --> F[检查编译器选项]
B -->|否| G[跳过验证]
function setupStatefulComponent(
instance: ComponentInternalInstance,
isSSR: boolean,
) {
const Component = instance.type as ComponentOptions

// 开发环境验证(组件名/子组件/指令/编译器选项)
if (__DEV__) {
  validateComponentName(Component.name, ...) // 组件命名规范检查
  validateChildComponents(Component.components) // 子组件合法性检查
  validateDirectives(Component.directives) // 指令命名规范检查
  checkCompilerOptions(Component.compilerOptions) // 编译器选项兼容性检查
}

// 1. 创建代理访问缓存
instance.accessCache = Object.create(null)

// 2. 创建公共实例代理(响应式系统核心)
instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers)

// 3. 执行setup函数
const { setup } = Component
if (setup) {
  // 创建setup上下文(包含attrs/slots/emit/expose)
  const setupContext = createSetupContext(instance)
  
  // 执行setup前的准备工作
  const reset = setCurrentInstance(instance) // 设置当前实例
  pauseTracking() // 暂停响应式追踪
  
  // 调用setup函数(带错误处理)实际就是trycath调用handleError
  const setupResult = callWithErrorHandling(
    setup,
    [shallowReadonly(instance.props), setupContext]
  )
  
  // 执行后清理
  resetTracking() // 恢复响应式追踪
  reset() // 重置当前实例

  // 处理setup结果
  if (isPromise(setupResult)) { // 异步setup处理
    return setupResult
        .then((resolvedResult: unknown) => {
          handleSetupResult(instance, resolvedResult, isSSR)
        })
        .catch(e => {
          handleError(e, instance, ErrorCodes.SETUP_FUNCTION)
        })
  } else { // 同步setup处理
    handleSetupResult(instance, setupResult, isSSR)
  }
} else { // 无setup函数的情况(选项式组件)
  finishComponentSetup(instance, isSSR)
}
}
核心代理处理器 PublicInstanceProxyHandlers

PublicInstanceProxyHandlers是Vue组件实例代理的核心,负责管理属性的访问和修改,确保响应式系统的正确运作,同时通过缓存和优化提升性能

export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
get({ _: instance }, key: string) {
  // 核心访问逻辑分4个阶段:
  // 1. 特殊标记处理(SKIP/__isVue)
  // 2. 非$开头属性访问(setup/data/props/ctx)
  // 3. 公共属性访问($attrs/$slots等)
  // 4. 全局属性和CSS模块处理

  // 性能优化关键点:
  const { accessCache } = instance
  if (key[0] !== '$') {
    const n = accessCache![key]
    if (n !== undefined) { // 缓存命中直接返回
      switch(n) { /*...*/ }
    }
    // 缓存未命中时按顺序检查各数据源
  }
},

set({ _: instance }, key, value) {
  // 属性赋值安全策略:
  // 1. setupState优先
  // 2. data属性可写
  // 3. props只读保护
  // 4. $开头的公共属性只读
}
}
handleSetupResult 针对setup返回值类型作不同处理

区分处理如下:

返回值类型处理方式应用场景 |
函数设为渲染函数动态模板/JSX
普通对象代理为响应式状态Composition API
VNode对象开发环境警告防止直接返回模板节点
其他类型开发环境类型警告强制类型约束
export function handleSetupResult(
instance: ComponentInternalInstance,
setupResult: unknown,
isSSR: boolean,
) {
// 核心处理逻辑分3种情况:

// 1. setup返回函数(作为渲染函数)
if (isFunction(setupResult)) {
  // 服务端渲染特殊处理
  if (__SSR__ && (instance.type as ComponentOptions).__ssrInlineRender) {
    instance.ssrRender = setupResult // 设置SSR专用渲染函数
  } else {
    instance.render = setupResult as InternalRenderFunction // 设置客户端渲染函数
  }
}

// 2. setup返回对象(状态对象)
else if (isObject(setupResult)) {
  // 开发环境错误检查
  if (__DEV__ && isVNode(setupResult)) {
    warn('setup()不应直接返回VNode') // 提示应返回渲染函数
  }
  
  // 状态处理
  // proxyRefs实现自动解包ref.value:
  // 输入:{ count: ref(0) }
  // 输出:模板中可直接使用count
  instance.setupState = proxyRefs(setupResult) 
  if (__DEV__) {
    exposeSetupStateOnRenderContext(instance) // 开发模式暴露setup状态
  }
}

// 3. 非法返回值处理
else if (__DEV__ && setupResult !== undefined) {
  warn(`setup()应返回对象,实际返回:${typeof setupResult}`) // 类型错误提示
}

// 最终处理
finishComponentSetup(instance, isSSR) // 完成组件设置
}
finishComponentSetup 完成组件设置

finishComponentSetup 函数是Vue组件初始化过程中的关键环节

主要负责:

  1. 检查并生成渲染函数,优先使用setup返回的render

  2. 处理选项式API的选项合并applyOptions

export function finishComponentSetup(
instance: ComponentInternalInstance,
isSSR: boolean,
skipOptions?: boolean,
) {
  //这里处理Vue 2.x的兼容逻辑, convertLegacyRenderFn 会将选项式API转换为兼容模式下的渲染函数
  if (__COMPAT__) {
    convertLegacyRenderFn(instance)
    if (__DEV__ && Component.compatConfig) {
      validateCompatConfig(Component.compatConfig)
    }
  }
  //组件没有render函数时,会尝试通过模板编译生成render函数。编译过程会根据不同配置合并全局和组件的编译器选项
  if (!instance.render) {
    if (!isSSR && compile && !Component.render) {
      // 模板编译逻辑
      const template = /* 模板来源判断 */;
      if (template) {
        // 合并编译器选项
        const finalCompilerOptions = extend(...);
        // 执行编译
        Component.render = compile(template, finalCompilerOptions);
      }
    }
    // 设置实例的render函数
    instance.render = (Component.render || NOOP) as InternalRenderFunction;
  }
  //当启用选项式API时(默认),会调用 applyOptions 处理组件选项(data、methods等)
      if (__FEATURE_OPTIONS_API__ && !(__COMPAT__ && skipOptions)) {
        applyOptions(instance)
  }
}
选项式API处理函数 applyOptions

applyOptions函数接收一个组件实例,然后处理各种选项,比如data、computed、methods、watch等。这符合Vue 2.x选项式API的处理方式,因此这个函数的主要作用是将选项合并到组件实例中

  • 首先函数解构了各种选项,包括data、computed、methods等

  • 处理inject和methods的部分,resolveInjections函数负责解析注入的依赖,而methods被绑定到组件实例的上下文中。这里使用了Object.defineProperty

  • data选项的处理中,函数检查data是否是一个函数,并调用它获取数据对象,然后使用reactive将其转换为响应式对象。这符合Vue 3的响应式系统设计。同时,开发环境下会对data中的每个属性进行检查,确保没有重复,并通过Object.defineProperty将它们暴露到上下文中

  • computed和watch的处理部分,computed属性通过computed函数创建,并绑定到上下文,而watch选项则通过createWatcher创建观察者。provide选项的处理使用了provide函数。

  • 生命周期钩子的注册部分,使用registerLifecycleHook将选项中的钩子函数注册到对应的生命周期事件上。这里还处理了Vue 2.x的兼容性钩子,如beforeDestroydestroyed,将它们映射到Vue 3的等效钩子上

  • 最后,处理expose选项,将需要暴露的属性定义到实例的exposed对象上,并设置组件的render函数和其他资源,如components和directives

    选项初始化顺序 :

  graph TD
A[Props] --> B[Inject]
B --> C[Methods]
C --> D[Data]
D --> E[Computed]
E --> F[Watch]
  export function applyOptions(instance: ComponentInternalInstance) {
  // 核心处理流程:
  // 1. 合并选项(包含mixins/extends)
  const options = resolveMergedOptions(instance)
  
  // 2. 开发环境属性重复检查
  const checkDuplicateProperties = __DEV__ ? createDuplicateChecker() : null
  
  // 3. 生命周期处理顺序
  if (options.beforeCreate) {
    callHook(options.beforeCreate, instance, LifecycleHooks.BEFORE_CREATE)
  }

  // 4. 解构各选项
   const {
    // state
    data: dataOptions,
    computed: computedOptions,
    methods,
    watch: watchOptions,
    provide: provideOptions,
    inject: injectOptions,
    // lifecycle
    created,
    beforeMount,
    mounted,
    beforeUpdate,
    updated,
    activated,
    deactivated,
    beforeDestroy,
    beforeUnmount,
    destroyed,
    unmounted,
    render,
    renderTracked,
    renderTriggered,
    errorCaptured,
    serverPrefetch,
    // public API
    expose,
    inheritAttrs,
    // assets
    components,
    directives,
    filters,
  } = options
  
  // 5.依赖注入检查
  //- injectOptions :组件选项中的 inject 配置,支持数组或对象格式
  //- ctx :组件实例的上下文对象,注入的属性会被挂载到这里
  //- checkDuplicateProperties :开发环境下的属性重复检查器
   if (injectOptions) {
    resolveInjections(injectOptions, ctx, checkDuplicateProperties)
  }
  
   //6.组件选项中定义的methods绑定到组件实例的上下文(ctx)中
  if (methods) {
    for (const key in methods) {
      const methodHandler = (methods as MethodOptions)[key]
      if (isFunction(methodHandler)) {
        //测试环境用Object.defineProperty暴露到上下文
        if (__DEV__) {
          Object.defineProperty(ctx, key, {
            value: methodHandler.bind(publicThis),
            configurable: true,
            enumerable: true,
            writable: true,
          })
        } else {
            //线上直接赋值
          ctx[key] = methodHandler.bind(publicThis)
        }
        if (__DEV__) {
          checkDuplicateProperties!(OptionTypes.METHODS, key)
        }
      } else if (__DEV__) {
        warn(
          `Method "${key}" has type "${typeof methodHandler}" in the component definition. ` +
            `Did you reference the function correctly?`,
        )
      }
    }
  }
  
  // 7. 响应式数据初始化
  if (dataOptions) {
    instance.data = reactive(dataOptions.call(publicThis, publicThis))
  }

  // 8. computed函数创建
   if (computedOptions) {
    for (const key in computedOptions) {
      const opt = (computedOptions as ComputedOptions)[key]
      const get = isFunction(opt)
        ? opt.bind(publicThis, publicThis)
        : isFunction(opt.get)
          ? opt.get.bind(publicThis, publicThis)
          : NOOP
      
      const set =
        !isFunction(opt) && isFunction(opt.set)
          ? opt.set.bind(publicThis)
          : __DEV__
            ? () => {
                warn(
                  `Write operation failed: computed property "${key}" is readonly.`,
                )
              }
            : NOOP
      const c = computed({
        get,
        set,
      })
      Object.defineProperty(ctx, key, {
        enumerable: true,
        configurable: true,
        get: () => c.value,
        set: v => (c.value = v),
      })
      if (__DEV__) {
        checkDuplicateProperties!(OptionTypes.COMPUTED, key)
      }
    }
  }

//createWatcher 创建观察者
  if (watchOptions) {
    for (const key in watchOptions) {
      createWatcher(watchOptions[key], ctx, publicThis, key)
    }
  }
    
    //provide选项的处理使用了`provide`函数
  if (provideOptions) {
    const provides = isFunction(provideOptions)
      ? provideOptions.call(publicThis)
      : provideOptions
    Reflect.ownKeys(provides).forEach(key => {
      provide(key, provides[key])
    })
  }

  if (created) {
    callHook(created, instance, LifecycleHooks.CREATED)
  }

  // 9. 生命周期钩子注册
  registerLifecycleHooks(options, publicThis)
}
依赖注入处理流程
  graph TD
A[检查inject选项] --> B{存在注入配置?}
B -->|是| C[解析注入来源]
B -->|否| D[跳过处理]
C --> E[处理数组/对象格式]
E --> F[从祖先组件查找provide]
F --> G[注入到组件上下文]
处理methods 流程图
graph TD
A[遍历methods选项] --> B{是函数类型?}
B -->|是| C[开发环境特殊处理]
B -->|否| D[生产环境直接绑定]
C --> E[使用defineProperty定义属性]
D --> F[直接赋值到上下文]
E --> G[设置可配置/可枚举/可写]
F --> H[绑定组件实例上下文]
处理data流程图
  • 类型校验 :强制要求 data 必须是函数(开发环境下)
  • 返回值校验 :返回值必须是普通对象(非Promise/非数组等)
  • 异步校验 :禁止返回Promise(提示使用setup+Suspense)
  • 使用 reactive() 创建深度响应式对象
  • 响应式系统基于Proxy实现(Vue 3核心特性)
 graph TD
A[检查data选项] --> B{存在配置?}
B -->|是| C[开发环境校验]
B -->|否| D[跳过处理]
C --> E[校验是否为函数]
E --> F[执行data函数]
F --> G[校验返回值类型]
G --> H[创建响应式对象]
H --> I[开发环境代理属性]

初始化组件结束后,进入渲染阶段

vue3源码解析(四)