Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue 生命周期钩子函数 #12

Open
yanglikun1 opened this issue Mar 29, 2018 · 0 comments
Open

Vue 生命周期钩子函数 #12

yanglikun1 opened this issue Mar 29, 2018 · 0 comments

Comments

@yanglikun1
Copy link
Contributor

前言

生命周期通俗点说是一个对象的生老病死(Cradle-to-Grave)但对于Vue而言,Vue的生命周期也就是 Vue实例及组件实例从创建到卸载的过程。

Vue的生命周期

Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

以下根据vue官网生命周期图示展开讲解
相关代码及实例均可clone: https://github.com/yanglikun1/bascVue.git 下载。

Vue生命周期钩子函数

beforeCreate

beforeCreate:在实例初始化之后,数据观测和event/watcher事件配置之前调用。

如何理解此钩子,堆上代码最直接:
[代码1-1]

beforeCreate() {
    window.console.log(this);
    window.console.log(`el:    ${this.$el}`);  
    window.console.log(`data   : ${this.$data}`);
  },
  • this 表示实例本身在此钩子中实例已经创建
  • this.$el 表示实例挂载的dom,输出 undefined 说明 实例尚未挂载
  • this.$data 表示 Vue 实例观察的数据对象 输出undefined 说明此时数据并未被实例观测到。

根据执行结果提示 beforeCreate 的解释是“创建实例之前执行的钩子事件”:对此处的理解是 数据观测、event/watch的配置是实例的一部分,beforeCreate只是new 了实例。

created

created:在实例创建完成后被立即调用。在这一步,实例已完成数据观测、属性和方法的运算,watch/event事件回调。然而,挂载阶段还没开始,$el属性目前不可见。

[代码2-1]

 created() {
    window.console.log(`el     : ${this.$el}`);
    window.console.log(`data   : ${JSON.stringify(this.$data)}`); 
    window.console.log(`msg: ${this.msg}`);
  },

Vue会自动将data里面的数据进行递归抓换成getter和setter进行观测;

  • this.$el 输出 undefined 说明 实例尚未挂载
  • this.$data 输出数据对象说明此时数据实例观测到data。
  • this.msg 数据data已经实现geter/seter转换。属性和方法的运算已配置。

对比 beforeCreate下this实例和created 中this实例的区别。

el选项的有无对生命周期过程的影响

