vue3生命周期源码浅析

5 篇文章 0 订阅
3 篇文章 0 订阅

概述

生命周期在Vue3中是一个很重要的概念,它允许我们在特定的时间点执行一些代码,比如在组件创建、更新和销毁时执行一些操作。

生命周期函数分为以下几种:

  • 1.onBeforeMount: 在组件挂载之前执行
  • 2.onMounted: 在组件挂载之后执行
  • 3.onBeforeUpdate: 在组件更新之前执行
  • 4.onUpdated: 在组件更新之后执行
  • 5.onBeforeUnmount: 在组件销毁之前执行
  • 6.onUnmounted: 在组件销毁之后执行
  • 7.onServerPrefetch: 在服务器端渲染时执行
  • 8.onRenderTracked: 在组件渲染时执行
  • 9.onRenderTriggered: 在组件渲染时执行
  • 10.onErrorCaptured: 在组件发生错误时执行

Vue3的生命周期运行示意图如下
在这里插入图片描述

源码分析

Vue3中的上述这些生命周期钩子函数的实现大同小异,主要都是基于hook函数实现,在不同的时机执行对应的·hook·,其主要实现位于packages\runtime-core\dist\runtime-core.cjs.js

const createHook =
  (lifecycle) =>
  (hook, target = currentInstance) => {
    if (!isInSSRComponentSetup || lifecycle === "sp") {
      injectHook(lifecycle, (...args) => hook(...args), target);
    }
  };

lifecycleonBeforeMount等钩子函数名称,如下枚举:,hook就是钩子函数中的回调函数,target为当前组件实例

export enum LifecycleHooks {
  BEFORE_CREATE = 'bc',
  CREATED = 'c',
  BEFORE_MOUNT = 'bm',
  MOUNTED = 'm',
  BEFORE_UPDATE = 'bu',
  UPDATED = 'u',
  BEFORE_UNMOUNT = 'bum',
  UNMOUNTED = 'um',
  DEACTIVATED = 'da',
  ACTIVATED = 'a',
  RENDER_TRIGGERED = 'rtg',
  RENDER_TRACKED = 'rtc',
  ERROR_CAPTURED = 'ec',
  SERVER_PREFETCH = 'sp',
}

在调用injectHook时,会将同类型的hook挂载到组件实例中,hook会被包装一层,用于处理错误捕获和跟踪,其中也用了缓存的策略,具体实现如下:

function injectHook(type, hook, target = currentInstance, prepend = false) {
  if (target) {
    const hooks = target[type] || (target[type] = []);
    const wrappedHook =
      hook.__weh ||
      (hook.__weh = (...args) => {
        if (target.isUnmounted) {
          return;
        }
        reactivity.pauseTracking(); // 暂停依赖追踪
        const reset = setCurrentInstance(target);
        const res = callWithAsyncErrorHandling(hook, target, type, args);
        reset();
        reactivity.resetTracking(); // 恢复依赖追踪
        return res;
      });
    if (prepend) {
      // 如果是前置钩子,则插入到数组的开头
      hooks.unshift(wrappedHook);
    } else {
      hooks.push(wrappedHook);
    }
    return wrappedHook;
  }
}

注册过程

上面阐述了hook的创建,接下来看下生命周期钩子函数的注册,函数注册后,才方便vue3在适当的时机调用。vue3自定义了一个registerLifecycleHook函数,用于注册生命周期钩子函数。如下所示:

function registerLifecycleHook(register, hook) {
  if (isArray(hook)) {
    hook.forEach((_hook) => register(_hook.bind(publicThis)));
  } else if (hook) {
    register(hook.bind(publicThis));
  }
}
registerLifecycleHook(onBeforeMount, beforeMount);
registerLifecycleHook(onMounted, mounted);
registerLifecycleHook(onBeforeUpdate, beforeUpdate);
registerLifecycleHook(onUpdated, updated);
registerLifecycleHook(onActivated, activated);
registerLifecycleHook(onDeactivated, deactivated);
registerLifecycleHook(onErrorCaptured, errorCaptured);
registerLifecycleHook(onRenderTracked, renderTracked);
registerLifecycleHook(onRenderTriggered, renderTriggered);
registerLifecycleHook(onBeforeUnmount, beforeUnmount);
registerLifecycleHook(onUnmounted, unmounted);
registerLifecycleHook(onServerPrefetch, serverPrefetch); // ssr内容,后面不会讲

这段代码还将钩子函数与publicThis绑定,并将它们传递给registerLifecycleHook函数。publickThis是就是当前instanceproxy对象,用于存储当前实例的变量

instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers);

生命周期函数函数经历的阶段

