Vue生命周期函数调用详解

目录
  • 生命周期
  • Vue.mixin
  • 生命周期选项合并
  • 调用生命周期函数
  • 结语

生命周期

Vue为用户提供了许多生命周期钩子函数,可以让用户在组件运行的不同阶段书写自己的逻辑。

那么Vue内部到底是如何处理生命周期函数的呢?Vue的生命周期究竟是在代码运行的哪个阶段执行呢?本文将实现Vue生命周期相关代码的核心逻辑,从源码层面来理解生命周期。

Vue.mixin

在介绍生命周期之前,我们先来看下Vue.mixin

Vue.mixinVue的全局混合器,它影响Vue创建的每一个实例,会将mixin 中传入的配置项与组件实例化时的配置项按照一定规则进行合并。对于生命周期钩子函数,相同名字的生命周期将会合并到一个数组中,混合器中的钩子函数将会先于组件中的钩子函数放入到数组中。在特定时机时,从左到右执行数组中的每一个钩子函数。

<div id="app">
</div>
<script>
  // 生命周期:
  Vue.mixin({
    created () {
      console.log('global created');
    }
  });
  const vm = new Vue({
    el: '#app',
    data () {
    },
    created () {
      console.log('component created');
    }
  });
  // global created
  // component created
</script>

上述代码会先执行Vue.mixin中的created函数,然后再执行组件中的created函数。下面我们看下Vue.mixin是怎么实现。

// src/global-api/index.js
import mergeOptions from '../shared/merge-options';

export function initGlobalApi (Vue) {
  Vue.options = {};
  Vue.mixin = function (mixin) {
    this.options = mergeOptions(this.options, mixin);
  };
}

// src/index.js
function Vue (options) {
  this._init(options);
}

// 初始化全局api
initGlobalApi(Vue);
export default Vue;

scr/index.js中执行initGlobalApi方法,会为Vue添加optionsVue.mixin属性。

Vue.mixin会将调用该函数时传入的配置项与Vue.options中的选项进行合并:

Vue.options = {};
Vue.mixin = function (mixin) {
  // Vue.options = mergeOptions(Vue.options, mixin)
  this.options = mergeOptions(this.options, mixin);
};

Vue.options中会保存所有全局的配置项,如components,directives等。执行Vue.mixin之后,Vue.options会和Vue.mixin 中的选项进行合并,之后会在组件初始化时将其和组件实例化时传入的选项根据不同的合并策略进行合并,这样会根据最终合并后的全局选项和组件选项来创建Vue 实例:

// src/init.js
function initMixin (Vue) {
  Vue.prototype._init = function (options) {
    const vm = this;
    // 在初始化根组件时: vm.constructor.options -> Vue.options
    // 在初始化子组件时: vm.constructor.options -> Sub.options
    // 将局部选项和全局选项进行合并
    vm.$options = mergeOptions(vm.constructor.options, options);
    // some code ...
  };
  // some code ...
}

现在关键逻辑来到了mergeOptions,下面来介绍mergeOptions的整体编写思路以及生命周期的合并过程。

生命周期选项合并

mergeOptions函数完成了组件中选项合并的逻辑:

const strategies = {};

function defaultStrategy (parentVal, childVal) {
  return childVal === undefined ? parentVal : childVal;
}
const LIFECYCLE_HOOKS = [
  'beforeCreate',
  'created',
  'beforeMount',
  'mounted',
  'beforeUpdate',
  'updated',
  'beforeDestroy',
  'destroyed'
];
function mergeHook (parentVal, childVal) {
  if (parentVal) {
    if (childVal) {
      // concat可以拼接值和数组,但是相对于push来说,会返回拼接后新数组,不会改变原数组
      return parentVal.concat(childVal);
    }
    return parentVal;
  } else {
    return [childVal];
  }
}

LIFECYCLE_HOOKS.forEach(hook => {
  strategies[hook] = mergeHook;
});

