第一节、vue中父子组件通信
父组件如何把数据传递给子组件,子组件如何把数据传递给父组件
1 父组件传递参数给子组件,子组件接受参数
父组件:
<字组件标签 k1="v1" :k2="v2"
注意:写活就用:
<字组件标签 :k1="v1"
子组件:
export default{
props:['k1',......]
}
注意:
props和data、computed同等地位,因此data怎么使用的,props就可以怎么使用\
demo:
Father.vue
<template>
<div>
<h1>父组件:</h1>
<Son :name="name" :user="user"/>
</div>
</template>
<script>
import Son from './Son.vue'
export default {
components:{
Son
},
data(){
return{
name:"张三",
user:{
username:"aaa",
password:"111"
}
}
},
methods:{
},
}
</script>
<style>
</style>
Son.vue
<template>
<div>
<h2>子组件</h2>
{{name}} {{user}}
</div>
</template>
<script>
export default {
props:["name","user"],
};
</script>
<style>
</style>
解释vue中单向数据流
描述的就是父子组件的通信问题
-
1 父组件传递给子组件后,如果父组件更新了数据,则子组件能自动同步?
答:能 -
2 父组件传递给子组件后,子组件能不能直接修改父组件传递过来值?
答:不能
2 子组件传递参数给父组件(自定义事件)
子组件能够调用授权后的父组件提供的方法,而调用方法的同时,就能顺便把参数传递给父子、
父组件
<子标签 @自定义事件名="函数"
methods:{
函数(xx){
xx就是子组件调用时携带的参数
}
}
子组件:
this.$emit('自定义事件名',参数)
props的扩展(约束)
props:{
对应父组件传递过来的自定义属性:{
//必传
required:true,
//类型约束
type:String/Number/Object/Array,
//默认值
default:"哈哈"
}
},
Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "xx"
该错误出现的原因:子组件在偷偷的直接修改props值(父组件传递过来的参数)
第二节、关于父子通信的套路
demo:父子文本框同步问题
1 套餐1: 子组件: data+watch组合
Father.vue
<template>
<div>
<h1>父组件</h1>
<input type="text" v-model="t">
<hr>
<Son :t="t" @change="change"/>
</div>
</template>
<script>
import Son from './Son.vue'
export default {
components:{
Son
},
data(){
return{
t:""
}
},
methods:{
change(val2){
this.t=val2
}
}
}
</script>
<style>
</style>
2 Son.vue
<template>
<div>
<h1>子组件</h1>
<input type="text" v-model="t2">
</div>
</template>
<script>
export default {
props:["t"],
data(){
return{
t2:""
}
},
watch:{
t(val){
console.log("val",val);
this.t2=val
},
t2(val2){
console.log("val2",val2);
this.$emit("change",val2)
}
}
};
</script>
<style>
</style>
2 套餐2:子组件:computed
son.vue
<template>
<div>
<h1>子组件</h1>
<input type="text" v-model="t2">
</div>
</template>
<script>
export default {
props:["t"],
computed:{
t2:{
get(){
return this.t
},
set(val){
this.$emit("change",val)
}
}
}
};
</script>
<style>
</style>
3 套餐3: 利用对象地址来避免单向数据流的规则 (不推荐)
Father.vue
<template>
<div>
<h1>父组件</h1>
<input type="text" v-model="obj.t">
<hr>
<Son :obj="obj" />
</div>
</template>
<script>
import Son from './Son.vue'
export default {
components:{
Son
},
data(){
return{
obj:{
t:"abc"
}
}
},
}
</script>
<style>
</style>
Son.vue
<template>
<div>
<h1>子组件</h1>
<input type="text" v-model="obj.t">
</div>
</template>
<script>
export default {
props:["obj"],
};
</script>
技巧:所有的数据、方法应该都来自于父组件
第三节、事件总线(event bus)
为了方面兄弟组件通信(其实任意关系的两个组件都能够使用bus进行通信)
原理:通过创建一个新的vue实例,作为兄弟组件之间传值的中转站
demo:
有两个兄弟组件:A.vue和B.vue
A组件想要把参数传递给B组件,意味着A组件需要调用B组件的方法,顺带把参数传递过去
B组件(被调用方法者)需要先通过bus注册监听,A组件(调用者)需要通过bus来调用
1 src下新建一个bus.js,编辑
import Vue from 'vue'
let bus=new Vue()
export default bus;
2 被调用者组件需要注册监听(B组件)
需要注意在function中this指向发生了改变,需要通过在改变之前用临时变量that来暂时保管
bus.$on("自定义事件",function(data){
//data就是传递过来的值
})
<script>
import bus from '@/bus.js'
export default {
data(){
return{
name:"张三"
}
},
//推荐mounted钩子函数
mounted(){
console.log("mounted",this.name);
let that=this;
bus.$on("ff",function(data){
//data就是传递过来的值
console.log(data);
console.log("bus",that.name);
})
}
}
</script>
3 调用者调用(A组件)
bus.$emit(“对应自定义事件”,参数)
import bus from '@/bus.js'
export default {
methods:{
test(){
bus.$emit("ff",'李四')
}
}
}
</script>
练习:把昨天的学生管理操作的 修改渲染功能用bus来实现
第四节、其他通信的方法
1 $parent
子组件通过this.$parent获取它的父组件,获得之后可以直接操作父组件定义的data、methods等
2 $children
父组件可以通过this.$children获取它的子组件数组,该语法有个弊端(如果有多个儿子,则无法清楚获取具体的某个儿子),因此该语法一般用于父组件只有一个子组件的情况
3 ref和$refs
父组件可以为某些子组件设置ref标记,今后通过this.$refs.标记来获得子组件对象
4 $attrs
爷组件传值给孙组件
Father.vue:
<Son :name="name"/>
Son.vue:
<Sun v-bind="$attrs"/>
Sun.vue:
<template>
<div>
Sun :{{name}}
</div>
</template>
<script>
export default {
props:['name']
}
</script>
第五节、插槽 slot
前置
父组件、子组件
概念
在vue中,引入的子组件的标签体是不允许写内容,为了解决这个问题,官方引入了插槽(slot)这个概念
插槽的核心思想:将共性抽取到组件,将不同暴露为插槽,一旦预留了插槽后,就可以让使用者根据自己的需求,决定这个插槽所插入的内容
使用场景 :在复用区域中既有相同的内容,又有不同的内容
注意:拥有都是子组件来开槽,父组件负责传递参数
作用
1 减少了文件个数---》体积变小---》项目性能提升
2 插槽使得组件更具有扩展性
分类:
1 匿名插槽
2 具名插槽
3 后备插槽
4 作用域插槽(难点)
插槽能够接受的内容:
一切内容:可以是数据、也可以使节点
1 匿名插槽
插槽没有名字,可以接受一切内容
Father.vue
<Son>内容</Son>
Son.vue
<slot></slot>
2 备用插槽(默认值)
如果父组件不插入内容,则子组件的插槽默认一个内容
Father.vue
<Son></Son>
Son.vue
<slot>默认值</slot>
3 具名插槽
插槽有名字,只能接受对应名字的内容
Father.vue
<template>
<div>
Father
<Son>
<template v-slot:s1>
a
</template>
<template v-slot:s2>
b
</template>
</Son>
</div>
</template>
简写:
v-slot: 可以简写成#
Son.vue
<slot name="s1"></slot>
<slot name="s2"></slot>
4 作用域插槽
前三个插槽的共同点:所有的内容由父组件来提供
如果数据在子组件中,因此我们需要把子组件的数据先传递给父组件,父组件拿到数据后和节点绑定完整,再通过插槽插入到子组件,整个过程:作用域插槽
father.vue
<template>
<div>
Father
<Son>
<template #s1="obj">
<a href="obj.myurl">{{obj.myname}}</a>
</template>
</Son>
</div>
</template>
son.vue
<slot name="s1" :myname="name" :myurl="url"></slot>
data(){
return{
name:"百度",
url:"http://www.baidu.com"
}
},
第六节、面试题:vue中组件通信的方法
父子: props+$emit
兄弟:bus
其他:$parent、$children、$refs+ref
插槽(slot)--属于父子
隔代: $attrs 、provide+inject
provide+inject使用
父组件:
<template>
<div>
Father
<Son></Son>
</div>
</template>
<script>
import Son from './Son.vue'
export default {
components:{
Son,
},
data(){
return{
name:"张三",
age:20
}
},
provide(){
return{
name:this.name,
age:this.age
}
}
子/孙组件
<template>
<div>
sun
{{name}}--{{age}}
</div>
</template>
<script>
export default {
inject:['name','age']
}
</script>
<style>
</style>
第七节、动态组件
概念和使用
组件切换
语法:
<component :is="子组件对象名"
demo:
Father.vue
<template>
<div>
<button @click="current='TabA'">tableA</button>
<button @click="current='TabB'">tableB</button>
<button @click="current='TabC'">tableC</button>
<keep-alive >
<component :is='current'></component>
</keep-alive>
</div>
</template>
<script>
import TabA from "./TableA.vue";
import TabB from "./TableB.vue";
import TabC from "./TableC.vue";
export default {
components: {
TabA,
TabB,
TabC,
},
data(){
return{
current:'TabA'
}
}
};
</script>
<style>
</style>
注意:引入的组件对象名不能是 TableA,。。。。。。。。。
TableA.vue
<template>
<div>
<h1>tableA</h1>
</div>
</template>
<script>
let timer;
export default {
created(){
console.log("A组件创建");
timer=setInterval(() => {
console.log("ding");
}, 1000);
},
destroyed(){
console.log("A组件销毁");
//清除定时器
clearInterval(timer)
}
}
</script>
<style>
</style>
keep-alive
默认情况下,虽则组件的切换(销毁),曾经客户在组件填写的表单内容都会被回收(清空),如果想要保留(缓存这些数据),我们可以使用vue提供的keepAlive容器对指定组件进行缓存(原理就是该组件不会被销毁)
语法:
<keep-alive>
<component :is='current'></component>
</keep-alive>
属性:
include :指定哪些组件被缓存,其余的就不被缓存
<keep-alive include="TabA,TabB">
<component :is='current'></component>
</keep-alive>
正则:
<keep-alive :include="/A/">
<component :is='current'></component>
</keep-alive>
exclude: 指定哪些组件不会缓存
<keep-alive exclude="TabA">
<component :is='current'></component>
</keep-alive>
3 如果使用了keep-alive,则可以使用以下两个钩子函数
//只要进入则触发
activated(){
console.log("进入A组件");
},
//只要离开则触发
deactivated(){
console.log('离开A组件');
}
name属性
能够为当前组件命令
export default {
name:"自定义组件名"
}
作用:
- 1 能够被devtools插件识别
- 2 支持 <keep-alive include/exclude=“” , include/exclude必须得用name新名字