Interpretation of Vue source code -- Hook Event

When learning becomes a habit, knowledge becomes common sense. Thank you for your attention, likes, collections and comments.

The new video and articles will be sent to WeChat official account for the first time. Li Yongning

The article has been included in github warehouse liyongning/blog , welcome to Watch and Star.

preface

Hook Event (Hook Event) I believe many Vue developers have never used or even heard of it. After all, it is not mentioned in Vue official documents.

Vue provides some life cycle hook functions for developers to add additional processing logic at specific logic points. For example, two life cycle hooks, beforeMount and mounted, are provided in the component mounting phase for developers to perform additional logic processing in the component mounting phase, such as preparing the data required for component rendering.

What is the relationship between Hook Event, which also means hook, and Vue's life cycle hook function? What's the use of it? This is the question to be answered in this article.

target

  • Understand what Hook Event is? Understand its usage scenarios
  • Deeply understand the implementation principle of Hook Event

What is a Hook Event?

Hook Event is a function implemented by Vue's custom event combined with life cycle hook to inject additional life cycle methods into components from outside.

Usage scenario

Suppose there is such a third party business component now, and the logic is very simple. In the mounted lifecycle, the interface is called to get the data, and then the data is rendered to the page.

<template>
  <div class="wrapper">
    <ul>
      <li v-for="item in arr" :key="JSON.stringify(item)">
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      arr: []
    }
  },
  async mounted() {
    // Call the interface to get the data rendered by the component
    const { data: { data } } = await this.$axios.get('/api/getList')
    this.arr.push(...data)
  }
}
</script>

Then it is found that there are some defects in this component in use. For example, the interface waiting time may be long. I want to output a loading on the console when the mounted life cycle starts to execute String to enhance the user experience.

How can this requirement be realized?

There are two ways: the first is more troublesome, modify the source code; The second method is much simpler, that is, the Hook Event we introduced today, which injects additional life cycle methods into components from outside.

<template>
  <div class="wrapper">
    <comp @hook:mounted="hookMounted" />
  </div>
</template>

<script>
// This is the third-party business component above
import Comp from '@/components/Comp.vue'

export default {
  components: {
    Comp
  },
  methods: {
    hookMounted() {
      console.log('loading ...')
    }
  }
}
</script>

At this time, when you refresh the page, you will find that when the business component requests data, it will output a loading on the console character string.

effect

What does Hook Event do?

Hook Event allows you to inject additional lifecycle methods into components from outside the component.

Implementation principle

After knowing the usage scenario and function of Hook Event, we will find its implementation principle from the source code to "know its nature and why".

As mentioned earlier, Hook Event is a function realized by Vue's custom events combined with the life cycle hook function, so let's look at the code related to the life cycle. For example, we know that Vue's life cycle function is executed through a method called callHook

callHook

/src/core/instance/lifecycle.js

/**
 * callHook(vm, 'mounted')
 * Executes the lifecycle hook function specified by the instance
 * If the instance is set with a corresponding Hook Event, such as: < comp @ hook: mounted = "method" / >, the execution of this event will be triggered after the life cycle function is executed
 * @param {*} vm Component instance
 * @param {*} hook Lifecycle hook function
 */
export function callHook (vm: Component, hook: string) {
  // Disable dependency collection during lifecycle hook function execution
  // #7573 disable dep collection when invoking lifecycle hooks
  pushTarget()
  // Get the specified hook function from the instance configuration object, such as mounted
  const handlers = vm.$options[hook]
  // mounted hook
  const info = `${hook} hook`
  if (handlers) {
    // The lifecycle hook is executed through invokeWithErrorHandler
    for (let i = 0, j = handlers.length; i < j; i++) {
      invokeWithErrorHandling(handlers[i], vm, null, vm, info)
    }
  }
  // Hook Event. If a Hook Event is set, such as < comp @ hook: mounted = "method" / >, the event will be triggered through $emit
  // vm._hasHookEvent identifies whether the component has a hook event, which is in VM$ Set when processing component custom events in on
  if (vm._hasHookEvent) {
    // vm.$emit('hook:mounted')
    vm.$emit('hook:' + hook)
  }
  // Turn off dependency collection
  popTarget()
}

invokeWithErrorHandling

/src/core/util/error.js

/**
 * General function, execute the specified function handler
 * The function passed in will be wrapped with try catch for exception capture
 */
export function invokeWithErrorHandling (
  handler: Function,
  context: any,
  args: null | any[],
  vm: any,
  info: string
) {
  let res
  try {
    // Execute the function handler passed in and return the execution result
    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
      // avoid catch triggering multiple times when nested calls
      res._handled = true
    }
  } catch (e) {
    handleError(e, vm, info)
  }
  return res
}

vm.$on

/src/core/instance/events.js

/**
 * Listen for custom events on the instance, VM_ event = { eventName: [fn1, ...], ... }
 * @param {*} event A single event name or an array of multiple event names
 * @param {*} fn Callback function executed when event is triggered
 * @returns 
 */
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
  const vm: Component = this
  if (Array.isArray(event)) {
    // Event is an array composed of multiple event names, then traverse these events and call $on recursively in turn
    for (let i = 0, l = event.length; i < l; i++) {
      vm.$on(event[i], fn)
    }
  } else {
    // Store the registered events and callbacks in the form of key value pairs in VM_ VM. In event object_ event = { eventName: [fn1, ...] }
    (vm._events[event] || (vm._events[event] = [])).push(fn)
    // hookEvent, which provides an opportunity to inject the declaration cycle method into the component instance from the outside
    // For example, inject additional logic into the mounted method of the component from outside the component
    // This capability is implemented in combination with the callhook method
    if (hookRE.test(event)) {
      vm._hasHookEvent = true
    }
  }
  return vm
}

summary

  • The interviewer asked: what is Hook Event?

    Answer:

    Hook Event is a function implemented by Vue's custom event combined with life cycle hook to inject additional life cycle methods into components from outside.

  • The interviewer asked: if Hook Event is implemented?

    Answer:

    <comp @hook:lifecycleMethod="method" />
    • When processing component custom events (vm.$on), if the component is found to have events in hook:xx format (xx is the life cycle function of Vue), VM_ Setting hashookevent to true indicates that the component has a Hook Event
    • When the component life cycle method is triggered, these life cycle functions will be executed internally through the callHook method. After the life cycle function is executed, if VM_ If hashookevent is true, it means that the current component has a Hook Event through VM$ Emit ('hook: XX ') triggers the execution of Hook Event

    This is the implementation principle of Hook Event.

link

Thank you for your attention, likes, collections and comments. See you next time.

When learning becomes a habit, knowledge becomes common sense. Thank you for your attention, likes, collections and comments.

The new video and articles will be sent to WeChat official account for the first time. Li Yongning

The article has been included in github warehouse liyongning/blog , welcome to Watch and Star.

Keywords: Javascript Front-end ECMAScript TypeScript Vue.js

Added by Arenlor on Tue, 01 Mar 2022 02:23:42 +0200