function mergeOptions (parent, child) { // 将子选项和父选项合并
  const options = {};

  function mergeField (key) {
    const strategy = strategies[key] || defaultStrategy;
    options[key] = strategy(parent[key], child[key]);
  }

  for (const key in parent) {
    if (parent.hasOwnProperty(key)) {
      mergeField(key);
    }
  }
  for (const key in child) {
    if (child.hasOwnProperty(key) && !parent.hasOwnProperty(key)) {
      mergeField(key);
    }
  }
  return options;
}
export default mergeOptions;

对于不同的选项,Vue会采取不同的合并策略。也就是为strategies添加Vue的各个选项作为key,其对应的合并逻辑是一个函数,为strategies[key]的值。如果没有对应key 的话,会采用默认的合并策略defaultStrategy来处理默认的合并逻辑。

这样可以让我们不用再用if else来不停为每一个选项进行判断,使代码更加简洁。并且在之后如果需要添加新的合并策略时,只需要添加类似如下代码即可,更易于维护:

function mergeXXX (parentVal, childVal) {
  return result
}
strategies[xxx] = mergeXXX

对于生命周期,我们会将每个钩子函数都通过mergeHook合并为一个数组:

function mergeHook (parentVal, childVal) {
  if (parentVal) {
    if (childVal) {
      // concat可以拼接值和数组,但是相对于push来说,会返回拼接后新数组,不会改变原数组
      return parentVal.concat(childVal);
    }
    return parentVal;
  } else {
    return [childVal];
  }
}

Vue.mixin中提到例子的合并结果如下:

现在我们已经成功将生命周期处理成了数组,接下来便到了执行数组中的所有钩子函数的逻辑了。

调用生命周期函数

完成上述代码后,我们已经成功将所有合并后的生命周期放到了vm.$options中对应的生命周期数组中:

vm.$options = {
  created: [f1, f2, f3],
  mounted: [f4, f5, f6]
  // ...
}

想要执行某个生命周期函数,可以用它的名字从vm.$options找到其对应的函数执行。为了方便生命周期的调用,封装了一个callHook函数来帮我们做这些操作:

// src/lifecycle.js
export function callHook (vm, hook) {
  const handlers = vm.$options[hook];
  if (handlers) {
    handlers.forEach(handler => handler.call(vm));
  }
}

对于目前我们已经完成的代码,可以在如下位置添加生命周期钩子函数的调用:

此时,用户在使用时传入的beforeCreate,created,beforeMount,Mounted钩子函数就可以正确执行了。

  • beforeCreate:在组件初始化状态initState之前执行,此时不能访问props,methods,data,computed等实例上的属性
  • created:组件初始化状态后执行,此时props,methods,data等选项已经初始化完毕,可以通过实例来直接访问
  • beforeMount: 组件过载之前执行
  • mounted: 组件挂载之后执行,即使用实例上最新的data生成虚拟DOM,然后将虚拟DOM挂载到真实DOM之后执行。

结语

生命周期函数本质上就是我们在配置项中传入回调函数,Vue会将我们传入的配置项收集到数组中,然后在特定时机统一执行。

Vue的生命周期从定义到执行一共经历了如下几个步骤:

  • 在组件实例化时作为选项传入
  • 首先将Vue.mixin中传入的配置项和Vue.options中的生命周期函数合并为一个数组
  • 将组件实例化时传入的选项和Vue.options中的生命周期继续进行合并
  • 封装callHook函数,从vm.$options中找到指定生命周期函数对应的数组
  • 在特定时机执行特定的生命周期函数

到此这篇关于Vue生命周期函数调用详解的文章就介绍到这了,更多相关Vue生命周期内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

源码地址:传送门

(0)

