likes
comments
collection
share

[Vue 源码] Vue 3.2 - 组件更新原理

作者站长头像
站长
· 阅读数 17

代码运行结果

[Vue 源码] Vue 3.2 - 组件更新原理

代码示例

  <body>
    <script src="../dist/runtime-dom.global.js"></script>
    <div id="app"></div>
    <script>
      const { createApp, h, reactive } = VueRuntimeDOM;

      const Son = {
        props: {
          name: String,
        },
        render() {
          return h("p", this.name);
        },
      };

      const App = {
        setup() {
          const state = reactive({ name: "zf" });
          setTimeout(() => {
            state.name = "jw";
          }, 3000);

          return () => {
            return h(Son, { name: state.name });
          };
        },
      };

      createApp(App).mount("#app");
    </script>
  </body>

挂载阶段

第一步,执行第一句代码,createApp(App).mount("#app"); 进行组件挂载。可以查看之前的文章,来看详细流程。

我们这里只聊聊组件挂载的核心,详细挂载请看: Vue3 组件挂载原理,执行 setup 函数。

  • 对于 App 组件来讲,挂载时的核心就是会创建组件的 ReactiveEffect 对象。
  • 每次更新和挂载都会调度 instance.update() 也就是 componentUpdateFn 函数。
  • 并且将将返回的函数作为 render 函数,进行调用得到组件的虚拟 Dom 节点,来作为 instance.subtree 属性。
  • Son 组件同理。

第二步:render 函数中 state.name 触发,触发 state 响应式对象的的 getter 操作,收集 组件的 ReaciveEffect 对象作为响应式对象的依赖。如下结构:


{
    { name: "zf" }: {
        name : [组件的 ReaciveEffect, 其他的 ReaciveEffect(比如 watch)]
     }
}

挂载核心操作完毕。

更新阶段

执行第一句代码:state.name = "jw"

第一:state.name = "jw", 原理可以看这篇文章:Vue 3.2 - Reactive 原理,这里几句带过,触发响应式对象 state 的 setter 操作,通过 Reflect.set 方法更新属性,然后触发 trigger 操作,执行 name 属性的 RectiveEffects 对象的 fn 属性,也就是 componentUpdateFn 函数。

第二:在 componentUpdateFn 函数中,调用 renderComponentRoot 函数,该函数中,再次调用 instance.render.call(), 获得新的虚拟 Dom, 再去将旧的和新的虚拟 Dom 进行 patch。

    const nextTree = renderComponentRoot(instance)

    patch(
      prevTree,
      nextTree,
      // parent may have changed if it's in a teleport
      hostParentNode(prevTree.el!)!,
      // anchor may have changed if it's in a fragment
      getNextHostNode(prevTree),
      instance,
      parentSuspense,
      isSVG
    )

第三:由于本次是更新逻辑,所以来到了 updateComponent 方法,在 updateComponent 调用 Son 组件 instace.update(),也就是 componentUpdateFn 函数。 在该函数中调用了 Son 组件 instance.render 方法,拿到最新的虚拟 Dom 进行渲染。

    if (shouldUpdateComponent(n1, n2, optimized)) {
        instance.next = n2
        invalidateJob(instance.update)
        instance.update()
      }

this 访问的时候,实际上是对 instance 上属性的代理。这里是可以代理的属性,请大家自行参考。

[Vue 源码] Vue 3.2 - 组件更新原理