Vue.js 父元件及子元件间的生命週期(Lifecycle)

之前已经有提到 Vue 实体的生命週期,现在我们来研究一下如果加上子元件的话整个生命週期会是怎么跑的。

建立父元件

使用 Vue 的 root 当作父元件:

<div id="app" style="background-color: green;">  root count: {{count}}  <button @click="count++">+1 for root</button>  <button @click="$destroy()">destroy root</button>  ...</div>
设置 count 并变动其值以便测试 update 钩子设置 $destroy 事件 trigger 以便测试 destroy 钩子
var vm = new Vue({  el: "#app",  data: {    count: 0  },  beforeCreate() {    console.log("root beforeCreate");  },  created() {    console.log("root create");  },  beforeMount() {    console.log("root beforeMount");  },  mounted() {    console.log("root mounted");  },  beforeUpdate() {    console.log("root beforeUpdate");  },  updated() {    console.log("root updated");  },  beforeDestroy() {    console.log("root beforeDestroy");  },  destroyed() {    console.log("root destroyed");  }});

在各个 hook 上设置 console.log 以便观察其触发的时机。

建立子元件

使用 Vue.component 注册元件。

Vue.component("child-component", {  template: `    <div :style="style">{{$options.name}} count: {{count}}      <button        @click="count++"      >+1 for child</button>      <button        @click="$destroy()"      >destroy child</button>    </div>`,  data() {    return {      count: 0,      style: "background-color: pink;"    };  },  beforeCreate() {    console.log(`${this.$options.name} beforeCreate`);  },  created() {    console.log(`${this.$options.name} create`);  },  beforeMount() {    console.log(`${this.$options.name} beforeMount`);  },  mounted() {    console.log(`${this.$options.name} mounted`);  },  beforeUpdate() {    console.log(`${this.$options.name} beforeUpdate`);  },  updated() {    console.log(`${this.$options.name} updated`);  },  beforeDestroy() {    console.log(`${this.$options.name} beforeDestroy`);  },  destroyed() {    console.log(`${this.$options.name} destroyed`);  }});

与父元件相同使用 count$destroy 观察 updatedestroy 钩子,并在各个 hook 加上 console.log

将子元件加入父元件中

<div id="app" style="background-color: green;">  root count: {{count}}  <button @click="count++">+1 for root</button>  <button @click="$destroy()">destroy root</button>  <child-component class="child" ref="childComponent" /></div>

父元件 mounted 结束后会是下面这样的结果:

"root beforeCreate""root create""root beforeMount""child-component beforeCreate""child-component create""child-component beforeMount""child-component mounted""root mounted"

父元件在 beforeMount 钩子执行完后会去做创建子元件的实体并且完成渲染后才会叫用 mounted 钩子。

接着按下父元件中的 +1 for root 按钮:

"root beforeUpdate""root updated"

会发现只有父元件的 update 钩子会被触发。

再来按下子元件中的 +1 for child 按钮:

"child-component beforeUpdate""child-component updated"

会发现子元件的更新也不会触发父元件的 update 钩子。

接着在父元件中按下 destroy root 按钮:

"root beforeDestroy""child-component beforeDestroy""child-component destroyed""root destroyed"

root 在执行 beforeDestroy 后会执行完 child 的 destroy 后再执行 destroyed 钩子。

接着因为 Vue 实体已经 destroy ,因此需要重新整理页面以恢复原本的状态。

重整后按下子元件的 destroy

"child-component beforeDestroy""child-component destroyed"

只有 child 的 destroy 钩子被触发。

父元件使用 ref 变动子元件 data

经由刚刚的测试我们知道父元件更新自身的数值是不会触发子元件的 update 钩子,那如果在父元件上使用 ref 变动子元件的 data 呢?

为了测试,在父元件中加上 sync child 按钮:

<div id="app" style="background-color: green;">  root count: {{count}}  <button @click="count++">+1 for root</button>  <button @click="$destroy()">destroy root</button>  <button @click="sync">sync child</button>  <child-component class="child" ref="childComponent" /></div>
var vm = new Vue({  ...  methods: {    sync() {      this.$refs.childComponent.count = this.count;    }  }});

按下 sync child 后,结果如下:

"child-component beforeUpdate""child-component updated"

虽然是在 root 触发变动,但只有 child 的 update 会被执行,因为只有 child 的 data 有被变动。

使用 props 变动子元件 data

上面直接用 ref 修改子元件内部的 data ,这次使用 props 试试看。

使用原本的 count 会将父元件的 template ,为避免因此而触发父元件的 update ,在父元件中加入 parentCount :

