Vue3组件挂载之创建组件实例详解

前情提要

上文我们讲解了执行createApp(App).mount('#root')中的mount函数,我们分析了创建虚拟节点的几个方法,以及setRef的执行机制、本文我们继续讲解mountComponent,挂载组件的流程。

本文主要内容

  • createComponentInstance发生了什么?
  • 如何标准化组件定义的props、emits?
  • 为什么provide提供的值子组件都能访问到?
  • 组件v-model实现原理、组件v-model修饰符实现原理。
  • 组件emit实现原理。
  • once修饰符实现原理。
  • 几乎所有组件实例属性的详细讲解。

mountComponent

  • 这个方法主要用于挂载组件,根据传递的虚拟节点创建组件实例,调用setupComponent函数进行初始化组件实例的插槽props,如果是有状态组件还需要处理setup的返回值。最后调用setupRenderEffect绑定副作用更新函数。这个函数比较简单,我们将重心放到createComponentInstance、setupComponent、setupRenderEffect当中、后文也是围绕这三个方法进行依次讲解。
const mountComponent = (
    initialVNode,
    container,
    anchor,
    parentComponent,
    parentSuspense,
    isSVG,
    optimized
  ) => {
    //创建组件实例
    const instance = (initialVNode.component = createComponentInstance(
      initialVNode,
      parentComponent,
      parentSuspense
    ));
    setupComponent(instance);
    if (instance.asyncDep) {
      //处理异步逻辑,suspense的时候在进行讲解
    }
    setupRenderEffect(
      instance,
      initialVNode,
      container,
      anchor,
      parentSuspense,
      isSVG,
      optimized
    );
  };

创建组件实例

  • createComponentInstance: 根据虚拟节点创建组件实例的方法,简单的说就是创建一个instance对象,这个上面有许多的属性用来描述当前这个组件以及存储需要后续使用到的属性,所以我们主要讲解各个属性的作用,我们先来看看这个函数的源码。
  • uid: 标识当前实例的id,这是一个从0开始的递增值,同时他也是Vue调度器优先级值越小的值优先级越高,试想一下,父组件先挂载,然后才会挂载子节点,子节点如果包含组件,那么uid的值将会比父组件uid值大,当父组件挂载完毕,父组件的兄弟组件开始挂载,那么uid将会更大,这样也符合流程更新逻辑,关于调度器我们之后单独开一章节来进行讲解。
  • vnode: 实例的虚拟节点
  • parent: 当前组件实例的父组件实例。
  • appContext:createApp的时候注入的一个上下文,我们通过app.component,app.mixin调用后缓存的componentmixin都放在这里面,如果没有parent表示当前是根组件,那么就是这个appContext,如果有parent则继承parent的appContext。我们可以通过这里发现所有的组件的appContext都会是唯一的一个context。因此所有组件实例都将能访问到全局注册的component、mixin、directive等。我们来回顾一下它的结构(省略了部分属性)
{
  app: null,
  config: {
    globalProperties: {},
    optionMergeStrategies: {},
    compilerOptions: {},
  },
  mixins: [],
  components: {}, 
  directives: {}, 
};
  • next:组件更新有两种形式,一种是组件内部的状态发生了改变,引起了组件自身更新;另外一种是父组件传递了propsprops改变,父组件更新、导致子组件引起了更新。第一种nextnull。如果是第二种,那么需要给next赋值为当前子组件最新的VNode(虚拟节点)。而next将会用于更新组件的props以及slots等属性。组件自身状态改变引起的更新是不需要更新props的。
  • subTree: 调用render函数之后返回的组件要渲染的虚拟根节点,可以通过这个属性读取到组件需要渲染的所有虚拟节点。
  • effect: 在setupRenderEffect中创建的reactiveEffect,我们在Vue3源码分析(2)中讲解了reactiveEffect,不了解的可以在阅读一下,目前简单理解为在调用render函数的时候收集依赖,如果响应式的值发生了改变会重新调用update函数执行更新流程。我们将会在讲解setupRenderEffect中详细讲解这一部分。
  • update: 组件更新函数,只要调用了这个函数,组件就会强制更新。同时响应式发生改变调用的也是这个函数。
  • render: 渲染函数。可以通过编译得到,也可以通过写template属性获得,也可以通过自己写render函数。
  • exposed: 组件指定的暴露到外部的属性。
  • provides: 用户在组件中设置了provide,子代组件可以通过inject收到传递的值,provides:parent ? parent.provides : Object.create(appContext.provides)通过这段代码分析我们可以知道,provides中至少含有全局提供的provides,如果当前组件提供了provide,后面会将其混合,并且继承父组件provides,这也就解释了为什么provides可以向下传递,因为每一层都可以收到本组件的provides父组件的provides并进行合并
  • components: 如果你想在template中使用组件,需要在这里注册,对于使用了

你可能感兴趣的:(Vue3组件挂载之创建组件实例详解)