Hello, I’m Jian Darui.

This is the fourth source code analysis, this article, we mainly do a brief review of the Vue life cycle, mainly on the life cycle of the source code analysis.

A simple review

The lifecycle functions of Vue3 and Vue2 are no different in terms of the way they are called, the sequence of triggering between parent and child components, etc. Here we only mention some key points:

  • Vue3Provides compositing for the lifecycleAPI, which allows us to call directly from the setup function
  • Vue3Did not providebeforeCreate,createdCombinatorial functions of the life cycle can be usedsetupThe delta function replaces these two combinationsAPI
  • Vue3Can also be likeVue2Configure the lifecycle functions through component configuration items as well
import { onMounted, onUpdated, onUnmounted } from 'vue'

const MyComponent = {
  setup() {
    onMounted(() = > {
      console.log('mounted! ')
    })
    onUpdated(() = > {
      console.log('updated! ')
    })
    onUnmounted(() = > {
      console.log('unmounted! ')}}}Copy the code

Because setup runs around beforeCreate and Created lifecycle hooks, there is no need to explicitly define them. In other words, any code written in these hooks should be written directly in the Setup function. — quoted from official documentation

  • Vue3A newrenderTracked/onRenderTracked,renderTriggered/onRenderTriggeredThe life cycleAPI
    • renderTrackedUsed to tell you which action tracks the component and the target object and key for that action.
    • onRenderTriggeredThis tells you what action triggered the rerender and the target object and key for that action.
<div id="app">
  <button v-on:click="addToCart">Add to cart</button>
  <p>Cart({{ cart }})</p>
</div>
<script>
const app = createApp({
  data() {
    return {
      cart: 0}},renderTracked({ key, target, type }) {
    console.log({ key, target, type })
    /* 👉 This will be recorded when the component is first rendered: {key: "cart", target: {cart: 0}, type: "get"} */
  },
  renderTriggered({ key, target, type }) {
    console.log({ key, target, type })
  },
  methods: {
    addToCart() {
      this.cart += 1
      /* 👉 This will cause renderTriggered {key: "cart", target: {cart: 1}, type: "set"} */
    }
  }
})

app.mount('#app')
</script>
Copy the code
  • Parent component completionmountedPhase, andDon’tEnsure that all subcomponents are mounted. If you want to wait for the entire view to be rendered, you can do so in themountedInternal usevm.$nextTick
  • Again, the parent component completesupdatedPhase,Don’tEnsure that all child components are rerendered as well. If you want to wait for the entire view to be rendered, you can do so in theupdatedInternal usevm.$nextTick.
  • This is with usIn the previousSpeak of thenextTickPrinciple related, read the small partner can think, look at the source, I believe you must have harvest.
  • It has to do with the updating principle of componentsupdateDelta function is also a functioneffect. This is acomponent EffectWhich will bring us in touch with the third levelEffectFunction, which will be explained later when we analyze the renderer. Let’s dig a hole here.

Analysis of the

A component lifecycle hook is an opportunity exposed to the user at various stages of a component’s creation, initialization, data response, template compilation, template mounting, data update, component uninstallation, and so on.

The start/end of each critical phase is actually a process responsible for the start/end of the execution of the function at that stage. In Vue, lifecycle functions are mainly mounted on component instances, and when the lifecycle corresponding to the current phase needs to be executed, all lifecycle hooks are directly fetched from the instance and executed again.

Here we first analyze the Vue lifecycle related source code, about each lifecycle in the specific execution process and call process, will be in our subsequent in-depth process will be explained to.

Look at the source code directly below.

Vue3 uses createHook to create lifecycle functions. The createHook function is a higher-order function.

// 👉 keep-alive component lifecycle
export { onActivated, onDeactivated } from './components/KeepAlive'

export const onBeforeMount = createHook(LifecycleHooks.BEFORE_MOUNT)
export const onMounted = createHook(LifecycleHooks.MOUNTED)
export const onBeforeUpdate = createHook(LifecycleHooks.BEFORE_UPDATE)
export const onUpdated = createHook(LifecycleHooks.UPDATED)
export const onBeforeUnmount = createHook(LifecycleHooks.BEFORE_UNMOUNT)
export const onUnmounted = createHook(LifecycleHooks.UNMOUNTED)
export const onServerPrefetch = createHook(LifecycleHooks.SERVER_PREFETCH)

export const onRenderTriggered = createHook(
  LifecycleHooks.RENDER_TRIGGERED
)
export const onRenderTracked = createHook(
  LifecycleHooks.RENDER_TRACKED
)
Copy the code

CreateHook function code:

export const createHook = (
  lifecycle
) = > (hook, target) = >(! isInSSRComponentSetup || lifecycle === LifecycleHooks.SERVER_PREFETCH) && injectHook(lifecycle, hook, target)Copy the code

InjectHook (); createHook (); injectHook (); createHook ();

InjectHook code:

function injectHook(type, hook, target = currentInstance, prepend = false) {
      if (target) {
          // 👉 gets the hook function of type type on target
          // 👉 can be multiple, if multiple, it is an array type
          const hooks = target[type] || (target[type] = []);
          // cache the error handling wrapper for injected hooks so the same hook
          // can be properly deduped by the scheduler. "__weh" stands for "with error
          // handling".
          // 👉 provides a layer of error-handling for registered hook functions
          const wrappedHook = hook.__weh ||
              (hook.__weh = (. args) = > {
                  if (target.isUnmounted) {
                      return;
                  }
                  
                  // 👉 disable all traces within lifecycle hooks because they may be invoked by internal side effects.
                  // 👉 such as state access and modification during the life cycle.
                  pauseTracking();
                  // 👉 sets currentInstance during the hook call.
               
                  // 👉 sets the current render instance
                  setCurrentInstance(target);
                  / / callWithAsyncErrorHandling function is responsible for calling the hooks, if execution error will be warned
                  const res = callWithAsyncErrorHandling(hook, target, type, args);
                  // 👉 the hook completes and resets the current instance
                  setCurrentInstance(null);
                  // 👉 reset Track
                  resetTracking();
                  // 👉 returns the result
                  return res;
              });
          // 👉 adds wrapped hook functions to hooks
          // 👉 Note: With this change, hooks also change target[type].
          if (prepend) {
              hooks.unshift(wrappedHook);
          } else {
              hooks.push(wrappedHook);
          }
          // 👉 returns the wrapped hook function
          return wrappedHook;
      } else {
          👉 error handling
          const apiName = toHandlerKey(ErrorTypeStrings[type].replace(/ hook$/.' '));
          warn('omit... '); }}Copy the code

InjectHook: injectHook: injectHook: injectHook: injectHook

  • The purpose of wrapping the user’s hook function is to alert the user when the hook function fails
  • Adds the wrapped hook function to the current instance’s array of lifecycle hook functions of the same type
  • When hook functions need to be executed, they are retrieved from the component instance for all phases and executed

conclusion

CreateHook (createHook, createHook, createHook); createHook (createHook, createHook, createHook); createHook (createHook, createHook, createHook, createHook); When called by the user, the function passed in by the user is wrapped in an error layer, mainly for exception reporting purposes, and then the wrappedHook is placed in a hooks array. When the appropriate phase is reached, the corresponding lifecycle hooks are retrieved from the instance and the execution is iterated.

If the component of subcomponents or multilayer components, in the parent component mounted | | after updated, does not represent a child components can be mounted | | updated end, because have to wait in the scheduler task is done, to get the latest DOM. Resolve returned by the scheduler at the end of execution is also nextTick’s promise.resolve

Finally very (Bu zhen (Yao) cheng (Lian) recommended my public number: coder Rhapsody. Your attention is the biggest encouragement to my creation.