vue3是在组件初始化时进行钩子函数的注册,主要经历了这些阶段:

render=>patch => processComponent => mountComponent => setupComponent => setupStatefulComponent => handleSetupResult=>finishComponentSetup => applyOptions=> registerLifecycleHook

各个阶段的作用如下:

  • render:Vue组件中定义的渲染函数,它返回一个虚拟 DOM 树描述;定义了组件的 UI 结构,它可以是模板语法或者render函数本身,用于生成组件的虚拟 DOM。
  • patchVue内部的核心函数,负责将虚拟 DOM 渲染成真实 DOM,并处理 DOM 的更新和变更;实现了 Vue 的响应式更新机制,确保虚拟 DOM 的变更能够高效地同步到真实 DOM 上,实现数据驱动的视图更新。
  • processComponent:处理组件的函数,可能包括组件的初始化、更新等过程。在组件的生命周期中,processComponent 负责管理组件的各个阶段,例如数据的准备、事件的绑定等,确保组件能够正常工作并响应数据变化。
  • mountComponent:挂载组件到 DOM 上的过程,包括创建组件实例、初始化数据等。mountComponent 是组件生命周期中的重要阶段,它负责将组件实例化并将其挂载到页面的 DOM 元素上,使得组件能够被用户访问和操作。
  • setupComponent:设置组件的函数,可能包括组件的配置项、参数处理等。setupComponent 阶段用于初始化组件的配置和参数,为后续的生命周期钩子、事件处理和数据绑定等操作做准备。
  • setupStatefulComponent:设置有状态组件的函数,用于处理有状态组件的状态初始化和管理。对于有状态的组件(即有响应式数据的组件),setupStatefulComponent负责初始化组件的状态数据,并确保这些数据的响应式更新机制能够正常运作。
  • handleSetupResult:处理设置结果的函数,可能包括对组件状态和属性的初始化。主要用于处理setup()函数的返回结果,确保组件的状态、计算属性等被正确地初始化和配置。
  • finishComponentSetup:完成组件设置的函数,确保组件已经初始化并准备好渲染。在组件初始化阶段的最后,finishComponentSetup确保所有的组件设置和初始化工作已经完成,组件可以安全地开始渲染和更新。
  • applyOptions:应用组件选项的函数,包括组件的配置项、生命周期钩子等。applyOptions 用于将组件定义的选项(如 data、methods、computed 等)应用到组件实例上,以及注册和管理组件的生命周期钩子函数,确保组件在各个生命周期阶段能够执行相应的逻辑。

生命周期钩子函数的调用时机

onBeforeMount

mountComponent阶段,会调用setupRenderEffect函数,这个函数顾名思义就是设置组件的effect,用于设置和管理组件的渲染效果,实现vue3组件的响应式更新机制,确保当组件的响应式数据发生变化时,能够自动重新渲染组件的视图
setupRenderEffect函数内部会new ReactiveEffect实例,并赋值给instance.effect,并调用componentUpdateFn函数执行。而在componentUpdateFn中会先判断组件是否已经被挂载,如果已经挂载,则处理更新逻辑,否则进行挂载逻辑。
当组件未被挂载时,instance.isMountedfalseVue3会从instance中获取注册钩子函数bm,若它存在,在调用invokeArrayFns进行遍历调用。

onMounted

onMounted钩子函数同onBeforeMount一样,在组件未被挂载时,从instance中解构注册的钩子函数m,通过queuePostRenderEffect进入调度器schedule,在组件挂载完成时调用flushPostFlushCbs,其内部会根据每一个jobid进行排序,然后依次执行。

onBeforeUpdate

onBeforeUpdate钩子函数和onBeforeMount函数类似,不同的是onBeforeUpdate是在组件被挂载后即instance.isMountedtrue时,在组件发生的响应式数据变化时,解构钩子函数bu,被调用

onUpdated

onUpdated钩子函数和onMounted函数类似,不同的是onUpdated是在组件被挂载后即instance.isMountedtrue时,在组件发生的响应式数据变化时,解构钩子函数u,被调用

onBeforeUnmount

onBeforeUnmount在组件卸载前会调用,对应的注册的钩子函数变量名为bum,会在unmountComponent函数里调用。而unmountComponent函数会在函数unmount中判断shapeFlag,若其为组件类型则调用。通过invokeArrayFns进行遍历执行

onUnmounted

onUnmounted在组件卸载后调用,对应的注册的钩子函数变量名为um,组件在卸载时先调用onBeforeUnmount钩子,如果instance.update存在,则调用unmount方法,然后调用queuePostRenderEffectum加入到调度器中,在组件卸载完成后调用flushPostFlushCbs,执行um钩子函数。

  • 27
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jinuss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值