相关推荐

  • vue2与vue3中生命周期执行顺序的区别说明

    目录 vue2与vue3中生命周期执行顺序区别 生命周期比较 简单例子说明 三种情况下的生命周期执行顺序 1.单页面下生命周期顺序 2.父子.兄弟组件的生命周期顺序 3.不同页面跳转时各页面生命周期的执行顺序 vue2与vue3中生命周期执行顺序区别 生命周期比较 vue2中执行顺序 beforeCreate=>created=>beforeMount =>mounted=>beforeUpdate =>updated=>beforeDestroy=>destro

  • vue生命周期beforeDestroy和destroyed调用方式

    目录 生命周期beforeDestroy和destroyed调用 情境一 情景二 beforeDestroy和destroyed生命周期函数的特点 生命周期beforeDestroy和destroyed调用 情境一 离开当前路由,会直接调用: 当前路由不使用 缓存,离开当前路由会直接调用 beforeDestroy 和 beforeDestroy 销毁 情景二 离开当前路由,不会直接调用,需要程序控制调用: 当前路由使用 缓存,离开当前路由不会直接调用 beforeDestroy 和 befor

  • 聊聊vue生命周期钩子函数有哪些,分别什么时候触发

    目录 vue生命周期钩子函数 以下为详解版 生命周期mounted和activated使用.踩坑 activated mounted 踩坑 vue生命周期钩子函数 vue生命周期即为一个组件从出生到死亡的一个完整周期 主要包括以下4个阶段:创建,挂载,更新,销毁 创建前:beforeCreate, 创建后:created 挂载前:beforeMount, 挂载后:mounted 更新前:beforeUpdate, 更新后:updated 销毁前:beforeDestroy, 销毁后:destro

  • vue的路由守卫和keep-alive后生命周期详解

    目录 Vue-Router懒加载 如何定义动态路由 param方式 query方式 Vue-Router导航守卫 全局路由钩子 router.beforeEach 路由独享守卫 组件内的守卫 beforeRouterEnter beforeRouteUpdate beforeRouteLeave Vue路由钩子在生命周期函数的体现 keep-alive 生命周期钩子 activated deactivated 触发钩子的完整顺序 总结 Vue-Router懒加载 1.箭头函数+import co

  • Vue中的生命周期介绍

    什么是vue的生命周期 Vue中的生命周期是指组件从创建到销毁的一系列过程.看下面这张官方文档的图: 从图片中可以看出Vue的整个生命周期包括8个状态,按照先后顺序分别为: beforeCreate Created beforeMount mounted beforeUpdate updated beforeDestroy destroyed Vue组件的生命周期共分为三个阶段,如下图所示: 创建阶段和销毁阶段在组件的生命周期中只会执行一次,而更新阶段会执行多次. 先看一下创建阶段完成的事情:

  • 详解Vue生命周期和MVVM框架

    目录 生命周期 与动态组件有关的两个特殊的钩子: 与组件异常捕获有关的一个钩子: 生命周期有哪些 生命周期 组件从开始到结束的全过程 创建阶段:beforeCreate.created挂载阶段:beforeMount.mounted 更新阶段:beforeUpdate.updated 组件从开始到结束的全过程 创建阶段:beforeCreate.created 挂载阶段:beforeMount.mounted 更新阶段:beforeUpdate.updated 销毁阶段:beforeDestro

  • Vue生命周期函数调用详解

    目录 生命周期 Vue.mixin 生命周期选项合并 调用生命周期函数 结语 生命周期 Vue为用户提供了许多生命周期钩子函数,可以让用户在组件运行的不同阶段书写自己的逻辑. 那么Vue内部到底是如何处理生命周期函数的呢?Vue的生命周期究竟是在代码运行的哪个阶段执行呢?本文将实现Vue生命周期相关代码的核心逻辑,从源码层面来理解生命周期. Vue.mixin 在介绍生命周期之前,我们先来看下Vue.mixin. Vue.mixin是Vue的全局混合器,它影响Vue创建的每一个实例,会将mixi

  • Vue生命周期区别详解

    生命周期分类 vue每个组件都是独立的,每个组件都有一个属于它的生命周期, 从一个组件创建.数据初始化.挂载.更新.销毁,这就是一个组件所谓的生命周期. 在组件中具体的方法有: beforeCreate created beforeMount mounted beforeUpdate updated beforeDestroy destroyed beforeCreate( 创建前 ) 在实例初始化之后,数据观测和事件配置之前被调用,此时组件的选项对象还未创建,el 和 data 并未初始化,因

  • Vue生命周期示例详解

    首先看看Vue文档里关于实例生命周期的解释图 那么下面我们来进行测试一下 <section id="app-8"> {{data}} </section> var myVue=new Vue({ el:"#app-8", data:{ data:"aaaaa", info:"nono" }, beforeCreate:function(){ console.log("创建前========&q

  • Java线程的生命周期的详解

    Java线程的生命周期的详解 对于多线程编程而言,理解线程的生命周期非常重要,本文就针对这一点进行讲解. 一.线程的状态 线程的存在有几种不同的状态,如下: New状态 Ready状态 Running状态 Dead状态 Non Runnable状态 1.New状态 New状态是线程已经被创建,但是还未开始运行的状态.此状态通过调用线程的start()方法可让线程运行. 2.Runnable状态 Runnable状态可称为准备运行状态,也可称为队列,此状态通过调用线程的start()方法可让线程运

  • Android bindService的使用与Service生命周期案例详解

    Android中有两种主要方式使用Service,通过调用Context的startService方法或调用Context的bindService方法,本文只探讨纯bindService的使用,不涉及任何startService方法调用的情况.如果想了解startService相关的使用,请参见<Android中startService的使用及Service生命周期>. bindService启动服务的特点 相比于用startService启动的Service,bindService启动的服务

  • 微信APP生命周期及页面生命周期示例详解

    目录 官方文档 小程序的启动流程 app生命周期 页面的生命周期 页面的生命周期(图) 官方文档 https://developers.weixin.qq.com/doc/search.html?query=生命周期&doc_type=miniprogram&jumpbackUrl=%2Fdoc%2F 小程序的启动流程 我们画一个图来表示一下,整个小程序的启动流程,我们就知道了: app生命周期 执行App()函数也就是注册一个App 1 在注册app的时候,可以判断小程序的进入场景 2

  • Rust指南之生命周期机制详解

    目录 前言 1.所有权中的垂悬引用解析 2.结构体中使用String 而不用&str 的原因 3.生命周期注释 4.结构体中使用字符串切片引用 5.静态生命周期 6.泛型.特性与生命周期综合使用 前言   Rust 生命周期机制是与所有权机制同等重要的资源管理机制,之所以引入这个概念主要是应对复杂类型系统中资源管理的问题.引用是对待复杂类型时必不可少的机制,毕竟在Rust 中复杂类型的数据不能被处理器轻易地复制和计算.但是为什么还有引入生命周期的概念呢,这是因为引用常常会导致非常复杂的资源管理问

  • Vue组件和Route的生命周期实例详解

    先讲点实际的 实用点的钩子: Created:vue实例被生成后的一个生命周期钩子函数.(页面初始化数据加载一般写这里); beforeCreate:给个loading界面 created撤销loading; beforeDestory:你确认删除XX吗? destoryed:当前组件已被删除,清空相关内容 关于Vue组件生命周期,翻译后图示: module.exports = { //props: ['父组建传的值'], data:function(){ lifecycle.push("dat

  • 两分钟让你彻底明白Android Activity生命周期的详解(图文介绍)

    大家好,今天给大家详解一下Android中Activity的生命周期,我在前面也曾经讲过这方面的内容,但是像网上大多数文章一样,基本都是翻译Android API,过于笼统,相信大家看了,会有一点点的帮助 ,但是还不能完全吃透,所以我今天特意在重新总结一下.首先看一下Android api中所提供的Activity生命周期图(不明白的,可以看完整篇文章,在回头看一下这个图,你会明白的): Activity其实是继承了ApplicationContext这个类,我们可以重写以下方法,如下代码: 复

  • IOS视图控制器的生命周期实例详解

    IOS视图控制器 所谓的生命周期,也就是几个函数的调用顺序,这里以用Storyboard来创建一个ViewController为例 然后我们测试如下代码 // // ViewController.m // // Created by huangwenchen on 14/12/26. // Copyright (c) 2014年 huangwenchen. All rights reserved. // #import "ViewController.h" @interface Vie

随机推荐