从 vue 源码看问题 —— 你知道 Hook Event 吗?

前言

在之前的几篇文章中,都有提到 vue 中调用生命周期钩子时是通过 callHook() 方法进行调用的,比如在初始化篇章中调用 beforeCreatecreated 生命周期钩子方式如下:

在这里插入图片描述

那么接下来一起来了解下到底什么是 Hook Event

Hook Event

是什么?

Hook Eventvue 的官方文档中并没有提及到,这也是很多人很少或者几乎没有使用到 Event Hook 的主要原因,但这不代表不可以使用.

Vue 中提供了对应的生命周期钩子,方便开发者在特定的时间节点进行对应的逻辑处理,比如:在组件的 mounted 生命周期钩子中,准备组件渲染所需要的数据.

Hook Event 是通过 Vue自定义事件 + 生命周期钩子 的方式,实现从组件外部为组件注入额外生命周期方法的功能.

怎么用?

这里直接通过一个简单的例子来快速了解 Hook Event 的使用.

首先通过 Vue.component() 注册了 ListView 主要用于列表渲染,在其 mounted 生命周期钩子中通过 setTimeout() 模仿接口请求,并在模板中进行使用 <list-view @hook:mounted="mountedAction"></list-view>,接着通过 @hook:mounted="mountedAction" 向列表组件组件中注入了外部定义的 mounted 钩子中需要执行的事件,此时控制台上的输出结果为:

在这里插入图片描述

这就证明了 Hook Event 就是通过 自定义事件 + 生命周期钩子 的方式,实现从组件外部为组件注入额外生命周期方法.

具体代码如下:

    // HTML 模板
    <div id="app">
     <list-view @hook:mounted="mountedAction"></list-view>
    </div>
    
    // JS 逻辑 
    <script>
      // 第三方 vue 组件
      Vue.component('ListView', {
        template:`<ul class="list-view">
          <li v-for="item in list">{{ item }}</li>
          </ul>`,
        data(){
          return {
            list: []
          }
        },
        mounted(){
          console.log("mounted in ListView...");
          setTimeout(()=>{
            this.list = [1,2,3,4,5,6];
          },1000);
        }
      });

      new Vue({
        el: '#app',
        methods:{
          mountedAction(){
            console.log("mountedAction from parent...");
          }
        }
      });
    </script>

callHook() 方法

文件位置:src\core\instance\lifecycle.js

callHook 中通过 vm._hasHookEvent 标识判断是否存在 Hook Event,而这个标识是在 Vue.prototype.$on 实例方法中进行定义的,详情可看 eventsMixin(Vue) 方法.

export function callHook (vm: Component, hook: string) {
  // #7573 disable dep collection when invoking lifecycle hooks
  
  // 调用前打开依赖收集 
  pushTarget()

  // 从组件配置项中获取对应的生命周期钩子,类型为数组
  const handlers = vm.$options[hook]

  const info = `${hook} hook`

  if (handlers) {
    for (let i = 0, j = handlers.length; i < j; i++) {
      // 通过 apply 或 call 的方式调用生命周期函数
      invokeWithErrorHandling(handlers[i], vm, null, vm, info)
    }
  }

  
  // 如果有 HookEvent 事件传入,则通过 $emit 调用这个事件,如 hook:mounted
  if (vm._hasHookEvent) {
  // 本质就是执行 vm._events['hook:mounted'] 数组中的所有处理函数
    vm.$emit('hook:' + hook)
  }

  // 调用完成后关闭依赖收集
  popTarget()
}

invokeWithErrorHandling() 方法

文件位置:src\core\util\error.js

/*
  1. 使用 try catch 包裹生命周期钩子中的逻辑,便于进行异常捕获
  2. 调用生命周钩子:有 args 参数通过 apply 调用,否则通过 call 调用
  3. 返回调用结果
*/
export function invokeWithErrorHandling (
  handler: Function,
  context: any,
  args: null | any[],
  vm: any,
  info: string
) {
  let res
  try {
    res = args ? handler.apply(context, args) : handler.call(context)
    if (res && !res._isVue && isPromise(res) && !res._handled) {
      res.catch(e => handleError(e, vm, info + ` (Promise/async)`))
      // issue #9511
      // 避免在嵌套调用时多次触发 catch
      res._handled = true
    }
  } catch (e) {
    // 异常处理
    handleError(e, vm, info)
  }
  return res
}

总结

Hook Event 原理是什么?
首先 Hook Event 的作用实现从组件外部为组件注入额外生命周期方法的功能,主要是通过 自定义事件 + 生命周期钩子 的方式实现的.

  • vue 在处理组件如 <comp @hook:lifecycle="customMethod" /> 时,会将这个事件通过 vm.$on() 即 Vue.prototype.$on() 方法进行监听处理,并且遇到格式为 hook:xx 的事件时,会将 vm._hasHookEvent 置为 true,表示该组件有 Hook Event
  • 在通过 callHook() 方法调用生命周期钩子时,会以循环的方式执行组件上对应的生命周期钩子
  • 在执行完后组件上的生命周期钩子后,会通过 vm._hasHookEvent 标识判断当前组件是否存在 Hook Event,如果存在就通过 vm.$emit('hook:xxx') 即 Vue.prototype.$emit() 方法调用 vm._events['hook:xxx'] 事件上的所有处理函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值