首先系统会判断对象中有没有el选项,有el选项,则继续编译过程;没有el选项,则停止编译,也意味着暂时停止了生命周期,直到 vm.$mount(el),页面此时必需已有el存在否则报错。

  1. 注释new Vue() 中el选项查看浏览器consle;
    [代码3-1]
 new Vue({
 // el: '#first',
  data() {
    return {
      msg: 30,
    };
  },
  // template: '<App/>',
  beforeCreate() {
    window.console.log('调用了beforCreate函数===============');
  },
  created() {
    window.console.log('调用了created函数===================');
  },
  beforeMount() {
    window.console.log('调用了beforeMount函数=============');
  },
  mounted() {
    window.console.log('调用了mounted函数===================');
  },

正如说明中所说停止编译,也意味着暂时停止了生命周期。

2.在created钩子中 添加 this.$mount('#first') 查看浏览器consle;
[代码3-2]

 new Vue({
 // el: '#first',
  data() {
    return {
      msg: 30,
    };
  },
  // template: '<App/>',
  beforeCreate() {
    window.console.log('调用了beforCreate函数===============');
  },
  created() {
    window.console.log('调用了created函数===================');
    this.$mount('#first');
  },
  beforeMount() {
    window.console.log('调用了beforeMount函数=============');
  },
  mounted() {
    window.console.log('调用了mounted函数===================');
  },

使用this.$mount 指定挂载dom 生命周期继续。

3.将this.$mount('#app') 放在最前和最后查看区别。

[代码3-3]

 new Vue({
 // el: '#first',
  data() {
    return {
      msg: 30,
    };
  },
  // template: '<App/>',
  beforeCreate() {
    window.console.log('调用了beforCreate函数===============');
  },
  created() {
    this.$mount('#first');
    window.console.log('调用了created函数===================');
  },
  beforeMount() {
    window.console.log('调用了beforeMount函数=============');
  },
  mounted() {
    window.console.log('调用了mounted函数===================');
  },

如果在beforeCreate中使用this.$mount() ,会出现什么情况?

template参数选项的有无对生命周期的影响

  1. 如果Vue实例对象中有template参数选项,则将其作为模版编译浏览器可读的HTML;
  2. 如果没有template参数选项,则外部的html作为模版编译,也就是说template参数的优先级要比html高。

修改外部html中挂载元素id为first,修改new Vue()中el选项值为#first 并注释掉template参数,在beforeMount/mounted中打印this.$el查看。
[代码4-1]

new Vue({
  el: '#first',
  data() {
    return {
      msg: 30,
      age: 1,
      number: 0,
    };
  },
  router,
  // template: '<App/>',
  beforeCreate() {
    window.console.log('调用了beforCreate函数===============');
  },
  created() {
    window.console.log('调用了created函数===================');
  },
  beforeMount() {
    window.console.log('调用了beforeMount函数=============');
    window.console.log(this.$el);
  },
  mounted() {
    window.console.log('调用了mounted函数===================');
    window.console.log(this.$el);
  },

我们可以看到在beforMount中数据尚未渲染到dom;

为什么说template比外部模版优先级高呢?咱们继续往下

打开注释掉template参数,在beforeMount/mounted中打印this.$el查看。
[代码4-2]

new Vue({
  el: '#first',
  data() {
    return {
      msg: 30,
      age: 1,
      number: 0,
    };
  },
  router,
  template: '<App/>',
  beforeCreate() {
    window.console.log('调用了beforCreate函数===============');
  },
  created() {
    window.console.log('调用了created函数===================');
  },
  beforeMount() {
    window.console.log('调用了beforeMount函数=============');
    window.console.log(this.$el);
  },
  mounted() {
    window.console.log('调用了mounted函数===================');
    window.console.log(this.$el);
  },

可以发现mounted 中的this.$el 被替换成了app, new Vue() 实例的template的优先级高于外部html 。

beforeMount

beforeMount:在挂载开始之前被调用,相关的render函数首次被调用。
上例中可以看到 在此时模版已经被编译但尚未渲染;

思考:此时template模版中的组件的状态

[代码5-1]

  beforeCreate() {
    window.console.log('App.vue调用了beforCreate函数');
  },
  created() {
    window.console.log('App.vue调用了created函数');
  },
  beforeMount() {
    window.console.log('App.vue调用了beforeMount函数');
  },
  mounted() {
    window.console.log('App.vue调用了mount函数');
  },
  beforeUpdate() {
    window.console.log('App.vue调用了beforeUpdate函数');
  },
  updated() {
    window.console.log('App.vue调用了updated函数');
  },

此时template模版中的组件实例已经挂载到相应的dom上,并且在Vue 实例完成挂载之前。

mounted

mounted:el 被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。如果root实例挂载了一个文档内元素,当 mounted 被调用时vm.$el也在文档内。

注意 mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted。

beforeUpdate

beforeUpdate:数据更新时调用,发生在虚拟dom重新渲染和打补丁之前。

(只有Vue实例中的数据被“写入”到我们的模板中)你可以在这个钩子中进一步的更改状态,这不会触发附加的重渲染过程。

[代码6-1]

  <body>
    <div id="first" style="font-size:50px;">{{msg}} {{age}}
      <button @click="msg = 2">修改msg</button>
      <button @click="number = 2">修改number</button>
    </div>
    <!-- built files will be auto injected -->
  </body>
  new Vue({
  el: '#first',
  data() {
    return {
      msg: 30,
      age: 1,
      number: 0,
    };
  },
  beforeUpdate() {
    window.console.log('调用了beforeUpdate函数=============');
  },
  updated() {
    window.console.log('调用了updated函数==================');
  }

点击"修改number"按钮结果是:没有触发beforUpdate/updated。

updated

updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件DOM已经更新,所以你现在可以执行依赖于 DOM 的操作。

然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。注意 updated 不会承诺所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以用 vm.$nextTick 替换掉 updated;

测试发现数据的改变引起的只是本实例的beforeUpdate/updated的触发。

activated

activated:keep-alive 组件激活时调用,组件第一次执行时激活keep-alive。

[代码7-1]

    <div id="app">
   <div class="demo">
     <keep-alive>
        <component :is="view"></component>
     </keep-alive>
    <button @click="view = 'HelloWorld'">点击切换</button>
   </div>

template 选项的模版组件,初始化触发了 TestKeep子组件的activate 钩子函数。

deactivated

deactivated:keep-alive 组件停用时调用。

当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。

按照[代码7-1] 点击 “点击切换” 按钮

我们看到子组件 TeatKeep 调用了deactivated,HelloWorld组件的activate触发。

beforeDestroy

beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
[代码8-1]

<div id="app">
   <div class="demo">
      <p><b>beforeDestroy</b>:实例销毁之前调用。在这一步,实例仍然完全可用。</p> <button @click="destoryd" >销毁App</button>
     <p><b>age = {{age}}</b></p>
   </div>
</div>
<!-- destoryd() 函数 执行 this.$destory-->
  beforeCreate() {
    window.console.log('App.vue调用了beforCreate函数');
  },
  created() {
    window.console.log('App.vue调用了create函数');
  },
  beforeMount() {
    window.console.log('App.vue调用了beforeMount函数');
  },
  mounted() {
    window.console.log('App.vue调用了mount函数');
  },
  beforeUpdate() {
    window.console.log('App.vue调用了beforeUpdate函数');
  },
  updated() {
    window.console.log('App.vue调用了Update函数');
  },
  beforeDestroy() {
    window.console.log('App.vue调用了beforeDestroy函数');
    this.age = 50;
    window.console.log(this.age);
    // 此钩子里面修改age 数据。
  },
  destroyed() {
    window.console.log('App.vue调用了destroyed函数');
  },

此时可以获取实例,仍触发updated函数,但不会触发beforUpdate函数,页面不再被渲染。

destroyed

destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
[代码9-1]

    <div id="app">
   <!-- <div class="fileimge"></div> -->
   <div class="demo">
      <p><b>beforeDestroy</b>:实例销毁之前调用。在这一步,实例仍然完全可用。</p> <button @click="destoryd" >销毁App</button>
      <p><b>destroyed</b>:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。</p>
     <p><b>age = {{age}}</b></p>
     <component :is="view"></component>
     <!--TestKeep子组件-->
   </div>
  </div>

点击‘销毁App’ 的结果

此时可以获取实例,但不能触发updated函数,子实例被销毁,修改v-show不会触发渲染。仍可获取this 但是关于实例的监听及子实例均已销毁,触发了子组件的销毁钩子,在点击'销毁App'按钮 已经没有效果。

errorCaptured

errorCaptured:当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。

本身报错此钩子不会触发。
[代码10-1]

// 子组件抛出 错误
errorCaptured(err, vm, info) {
    window.console.log('error================');
    const error = `${err.stack}\n\nfound in ${info} of component`;
    window.console.log(error);
    return false; // 阻止向上传播;
  }

总结

讲完这些Vue生命周期钩子我们还会有很多疑惑,不过通过我们的不断探索会发现更多的使用场景和方式。你我共勉!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant