0
点赞
收藏
分享

微信扫一扫

vue3生命周期源码详解

在这里插入图片描述

钩子函数的特点

源码中如何注册、实现钩子函数?

代码示例

const onBeforeMount = createHook('bm' /* BEFORE_MOUNT */)
const onMounted = createHook('m' /* MOUNTED */)
const onBeforeUpdate = createHook('bu' /* BEFORE_UPDATE */)
const onUpdated = createHook('u' /* UPDATED */)
const onBeforeUnmount = createHook('bum' /* BEFORE_UNMOUNT */)
const onUnmounted = createHook('um' /* UNMOUNTED */)
const onRenderTriggered = createHook('rtg' /* RENDER_TRIGGERED */)
const onRenderTracked = createHook('rtc' /* RENDER_TRACKED */)
const onErrorCaptured = (hook, target = currentInstance) => {
  injectHook('ec' /* ERROR_CAPTURED */, hook, target)
}

除去onErrorCaptured其他的钩子函数都是直接调用createHook。

createHook方法源码解析

 const createHook = function(lifecycle)  {
  return function (hook, target = currentInstance) {
    injectHook(lifecycle, hook, target)
  }
}

柯里化的封装过程带来的优势

  1. 提高函数的复用性:可以将原本接收多个参数的函数转变为接收部分参数的函数。这样,我们可以复用具有相同生命周期的钩子函数的逻辑,只需传入不同的目标实例即可。这种封装方式使得代码更加灵活,易于扩展和维护。

  2. 简化函数调用:可以将原本需要在调用时传递的参数提前传入并固定,从而简化函数调用。在这个例子中,createHook 函数返回的函数只需要传递 hook 和可选的 target 参数即可,无需再重复传递 lifecycle 参数。

  3. 隐藏实现细节:可以隐藏一些实现细节,使得函数调用更加简洁,同时也减少了外部对内部函数的直接访问。这种封装方式可以提高代码的可维护性和安全性

injectHook方法源码解析

function injectHook(type, hook, target = currentInstance, prepend = false) {
  // 从目标组件实例中获取相应的钩子函数数组
  const hooks = target[type] || (target[type] = [])
  // 封装传入的钩子函数,用于添加实例检查、错误处理等逻辑
  const wrappedHook = hook.__weh || 
    (hook.__weh = (...args) => {
      // 检查目标组件是否已卸载
      if (target.isUnmounted) {
        return
      }
      // 停止依赖收集,避免执行钩子时产生副作用
      pauseTracking()
      // 设置当前运行组件实例为目标实例  
      setCurrentInstance(target)
      // 调用传入的钩子函数
      const res = callWithAsyncErrorHandling(hook, target, type, args)
      // 重置当前组件实例
      setCurrentInstance(null)
      // 恢复依赖收集
      resetTracking()
      return res
    })
  // 根据传入的参数将封装后的钩子推入数组前或后
  if (prepend) {
    hooks.unshift(wrappedHook) 
  } else {
    hooks.push(wrappedHook)
  }
}

举例onBeforeMount 和 onMounted

const setupRenderEffect = (instance, initialVNode, container, anchor, parentSuspense, isSVG, optimized) => {
  // 创建响应式的副作用渲染函数
  instance.update = effect(function componentEffect() { 
    if (!instance.isMounted) {
      // 获取组件注册的钩子函数
      const { bm, m } = instance;
      // 渲染组件生成子树vnode
      const subTree = (instance.subTree = renderComponentRoot(instance))
      // 执行beforeMount钩子函数
      if (bm) {
        invokeArrayFns(bm) 
      }
      // 将子树vnode挂载到container上
      patch(null, subTree, container, anchor, instance, parentSuspense, isSVG)
      // 保留渲染生成的子树根DOM节点
      initialVNode.el = subTree.el;
      // 执行mounted钩子函数  
      if (m) {
        queuePostRenderEffect(m, parentSuspense)
      }
      // 标记已挂载
      instance.isMounted = true;
    } else {
      // 更新组件
    }
  }, prodEffectOptions)
} 
举报

相关推荐

0 条评论