标签:actions get listeners 轻量 使用 链式 根据 cti 因此
一、组件间的关系
1、父子关系
2、兄弟关系
3、隔代关系
二、组件间的通信方式
方法一:props / $emit
// app.vue 父组件
<template>
<div>
<sub1 v-bind:data_from_app = "msg_parent"></sub1>
</div>
</template>
<script>
import sub1 from ‘./components/sub1.vue‘;
export default {
data(){
return {
msg_parent:‘父组件数据app‘
}
},
components:{
sub1
}
}
</script>
<style lang="scss" scoped>
</style>
// 子组件 sub1.vue
<template>
<div>
<p>{{data_from_app}}</p>
</div>
</template>
<script>
export default {
data(){
return {
sub_msg:‘子组件sub1数据‘,
}
},
props:{
data_from_app:{
type:String,
require:true
}
}
}
// 父组件通过props向下传递给子组件。注:组件中的数据共有三种形式:data,props,computed
</script>
<style lang="scss" scoped>
</style>
2、子组件向父组件传值---(通过事件的形式)--- $emit
子组件通过events给父组件发送消息,实际就是把自己的数据发送到父组件 $emit发送 $event接收
// 子组件 sub1.vue
<template>
<div>
<p>{{data_from_app}}</p>
<p @click="changeText">{{text_sub}}</p>
</div>
</template>
<script>
export default {
data(){
return {
sub_msg:‘子组件sub1数据‘,
text_sub:‘子组件原来的数据‘
}
},
props:{
data_from_app:{
type:String,
require:true
}
},
methods:{
changeText(){
// 自定义事件,传递值 "子组件向父组件传值"
this.$emit(‘titleChanged‘,‘子组件向父组件传值‘)
}
}
}
// 1、props
// 父组件通过props向下传递给子组件。注:组件中的数据共有三种形式:data,props,computed
</script>
<style lang="scss" scoped>
</style>
// 父组件 app.vue
<template>
<div>
<!-- 给子组件定义了一个点击事件changeText,
在子组件中的时间定义中,使用$emit传递titleChanged和要传递给父组件参数,子组件用$emit来传递参数数据 -->
<sub1 v-on:titleChanged="updateText" v-bind:data_from_app = "msg_parent"></sub1>
<!-- 父组件通过$event来接收传递过来的参数 updateText($event) -->
<!-- 这里要与子组件 titleChanged 保持一致 -->
</div>
</template>
<script>
import sub1 from ‘./components/sub1.vue‘;
export default {
data(){
return {
msg_parent:‘父组件数据app‘,
text:‘父组件原本的text‘
}
},
methods:{
updateText(e){
this.text = e;
console.log(this.text)
}
},
components:{
sub1
}
}
</script>
<style lang="scss" scoped>
</style>
方法二、$emit / $on
这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量的实现了任何组件间的通信,包括父子、兄弟、隔代。当项目比较大时、可以考虑使用vuex。
实现方式:
var Event = new Vue()
Event.$emit(事件名,数据) ;
Event.$on(事件名,data => {});
<div id="itany">
<my-a></my-a>
<my-b></my-b>
<my-c></my-c>
</div>
<template id="a">
<div>
<h3>A组件:{{name}}</h3>
<button @click="send">将数据发送给C组件</button>
</div>
</template>
<template id="b">
<div>
<h3>B组件:{{age}}</h3>
<button @click="send">将数组发送给C组件</button>
</div>
</template>
<template id="c">
<div>
<h3>C组件:{{name}},{{age}}</h3>
</div>
</template>
<script>
var Event = new Vue();//定义一个空的Vue实例
var A = {
template: ‘#a‘,
data() {
return {
name: ‘tom‘
}
},
methods: {
send() {
Event.$emit(‘data-a‘, this.name);
}
}
}
var B = {
template: ‘#b‘,
data() {
return {
age: 20
}
},
methods: {
send() {
Event.$emit(‘data-b‘, this.age);
}
}
}
var C = {
template: ‘#c‘,
data() {
return {
name: ‘‘,
age: ""
}
},
mounted() {//在模板编译完成后执行
Event.$on(‘data-a‘,name => {
this.name = name;//箭头函数内部不会产生新的this,这边如果不用=>,this指代Event
})
Event.$on(‘data-b‘,age => {
this.age = age;
})
}
}
var vm = new Vue({
el: ‘#itany‘,
components: {
‘my-a‘: A,
‘my-b‘: B,
‘my-c‘: C
}
});
</script>
$on 监听了自定义事件data-a 和 data-b,因为有时不确定何时会触发事件,一般会在mounted或created钩子中监听
方法三、vuex

vuex 的原理:
vuex实现了一个单向数据流,在全局拥有一个State存放数据,当组件要更改State中数据时,必须通过mutation进行,mutation同时提供了订阅者模式供外部插件调用获取State数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量同步操作需要走Action,但Action是无法直接修改state的,还是需要通过mutation来修改state的数据。最后根据state的变化,渲染到视图上。
各模块的介绍:
$store.dispatch(‘action 名称‘, data1)来触发。然后由commit()来触发mutation的调用 , 间接更新 state。负责处理Vue Components接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台API请求的操作就在这个模块中进行,包括触发其他action以及提交mutation的操作。该模块提供了Promise的封装,以支持action的链式触发。commit(‘mutation 名称‘)来触发。是Vuex修改state的唯一推荐方法。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。vuex是vue的状态管理器,存储的数据是响应式的,但是并不会保存起来,刷新之后就回到了初始状态。具体做法应该是在vuex里面数据改变时把数据拷贝一份保存到localStorage中,刷新之后,如果localstorage里面有保存的数据,取出来替换store里面的state
方法四:$attrs / $listeners
$attrs:包含了父级作用域中,不被prop所识别(且获取)的特性绑定(class和style除外),当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定(class和style除外),并且可以通过v-bind="$attrs"传入内部组件。通常配合interitAttrs选项一起使用。
$listeners:包含了父级作用域中(不含.native修饰器的)v-on事件监听器。他可以通过v-on="$listeners"传入内部组件
// app.vue 最外层的组件,本身定义了foo boo coo 三个数据
// 此时里面的子组件是 sub1 ,给它使用了 foo boo coo 三个数据,但 sub1 本身只定义了 foo 这个属性
<template>
<div>
<!-- 给子组件定义了一个点击事件changeText,
在子组件中的时间定义中,使用$emit传递titleChanged和要传递给父组件参数,子组件用$emit来传递参数数据 -->
<p>父组件中的数据:foo:{{foo}}、boo:{{boo}}、coo:{{coo}}</p>
<sub1 :foo="foo"
:boo="boo"
:coo="coo"
v-on:titleChanged="updateText"
v-bind:data_from_app = "msg_parent"></sub1>
<!-- 父组件通过$event来接收传递过来的参数 updateText($event) -->
<!-- 这里要与子组件 titleChanged 保持一致 -->
<!-- <subvue1></subvue1>
<subvue2></subvue2> -->
</div>
</template>
<script>
import sub1 from ‘./components/sub1.vue‘;
// import subvue1 from ‘./components/subVue/subvue1.vue‘;
// import subvue2 from ‘./components/subVue/subvue2.vue‘;
export default {
data(){
return {
msg_parent:‘父组件数据app‘,
text:‘父组件原本的text‘,
foo:‘html‘,
boo:‘css‘,
coo:‘vue‘
}
},
methods:{
updateText(e){
this.text = e;
console.log(this.text)
}
},
components:{
sub1,
// subvue1,
// subvue2
}
}
</script>
<style lang="scss" scoped>
</style>
// sub1 APP 的子组件,本身只定义了 foo 这个属性,因此通过$attrs拿到的是 boo coo
// 这里sub1有了一个子组件 subvue1,给这个子组件使用了$attrs,即boo coo ,但本身子组件只有 boo 属性
<template>
<div>
<p>{{data_from_app}}</p>
<p @click="changeText">{{text_sub}}</p>
<p>foo:{{foo}}</p>
<p>sub1的$attrs:{{$attrs}}</p>
<subvue1 v-bind="$attrs"></subvue1>
</div>
</template>
<script>
import subvue1 from ‘./subVue/subvue1.vue‘
export default {
data(){
return {
sub_msg:‘子组件sub1数据‘,
text_sub:‘子组件原来的数据‘
}
},
inheritAttrs:false, // 可已自动关闭挂载到根元素上没有在props声明的属性
props:{
data_from_app:{
type:String,
require:true
},
foo:String
},
created(){
console.log(this.$attrs);
},
methods:{
changeText(){
// 自定义事件,传递值 "子组件向父组件传值"
this.$emit(‘titleChanged‘,‘子组件向父组件传值‘)
}
},
components:{
subvue1
}
}
// 1、props
// 父组件通过props向下传递给子组件。注:组件中的数据共有三种形式:data,props,computed
</script>
<style lang="scss" scoped>
</style>
// subvue1 sub1 的子组件,本身只定义了 boo 这一个属性,因此 attrs 拿到的是 coo
<template>
<div>
<h3>subvue1组件</h3>
<p>boo:{{boo}}</p>
<!-- <button >subvue1组件:将数据发给组件subvue2</button> -->
<p>subvue1的$attrs:{{$attrs}}</p>
</div>
</template>
<script>
import Vue from ‘vue‘;
export default {
data(){
return {
data1:‘subvue1‘,
data_from:‘‘
}
},
props:{
boo:String
},
mounted(){
},
created9(){
console.log(this.$attrs);
},
methods:{
}
}
</script>
<style lang="sass" scoped>
</style>

