Vue.js 组件实例创建和管理代码详解

该代码片段展示了 Vue.js 中创建和管理组件实例的核心逻辑。下面将对代码中的关键部分进行详细解释和注释。

const InternalObjectKey = `__vInternal`;

function guardReactiveProps(props) {
  if (!props)
    return null;
  return isProxy(props) || InternalObjectKey in props ? extend({}, props) : props;
}

const emptyAppContext = createAppContext();
let uid = 0;

function createComponentInstance(vnode, parent, suspense) {
  const type = vnode.type;
  const appContext = (parent ? parent.appContext : vnode.appContext) || emptyAppContext;
  const instance = {
    uid: uid++, // 组件实例唯一标识
    vnode, // 虚拟节点
    type, // 组件类型
    parent, // 父组件实例
    appContext, // 应用上下文
    root: null, // 根组件实例
    next: null, // 下一个组件实例
    subTree: null, // 渲染结果
    effect: null, // 副作用函数
    update: null, // 更新函数
    scope: new EffectScope( // 副作用作用域
      true
      /* detached */
    ),
    render: null, // 渲染函数
    proxy: null, // 代理对象
    exposed: null, // 暴露给外部的属性
    exposeProxy: null, // 暴露给外部的代理对象
    withProxy: null, // 用于 `with` 语法的代理对象
    provides: parent ? parent.provides : Object.create(appContext.provides), // 父级组件提供的依赖
    accessCache: null, // 缓存访问的属性
    renderCache: [], // 渲染缓存
    // local resolved assets
    components: null, // 组件
    directives: null, // 指令
    // resolved props and emits options
    propsOptions: normalizePropsOptions(type, appContext), // props 选项
    emitsOptions: normalizeEmitsOptions(type, appContext), // emits 选项
    // emit
    emit: null, // 事件发射函数
    emitted: null, // 已发射的事件
    // props default value
    propsDefaults: EMPTY_OBJ, // props 默认值
    // inheritAttrs
    inheritAttrs: type.inheritAttrs, // 是否继承 attrs 属性
    // state
    ctx: EMPTY_OBJ, // 组件上下文
    data: EMPTY_OBJ, // 数据
    props: EMPTY_OBJ, // props
    attrs: EMPTY_OBJ, // attrs
    slots: EMPTY_OBJ, // slots
    refs: EMPTY_OBJ, // refs
    setupState: EMPTY_OBJ, // setup 状态
    setupContext: null, // setup 上下文
    // suspense related
    suspense, // suspense 对象
    suspenseId: suspense ? suspense.pendingId : 0, // suspense 标识
    asyncDep: null, // 异步依赖
    asyncResolved: false, // 异步解析状态
    // lifecycle hooks
    // not using enums here because it results in computed properties
    isMounted: false, // 是否已挂载
    isUnmounted: false, // 是否已卸载
    isDeactivated: false, // 是否已停用
    bc: null, // beforeCreate 钩子
    c: null, // created 钩子
    bm: null, // beforeMount 钩子
    m: null, // mounted 钩子
    bu: null, // beforeUpdate 钩子
    u: null, // updated 钩子
    um: null, // beforeUnmount 钩子
    bum: null, // beforeUpdate 钩子
    da: null, // deactivated 钩子
    a: null, // activated 钩子
    rtg: null, // renderTracked 钩子
    rtc: null, // renderTriggered 钩子
    ec: null, // errorCaptured 钩子
    sp: null // serverPrefetch 钩子
  };
  {
    instance.ctx = createDevRenderContext(instance); // 开发环境下的渲染上下文
  }
  instance.root = parent ? parent.root : instance; // 根组件实例
  instance.emit = emit.bind(null, instance); // 事件发射函数
  if (vnode.ce) {
    vnode.ce(instance); // 创建组件实例时执行的钩子
  }
  return instance;
}

let currentInstance = null;