var vm = new Vue({  ...  data: {    count: 0,    parentCount: 0  },  ...});

另外在 template 中加入 +1 for root parent count 按钮,并且将 parentCount 传入 child 中:

<div id="app" style="background-color: green;">  root count: {{count}}  <button @click="count++">+1 for root</button>  <button @click="parentCount++">+1 for root parent count</button>  <button @click="$destroy()">destroy root</button>  <child-component class="child" :parent-count="parentCount" /></div>
Vue.component("child-component", {  ...  props: ['parentCount']  ...});

按下后结果如下:

"root beforeUpdate""child-component beforeUpdate""child-component updated""root updated"

虽然 parentCount 没有变动 root 的模板,但依然触发了 update 钩子,由此可知,不管 data 有没有挂到画面上,变动 data 就会触发 update 钩子。

多个子元件

刚刚的例子只有一个子元件,现在来看看多个子元件会是怎么运作的:

var mixin = {  template: `    <div :style="style">{{$options.name}} count: {{count}}      <button        @click="count++"      >+1 for child</button>      <button        @click="$destroy()"      >destroy child</button>    </div>`,  data() {    return {      count: 0,      style: "background-color: pink;"    };  },  props: ['parentCount'],  watch: {    parentCount(val) {      this.count = val;    }  },  beforeCreate() {    console.log(`${this.$options.name} beforeCreate`);  },  created() {    console.log(`${this.$options.name} create`);  },  beforeMount() {    console.log(`${this.$options.name} beforeMount`);  },  mounted() {    console.log(`${this.$options.name} mounted`);  },  beforeUpdate() {    console.log(`${this.$options.name} beforeUpdate`);  },  updated() {    console.log(`${this.$options.name} updated`);  },  beforeDestroy() {    console.log(`${this.$options.name} beforeDestroy`);  },  destroyed() {    console.log(`${this.$options.name} destroyed`);  }};Vue.component("child-component", {  mixins: [mixin],});Vue.component("child-two-component", {  mixins: [mixin],  data() {    return {      style: "background-color: yellow"    };  }});Vue.component("child-three-component", {  mixins: [mixin],  data() {    return {      style: "background-color: gray"    };  }});

将刚刚的 child options 拉出来变成 mixin ,并使用 mixin 定义子元件,为了区分使用不同的颜色当背景。

并且加进 root 中:

<div id="app" style="background-color: green;">  root count: {{count}}  <button @click="count++">+1 for root</button>  <button @click="parentCount++">+1 for root parent count</button>  <button @click="$destroy()">destroy root</button>  <div>    <child-component class="child" :parent-count="parentCount" />  </div>  <div>    <child-two-component class="child" :parent-count="parentCount" />  </div>  <div>    <child-three-component class="child" :parent-count="parentCount" />  </div></div>

可以看到再渲染时的钩子触发顺序:

"root beforeCreate""root create""root beforeMount""child-component beforeCreate""child-component create""child-component beforeMount""child-two-component beforeCreate""child-two-component create""child-two-component beforeMount""child-three-component beforeCreate""child-three-component create""child-three-component beforeMount""child-component mounted""child-two-component mounted""child-three-component mounted""root mounted"

对各别 child 按下 +1 for child

"child-component beforeUpdate""child-component updated"
"child-two-component beforeUpdate""child-two-component updated"
"child-three-component beforeUpdate""child-three-component updated"

各个 child 是互不影响的,都只会触发自身的 update 钩子。

现在因为按下 +1 的关係,所有的 child 的 count 都是 1,这时再按 +1 for root parent count 的话,结果如下:

"root beforeUpdate""root updated"

虽然 parentCount 变为 1 ,但是因为跟 child count 的值相同,所以不会触发 update

这时再按下 +1 for root parent count 会看到 child 的 update 触发:

"root beforeUpdate""child-component beforeUpdate""child-two-component beforeUpdate""child-three-component beforeUpdate""child-three-component updated""child-two-component updated""child-component updated""root updated"

可以看到钩子依序触发 beforeUpdate ,再以反序触发 updated

多个子元件的 destroy

按下 root 的 destroy 按钮观察 destroy 的情形:

"root beforeDestroy""child-component beforeDestroy""child-component destroyed""child-two-component beforeDestroy""child-two-component destroyed""child-three-component beforeDestroy""child-three-component destroyed""root destroyed"

个别的子元件依序触发了 destory

DEMO

CODEPEN

结论

之前介绍了各个生命週期的的定义及触发时机,这次介绍了元件间交互的生命週期,定义及交互都了解对于开发是很有帮助的,也可以减少叫用错误的问题。

同步发表于 勇者斗 Vue 龙


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章