本文较为冗长,单条举多例进行说明。
一、父子通信:props,$emit。
1、props:
父组件
<template>
<div>
<div style="border: 1px solid #ddd;padding: 40px 0;">
这是父组件
<el-button type="primary" @click="changeChildCom()">改变子组件</el-button>
</div>
<ChildComponent :childPropsName="TestProps"></ChildComponent>
<!--ChildComponent 为注册组件名称,childPropsName 名称自定义,子组件在接收时需要,TestProps 为父组件变量名称-->
</div>
</template>
<script>
import ChildComponent from '../test/child' //子组件引入
export default {
components:{
ChildComponent //注册组件
},
name: "index",
data(){
return{
TestProps:0
}
},
methods:{
changeChildCom(){ //改变父组件参数,子组件显示内容随之改变
this.TestProps += 1
}
}
};
</script>
子组件
<template>
<div>这是子组件
<el-button> {{childPropsName}}</el-button><!--childPropsName 为父组件在引用子组件时引用-->
</div>
</template>
<script>
export default {
name: "child",
props:{
childPropsName:{
type:Number,
value:0
}
}
};
</script>
注意父子组件的 childPropsName
用法
子组件需求父组件的某变量 TestProps,在父组件中引入注册并使用当前子组件,在使用时绑定一个子组件接收名称 childPropsName,子组件的 props 中再声明一次将要接收这个变量即可。
子组件显示内容依赖父组件参数TestProps,当父组件参数发生改变,会影响子组件显示内容。
2、$emit:
父组件
<template>
<div>
<div style="border: 1px solid #ddd;padding: 40px 0;">
这是父组件{{TestParent}}
</div>
<ChildComponent @parentFunction="getchildData"></ChildComponent>
</div>
</template>
<script>
import ChildComponent from '../test/child'
export default {
components:{
ChildComponent
},
name: "index",
data(){
return{
TestParent:0
}
},
methods:{
getchildData(val){
this.TestParent = val
}
}
};
</script>
<template>
<div>这是子组件
<el-button type="error" @click="handleParent">改变父组件</el-button>
</div>
</template>
<script>
export default {
name: "child",
methods:{
handleParent(){
this.$emit('parentFunction',1)
}
}
};
</script>
$emit
是一个程序化的事件监听器,他可以通过v-on进行事件监听
在引入使用子组件时,在子组件上就通过 v-on或者 @ 绑定父组件的方法,在子组件通过$emit
去调用这个方法,并将值通过传参的形势发送给父组件。
二、多组件嵌套关系的父组件、孙子组件传参:$attrs、 $listeners
当拥有一级与二级的层级嵌套关系的时候,props与$emit就可以适用了,但当有多级嵌套关系,父组件的孙子组件要想实现与父组件通信的时候,如果再使用props与 $emit就需要多次重复接收或发送,就太过冗余了。
Vue 2.4开始提供了 $attrs和 $listeners来解决这个问题,能够让父组件与孙子组件这种多层嵌套关系的组件进行参数传递。
关系:父组件 => 父组件引用子组件 => 子组件引用孙子组件
目的:父组件引用孙子组件数据,孙子组件使用父组件数据。
父组件
<template>
<div>
<div style="border: 1px solid #ddd;padding: 40px 0;">
这是父组件{{messagec}}
</div>
<ChildComponent :messagec="messagec" @grandsonMethod="getGrandsonData"></ChildComponent>
</div>
</template>
<script>
import ChildComponent from '../test/child'
export default {
components:{
ChildComponent
},
name: "index",
data(){
return{
messagec:'123'
}
},
methods:{
getGrandsonData(val){
console.log("这是来自孙子组件的数据:",val)
this.messagec = val
}
}
};
</script>
子组件
<template>
<div>这是子组件
<TextT v-bind="$attrs" v-on="$listeners"></TextT>
</div>
</template>
<script>
import TextT from './childT'
export default {
components:{
TextT
},
name: "child",
};
</script>
孙子组件
<template>
<div>
<div>这是孙子组件</div>
<input type="text" v-model="$attrs.messagec" @input="commitData($attrs.messagec)" />
</div>
</template>
<script>
export default {
name: "childT",
methods:{
commitData(val){
this.$emit('grandsonMethod',val)
}
}
};
</script>
子组件仅作为过渡桥梁的作用,通过 v-bind="$attrs"
/ v-on="$listeners"
建立引用关系,而父组件就会将所有的事件监听器指向子组件中引用的孙子组件,从而达到数据通信的作用。
注意:父传孙子:子组件使用 v-bind="$attrs"
孙子组件可以直接使用this.$attrs.父组件变量名去访问这个变量。
孙子调用父组件方法并传值:v-on="$listeners"
同样使用$emit。
三、prototype链式层级关系抓取内容: $root、 $parent、 $children、 $el
脚手架的项目开端,从入口文件中的new Vue开始
我们在App.vue的id为app的dom节点下开始了罪恶的一生,链式关系就从这儿开始。
目前用作讲解的例子关系:
Vue根实例
------App.vue
|----------Head.vue
|
|
|----------Body.vue
|-------------|----Parent.vue
|---------------------|----Child.vue
|-----------------------------|-----Grandson.vue
|
|----------Foot.vue
最顶级,最外层级为Vue根实例,其次是App.vue组件,我们在App.vue组件下可能引入使用了类似头部,主体,脚注甚至更多的组件内容,也可能在这些组件下引入使用了再下级的组件,类似Body.vue中引入使用了父组件,父组件下引用了子组件,子组件下引用了孙子组件。不断的套娃。
1、$root,直接获取根实例
不论你嵌套层级有多深,$root可以直接获取到嵌套的最顶层根实例部分
我在以上嵌套关系的Grandson.vue(孙子组件)中打印了$root的内容,获取到一个Vue的实例化对象
可以看到这个对象下会有很多方法,在这个对象下,似乎看到了我们所需要的 $children, $el , $parent
根实例下的 $children
包含所有的嵌套层级关系。
2、$el 节点操作
在实际的操作过程中,移入 $el 时,发现页面上的根节点#App被选中
单独打印这个 $el,确实也得到了节点内容
既然我们已经选中了这个节点,那我们同样可以操作他。
this.$root.$el.style.display = 'none'
亲测有效。
3、$parent 当前嵌套的上一层关系
父组件
<template>
<div>
<div style="border: 1px solid #ddd;padding: 40px 0;">
这是父组件{{parentData}}
</div>
<ChildComponent></ChildComponent>
</div>
</template>
<script>
import ChildComponent from '../test/child'
export default {
components:{
ChildComponent
},
name: "index",
data(){
return{
parentData:'123'
}
},
methods:{
parentMethod(val){
this.parentData = val
}
}
};
</script>
子组件
<template>
<div>这是子组件
<el-button @click="getParentData">获取父组件的数据</el-button>
<el-button @click="useParentMethod">使用父组件的方法并传参</el-button>
</div>
</template>
<script>
export default {
name: "child",
methods:{
getParentData(){
alert(this.$parent.parentData)
},
useParentMethod(){
this.$parent.parentMethod('456')
},
}
};
</script>
页内效果
可以看到,以上为父组件引入了子组件,父组件定义了一个变量为parentData,初始值为123,并定义了事件parentMethod
,事件中重新对parentData变量赋值为传递过来的参数。
通过this.$parent.变量名
可以使用父类组件的变量,也可以直接操作这个变量。
通过this.$parent.方法名
可以使用父类组件的方法,在子组件中可以调用这个方法并传递参数。
点击获取 父类组件的数据
按钮,触发弹窗,弹出父组件 parentData
的值123
点击 使用父组件的方法并传参
按钮,父组件的值改变为456
4、$parent 嵌套下的组件列
慎用
慎用
慎用
父组件
<template>
<div>
<div style="border: 1px solid #ddd;padding: 40px 0;">
这是父组件<button @click="parentMethod">获取子组件兑现列表</button>
</div>
<ChildComponent></ChildComponent>
<GrandComponent></GrandComponent>
</div>
</template>
<script>
import ChildComponent from '../test/child'
import GrandComponent from '../test/childT'
export default {
components:{
ChildComponent,
GrandComponent
},
name: "index",
methods:{
parentMethod(){
console.log(this.$children)
}
}
};
</script>
子组件1
<template>
<div>这是子组件1</div>
</template>
<script>
export default {
name: "child",
data(){
return{
childData1:'123'
}
}
};
</script>
子组件2
<template>
<div>这是子组件2</div>
</template>
<script>
export default {
name: "child",
data(){
return{
childData2:'2354'
}
}
};
</script>
父组件引入了 子组件1 与 子组件2,并定义了方法打印组件下的$children
。
在组件1 组件2中分别定义了一个变量。
点击触发后,结果为一个包含各自组件变量属性的列表。
可以通过this.$children[组件下标].变量
/ this.$children[组件下标].方法
的形式去获取变量,调用方法。
至于慎用的原因:如果你使用的其他的ui框架,比如element ui
在组件中引用了框架中的某些元素样式,都有可能是框架中定义的组件,且使用$children,不利于后续的开发维护。
四、父传子孙,provide和inject 数据注入
Vue 2.2.0新增,这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效,只要嵌套的关系存在,下层组件就可以使用上层的数据/方法。
注意:下层组件只能对这个属性进行使用,并不能直接进行修改,当父组件向下注入参数发生改变,所有下层组件中使用的变量都会发生变化
嵌套关系:父组件(向下注入) ==》 父组件引用子组件 ==》子组件引用孙子组件
父组件
<template>
<div>
<div style="border: 1px solid #ddd;padding: 40px 0;">
这是父组件
</div>
<ChildComponent></ChildComponent>
</div>
</template>
<script>
import ChildComponent from '../test/child'
export default {
components:{
ChildComponent
},
provide:{
parentCommon:'-----来自爸爸的爱'
},
name: "index"
};
</script>
子组件
<template>
<div>这是子组件{{parentCommon}}
<GrandComponent></GrandComponent>
</div>
</template>
<script>
import GrandComponent from './childT'
export default {
name: "child",
inject:['parentCommon'],
components:{
GrandComponent
}
};
</script>
孙子组件
<template>
<div>这是孙子组件{{parentCommon}}</div>
</template>
<script>
export default {
name: "child",
inject:['parentCommon']
};
</script>
父组件使用provide
注入了名为 parentCommon
的变量,下层的子组件与孙子组件都可以通过inject
和这个变量名称去共用这个变量。
得出结果:
五、子传父,$refs 收纳持有属性 ref 的DOM和实例
如果在引入使用组件时,组件上有注册使用ref属性
,Vue组件就可以将持有这个属性的DOM 元素和组件实例收纳到当前Vue组件的$refs中。
适用与父子组件关系,作用是父组件使用子组件方法和变量。
子组件
<template>
<div>
这是子组件
</div>
</template>
<script>
export default {
name: "child",
data(){
return{
childData:'这是子组件的数据'
}
},
methods:{
childMethod(){
console.log('触发了子组件的方法')
}
}
};
</script>
父组件
<template>
<div>
<div style="border: 1px solid #ddd;padding: 40px 0;">
这是父组件
<el-button @click="getChildData">获取子组件的数据</el-button>
</div>
<ChildComponent ref="childChildComponent"></ChildComponent>
</div>
</template>
<script>
import ChildComponent from '../test/child'
export default {
components:{
ChildComponent
},
name: "index",
methods:{
getChildData(){
//console.log(this.$refs.childChildComponent); //==>打印子组件Vue组件对象
//this.$refs.childChildComponent.childData = '147258' // 触发后会修改子组件childData的值
console.log(this.$refs.childChildComponent.childData); //==》打印出 这是子组件的数据
this.$refs.childChildComponent.childMethod() //触发子组件方法,并打印 触发了子组件的方法
}
}
};
</script>
父组件在引入使用子组件时定义属性ref,属性值自定义,父组件可以通过 this.$refs.自定义属性值.子组件变量名
/ this.$refs.自定义属性值.子组件方法名
去 使用 / 修改 子组件的变量、调用子组件的方法。
六、Vuex 集中状态管理仓库
这个官网有更详细的说明,如果在项目中有跨多组件、多组件公用变量,且关系复杂的时候,就可以使用 Vuex
也有对其讲解升入的博主 博客供大家深入思考。