const getCurrentInstance = () => currentInstance || currentRenderingInstance;

const setCurrentInstance = (instance) => {
  currentInstance = instance;
  instance.scope.on(); // 开启副作用作用域
};

const unsetCurrentInstance = () => {
  currentInstance && currentInstance.scope.off(); // 关闭副作用作用域
  currentInstance = null;
};

const isBuiltInTag = /* @__PURE__ */ makeMap("slot,component");

function validateComponentName(name, config) {
  const appIsNativeTag = config.isNativeTag || NO;
  if (isBuiltInTag(name) || appIsNativeTag(name)) {
    warn("Do not use built-in or reserved HTML elements as component id: " + name);
  }
}

解释:

  • InternalObjectKey:一个用于标识对象是否被处理过的字符串常量,用于避免重复处理。

  • guardReactiveProps(props):一个函数,用于确保传入的 props 对象是响应式的。如果 props 对象本身就是一个响应式代理,或者已经被处理过(即包含了 InternalObjectKey),则返回一个浅拷贝的 props 对象;否则返回原始 props 对象。

  • createComponentInstance(vnode, parent, suspense):一个函数,用于创建一个组件实例。该函数接收三个参数:

    • vnode:一个虚拟节点对象,用于描述组件的元信息。
    • parent:一个可选的组件实例,表示当前组件实例的父级组件。
    • suspense:一个可选的 suspense 对象,表示当前组件实例所处的 suspense 状态。

    该函数返回一个组件实例对象,包含了该组件实例的各种状态和方法,例如:

    • uid:组件实例唯一标识
    • vnode:虚拟节点
    • type:组件类型
    • parent:父组件实例
    • appContext:应用上下文
    • root:根组件实例
    • next:下一个组件实例
    • subTree:渲染结果
    • effect:副作用函数
    • update:更新函数
    • scope:副作用作用域
    • render:渲染函数
    • proxy:代理对象
    • exposed:暴露给外部的属性
    • exposeProxy:暴露给外部的代理对象
    • withProxy:用于 with 语法的代理对象
    • provides:父级组件提供的依赖
    • accessCache:缓存访问的属性
    • renderCache:渲染缓存
    • components:组件
    • directives:指令
    • propsOptions:props 选项
    • emitsOptions:emits 选项
    • emit:事件发射函数
    • emitted:已发射的事件
    • propsDefaults:props 默认值
    • inheritAttrs:是否继承 attrs 属性
    • ctx:组件上下文
    • data:数据
    • props:props
    • attrs:attrs
    • slots:slots
    • refs:refs
    • setupState:setup 状态
    • setupContext:setup 上下文
    • suspense:suspense 对象
    • suspenseId:suspense 标识
    • asyncDep:异步依赖
    • asyncResolved:异步解析状态
    • isMounted:是否已挂载
    • isUnmounted:是否已卸载
    • isDeactivated:是否已停用
    • 生命周期钩子函数
  • getCurrentInstance()setCurrentInstance(instance):两个函数,用于获取和设置当前的组件实例。在组件渲染过程中,会通过 setCurrentInstance(instance) 将当前组件实例保存到一个全局变量中,以便在需要时能够访问它。

  • isBuiltInTag(name)validateComponentName(name, config):两个函数,用于检查组件名称是否合法。其中 isBuiltInTag(name) 用于检查组件名称是否为内置标签或组件,而 validateComponentName(name, config) 则用于检查组件名称是否合法(例如是否与内置标签或组件重名),如果不合法,则会发出警告。

该代码片段展现了 Vue.js 中创建和管理组件实例的核心流程,为开发者理解 Vue.js 的组件机制提供了基础。

注意: 这段代码仅展示了部分核心逻辑,实际的 Vue.js 代码会更加复杂,包含了更多细节和优化。

Vue.js 组件实例创建和管理代码详解

原文地址: https://www.cveoy.top/t/topic/lR9y 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录