一.组件的生命周期
1.概念及要点
1) 每一个.vue文件都是个 单文件组件( single file component , 简称 SFC )
2) 概念:组件生命周期, 指的是一个组件从创建(实例化)开始到组件被销毁(卸载)的全过程
3) 分类:组件的生命周期,分为四个阶段,每个阶段有一对钩子函数:
1> 创建期
-- 创建期又称实例化期 , 有一对钩子函数,beforeCreate()和created(),这两个钩子函数会自动执行,且只会执行一次,分别在实例化前和实例化完成之后执行
2> 挂载期
-- 挂载期,有一对钩子函数,beforeMount()和mounted(),会自动执行,且只会执行一次,分别在挂载前和挂载后完成后执行
-- 一般将监听事件,闭包,定时器,服务器网络请求等在mounted()钩子函数里面设置
3> 更新期
-- 更新期, 更新期的方法可以反复执行
4> 卸载期
-- 卸载(销毁)期 , 卸载期的方法只会执行一次, 一旦执行完毕 组件就被销毁了(无法使用了)
--(重要点) 在这里取消网络请求, 销毁定时器, 取消事件监听,销毁闭包等,避免组件卸载之后无法操作定义在其中定时器等,造成内存泄漏(实例化或者挂载期添加的定时器,闭包等在组件卸载之后依然存在,会占用一定的内存,如果不及时销毁,会影响性能)
2.代码的演示
<template>
<div class="app">
app
<h1> {{ count }} </h1>
<button @click="handleClick">加1</button>
渲染swiper组件
<swiper v-if="count%5!=0" />
</div>
</template>
<script>
导入组件
import swiper from './components/swiper.vue'
每一个.vue文件都是个 单文件组件( single file component , 简称 SFC )
组件生命周期, 指的是一个组件从创建开始到组件被销毁的全过程
export default {
data(){
return {
count:0
}
},
methods:{
handleClick(){
this.count++;
}
},
注册局部组件
components:{
swiper
},
创建(实例化)期 , 会自动执行,且只会执行一次
beforeCreate(){
console.log('beforeCreate 组件创建(实例化)前');
},
created(){
console.log('created 组件创建(实例化)完成');
},
挂载期 , 会自动执行,且只会执行一次
beforeMount(){
console.log('beforeMount 挂载前');
},
mounted(){
console.log('mounted 挂载完成');
可以在这里发送网络请求,创建定时器,监听事件
},
更新期, 更新期的方法可以反复执行
beforeUpdate(){
console.log('beforeUpdate 更新前');
},
updated(){
console.log('updated 更新完成');
},
卸载(销毁)期 , 卸载期的方法只会执行一次, 一旦执行完毕 组件就被销毁了(无法使用了)
beforeUnmount(){
console.log('beforeUnmount 组件卸载(销毁)前');
在这里取消网络请求, 销毁定时器, 取消事件监听
},
unmounted(){
console.log('unmounted 组件卸载(销毁)完成');
}
}
</script>
<style>
</style>
二.组建通信(传值)
1.概念及要点
组件通信,又称组件传值,共分为四种情况:
1) 父组件向子组件传值(通信):
1> 步骤:
1>> 父组件在子组件身上通过自定义属性传值给子组件,
2>> 子组件内部通过props选项接收值
2> 语法:
1>> 父组件:<子组件名 :自定义属性名="传递值变量名" />(自定义属性名和传递值变量名一般是相同的)
2>> 子组件:props:['自定义属性名'](props后面一般是一个数组,可以接受多个值)
3> 注意:props中的数据是只读的( 不能直接修改,内置基本类型的值是可以修改不会影响子组件的,但不建议在子组件修改父组件传递来的值 )
2) 子组件向父组件传值(通信):
1> 步骤:
1>> 父组件在子组件身上绑定自定义事件并指定一个回调函数
2>> 子组件内部通过$emit方法触发事件并传值
2> 语法:
1>> 父组件:<子组件名 @自定义事件名="事件绑定函数()" />(回调函数需要在父组件methods选项中定义)
2>> 子组件:this.$emit("自定义事件名",需要传递的值)
3) 兄弟组件通信(传值):
1> 两个方法:
1>> 通过共同的父组件传值:子组件A--子-父传值--->父组件----父-子传值----->子组件B
2>> 通过搭建通信桥梁,借助mitt库的on和emit方法实现
2> 步骤
1>> 先实例化一个公共的通信对象(mitt库),创建eventbus.js文件作为通信桥梁,在其中引入库组件并设置抛出,然后为需要传值的两个兄弟组件分别导入该库
2>> 其中一个组件提前在mounted()钩子函数中用mitt.on()方法监听事件并设置一个回调函数,
3>> 另外一个兄弟组件通过mitt.emit()方法去触发事件并传值
3> 要点
1>> 收值组件设置监听
2>> 传值组件负责触发和传值
4) 跨组件传值(通信): 只能从外向里层传递,如果要从里向外传值可以用mitt库
1> 方法:
1>> 层层传递:A--->B--->C--->D(缺点是传递过程中可能会收到组件内部某些影响,将传递的值改变,准确性有欠缺)
2>> 选项传值(provide和inject选项)
2> 步骤
1>> 外层组件通过provide选项传值
2>> 内层组件通过inject选项接收值
3> 语法:
1>> 外层组件作为传值方:
provide(){
return{
传递值名:值,
......
}
}
2>> 内层组件作为收值方:
inject:["传递值名",.......]
2.父组件向子组件传值(通信)
语法:
父组件:<子组件名 :自定义属性名="传递值变量名" />(自定义属性名和传递值变量名一般是相同的)
子组件:props:['自定义属性名'](props后面一般是一个数组,可以接受多个值)
父组件:::::::::::::::::::::::::::::::::
<template>
<div class="app">
app
<child :money="money"/>-----------------渲染子组件,为其添加父元素自定义属性,属性值设置为父元素响应式数据,也就是由父元素传递给子元素的数值
</div>
</template>
<script>
import child from './components/child.vue'-----------导入子组件
export default {
data(){
return {
money:88888----------------在data中设置需要传递的数据
}
},
components:{-------------注册局部组件
child
}
}
</script>
子组件:::::::::::::::::::::::::::::::
<template>
<div class="child">
<h3>child子组件 - {{money}}</h3>
</div>
</template>
<script>
export default {
props中的数据是只读的( 不能直接修改 )
props:['money'],-------------使用props方法接收父组件传来的值,接收到之后就可以在子组件的组件模板区域直接使用了,使用方法与子组件本身的响应式数据一样
}
</script>
3.子组件向父组件传值(通信)
语法:
父组件:<子组件名 @自定义事件名="事件绑定函数不带括号" />(事件绑定函数需要在父组件methods选项中定义)
子组件:this.$emit("自定义事件名",需要传递的值)
子组件:传值方
<template>
<div class="child">
<h3>child子组件 - {{money}}</h3>
<button @click="handleClick">给父组件传值</button>
</div>
</template>
<script>
export default {
props:['money'],
methods:{
handleClick(){
this.$emit('savemoney', this.money * 2 )----------触发事件,并向父元素中事件类型绑定的回调函数传入实参,即就是子组件需要传递给父组件的值
}
}
}
</script>
父组件:收值方
<template>
<div class="app">
app
<child :money="money" @savemoney="sm" />-------------渲染子组件,为子组件添加自定义事件类型并绑定在methods中设置好的事件,回调函数函数需要设置形参,子元素触发自定义事件时,会将传递的值通过回调函数函数的形参传入,可在回调函数中将传来的值取出。
</div>
</template>
<script>
import child from './components/child.vue'
export default {
data(){
return {
money:88888
}
},
methods:{
sm(data){---------------sm为自定义事件绑定的回调函数,data形参用来接收子元素传值
console.log('从子组件传来的值:',data);---------此处data会接受到子组件传来的值
}
},
components:{
child
}
}
</script>
4.兄弟组件通信(传值)
步骤:
1)创建eventbus.js文件作为通信桥梁,实例化一个公共的通信对象(mitt库),在其中引入库组件并设置抛出,然后为需要传值的两个兄弟组件分别导入通信桥梁文件,方便使用该库
2)其中一个组件提前在mounted()钩子函数中用mitt.on()方法监听事件并设置一个回调函数,
3)另外一个兄弟组件通过mitt.emit()方法去触发事件并传值
通信桥梁eventbus.js文件:
import mitt from 'mitt'
实例化通信对象(调用代表实例化,也可以写new mitt()),并导出(export default + 对象 代表默认导出)
export default mitt()---------------调用mitt将其实例化,然后默认导出,将mitt实例化对象对外抛出
组件A:传值方
<template>
<div class="childa">
<h3>childa组件</h3>
<button @click="handleClick">传值给b组件</button>
</div>
</template>
<script>
import mitt from '../utils/eventbus'-------------导入通信对象
export default {
methods:{
handleClick(){--------------通过点击事件触发第一个参数---自定义事件事件,并传值
mitt.emit('trandferdata','我喜欢你!')----------库mitt调用其emit方法,第一个参数api执行之后代表触发的自定义事件,第二个参数代表传出的值
}
}
}
</script>
组件B:收值方
<template>
<div class="childb">
<h3>childb组件</h3>
</div>
</template>
<script>
import mitt from '../utils/eventbus'----------------导入通信对象
export default {
methods:{
getdata(data){
console.log('接收到a组件传来的值:',data);------此处设置接收到组件A传值然后将其打印出来
}
},
mounted(){-----------------------在挂载完成的钩子函数中添加监听事件
mitt.on('trandferdata',this.getdata)--------------mitt库的on方法接收两个参数,第一个参数代表监听的事件,第二个参数代表被监听的事件触发之后自动执行的操作,此处为methods定义的函数,该函数设置一个形参,组件A传递来的数据会作为实参传入该函数
},
beforeUnmount(){--------------------为保证性能,需要在卸载前将设置的监听事件清除,一系列清除与销毁动作设置在这里
mitt.off('trandferdata',this.getdata)------------mitt库的off方法代表清除事件监听,他接受两个参数,第一个参数代表设置监听时被监听的事件,第二个参数代表被监听时间触发时执行的操作,清除监听时方法的参数和设置监听是是一样的
}
}
</script>
5.跨组件传值(通信)
步骤:
1)外层组件通过provide选项传值
provide(){
return{
传递值名:
}
}
2)内层组件通过inject选项接收值
inject:["传递值名",.....]
最外层组件:传值方
<template>
<div class="app">
app
<childx />
</div>
</template>
<script>
import childx from './components/childx.vue'
export default {
data(){
return {
money:66666
}
},
provide(){---------------------通过provide选项传值,该选项类似于data选项,必须为函数格式,并返回一个对象,需要传递的值可以作为对象属性添加在该对象中
return {
m:this.money,
n:888
}
},
components:{
childx
}
}
</script>
最内层组件:收值方
<template>
<div class="childz">
<h3>childz组件 - {{m}} - {{n}}</h3>
</div>
</template>
<script>
export default {
inject:['m','n']-----------------通过inject选项 接收值,inject为对象格式,一般是一个数组,将接收到的值作为数组元素一依次添加进去就可以在该组件的组件模板正常使用了,使用形式等同于本组件的响应式数据,使用插值表达式即可
}
</script>
6.组件通信综合案例
根组件::::::::::::::::::::
<template>
<div class="box">
父组件为子组件添加自定义属性,并将准备传给两个子组件的值作为自定义属性的值
<Left :list="list" />------------渲染一级列表组件
<Right :list="list" />-----------渲染二级列表组件
</div>
</template>
<script>
import Left from './components/Left.vue'---------导入一级列表组件
import Right from './components/Right.vue'-----------导入二级列表组件
export default {
components:{-------------注册导入的组件
Left,
Right
},
data(){
return {
list:[
{
name:'手机',
list:[ { name:'苹果手机' },{ name:'小米手机' } ]
},
{
name:'家电',
list:[ { name:'苹果家电' },{ name:'小米家电' } ]
},
{
name:'口红',
list:[ { name:'苹果口红' },{ name:'小米口红' } ]
},
]
}
},
}
</script>
一级列表渲染
<template>
<div class="list">
渲染一级分类列表
<div @click="handleClick(index)" v-for="(item,index) in list" :key="index" :class="{ cate:true, active: currentIndex == index }">
{{ item.name }}
</div>
</div>
</template>
<script>
import mitt from '../utils/eventbus'
export default {
props:['list'],-------------子组件通过props接收父元素传来的值,方便渲染时引用
data(){
return {
currentIndex:0
}
},
methods:{
handleClick(i){
this.currentIndex = i;--------保存下标( 修改currentIndex这个响应式数据的值 )
mitt.emit('saveIndex',i);----------使用mitt库调用emit方法,触发事件并传值,第一个参数为自定义事件,emit这个方法在调用时可以触发作为其参数传入的事件,并将第二个参数作为传出的值一并传出
}
}
}
</script>
二级列表渲染
<template>
<div class="sublist">
渲染二级分类列表
<div class="cate" v-for="(item,index) in list.length > 0 && list[currentIndex].list" :key="index">-----当用来渲染的列表可能存在数组为空的情况时,可以设置成list.length > 0 && list[currentIndex].list,使用短路与的方法在数组为空时跳过后续代码不执行,避免报错,也可以在循环体之前的标签添加v-if,条件设置为list.length>0,这样当数组为空时,条件不成立,渲染列表的代码也不会执行,这里的条件渲染指令只能用v-if,用v-show无法避免报错,因为v-if在条件不成立时直接将对应标签从代码中删除,代码未执行,也就不会报错;而v-show在条件不成立时只是给对应标签添加display:none属性,代码还是会执行,因此还是会报错
或
<div class="cate" v-for="(item,index) in list[currentIndex].list" :key="index">
{{ item.name }}
</div>
</div>
</template>
<script>
import mitt from '../utils/eventbus'
export default {
data(){
return {
currentIndex:0
}
},
props:['list'],
methods:{
getIndex(i){
this.currentIndex = i;---------------保存下标
}
},
mounted(){
在完成挂载的钩子函数中设置监听
mitt.on('saveIndex',this.getIndex)-----------此处设置事件监听,一级列表组件中该自定义事件被触发之后,传出的值从此处的第二个回调函数的形参传入,
},
beforeUnmount(){
在卸载前的钩子函数中取消监听
mitt.off('saveIndex',this.getIndex);
}
}
</script>