小结:$attrs表示没有继承数据的对象,格式为{属性名:属性值},vue2.4提供了$attrs、$listeners来传递数据与实践,跨级组件之间的通信更加简单。
$attrs 里面存放的是父组件中绑定的非props属性。$listeners 里面存放的是父组件中绑定的非原生事件。
方法五:provide/inject
二者需要一起使用。允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在上下游关系成立的时间里始终生效。就是:祖先组件中通过provide来提供变量,然后在子孙组件中通过inject来注入变量。
主要解决了跨级组件中通信问题,不过他的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立一种主动提供与依赖注入的关系。
// A.vue
export default {
provide: {
name: ‘浪里行舟‘
}
}
// B.vue
export default {
inject: [‘name‘],
mounted () {
console.log(this.name); // 浪里行舟
}
}
provide/inject绑定并不是响应式的,这是刻意为之的。如果传入一个可监听对象,那么其对象的属性还是可响应的。所以,A.vue中name改变了,B.vue的this.name是不会改变的。
方法六:$parent / $children 与 ref
ref :如果在普通的dom元素上使用,引用指向的就是dom元素;如果用在子组件上,引用就指向组件实例。
$parent / $children 访问父、子实例
以上两种方法得到的都是组件实例,使用后可以调用组件的方法或访问数据。
弊端:无法在跨级或兄弟间通信
// component-a 子组件
export default {
data () {
return {
title: ‘Vue.js‘
}
},
methods: {
sayHello () {
window.alert(‘Hello‘);
}
}
}
// 父组件
<template>
<component-a ref="comA"></component-a>
</template>
<script>
export default {
mounted () {
const comA = this.$refs.comA;
console.log(comA.title); // Vue.js
comA.sayHello(); // 弹窗
}
}
</script>
四、总结
父向子传递数据是通过 props,子向父是通过 events($emit);通过父链 / 子链也可以通信($parent / $children);ref 也可以访问组件实例;provide / inject API;$attrs/$listeners
Bus;Vuex
Bus;Vuex;provide / inject API、$attrs/$listeners
标签:actions get listeners 轻量 使用 链式 根据 cti 因此
原文地址:https://www.cnblogs.com/1220x/p/11741332.html