数据通信在我们开发过程中遇到的频率是相当的多,特别是现在主流的单页面、多组件的开发方式。项目开发周期长,功能复杂,页面多。这种情况下我们需要借助模块化、组件化的方式进行开发,一是能够复用代码,可维护性高,二是能够协同开发,互不干扰,提升开发效率。这样的开发模式必然会存在大量的数据通信,比如父子组件之间的通信,又或者是毫不关系的两个组件之前需要进行数据的通信,还有全局数据的传递及使用等等。我把vue中数据通信的几种方式总结入下。
图片父组件向子组件传递数据
父组件向子组件传递数据的方法是父组件通过属性的方式向子组件传递,子组件通过prop接收。下面我们来看一下代码。
// 父组件
<template>
<div class="container">
<child :title="title"></child>
</div>
</template>
<script>
import Child from "./component/child.vue";
export default {
name: "demo",
data: function() {
return {
title: "我是父组件给的"
};
},
components: {
Child
},
};
</script>
// 子组件
<template>
<div class="text">{{title}}</div>
</template>
<script>
export default {
name: 'demo',
data: function() {},
props: {
title: {
type: String
}
},
};
</script>
子组件向父组件传递数据
子组件向父组件传递数据是通过事件的形式,子组件通过 $emit 事件向父组件发送消息,将自己的数据传递给父组件。
// 父组件
<template>
<div class="container">
<div class="title">{{title}}</div>
<child @changeTitle="parentTitle"></child>
</div>
</template>
<script>
import Child from "./component/child.vue";
export default {
name: "demo",
data: function() {
return {
title: null
};
},
components: {
Child
},
methods: {
parentTitle(e) {
this.title = e;
}
}
};
</script>
// 子组件
<template>
<div class="center">
<button @click="childTitle">我给父组件赋值</button>
</div>
</template>
<script>
export default {
name: 'demo',
data() {
return {
key: 1
};
},
methods: {
childTitle() {
this.$emit('changeTitle', `我给父组件的第${this.key}次`);
this.key++;
}
}
};
</script>
事件监听的方式传递数据
这个方法是通过创建了一个空的 vue 实例,当做 $emit 事件的处理中心(事件总线),通过他来触发以及监听事件,方便的实现了任意组件间的通信,包含父子,兄弟,隔代组件。
// 父组件
<template>
<div class="container">
<child1 :Event="Event"></child1>
<child2 :Event="Event"></child2>
<child3 :Event="Event"></child3>
</div>
</template>
<script>
import Vue from "vue";
import Child1 from "./component/child1.vue";
import Child2 from "./component/child2.vue";
import Child3 from "./component/child3.vue";
const Event = new Vue();
export default {
name: "demo",
data: function() {
return {
Event: Event
};
},
components: {
Child1,
Child2,
Child3
},
};
</script>
// 子组件1
<template>
<div class="center">
1.我的名字是:{{name}}
<button @click="send">我给3组件赋值</button>
</div>
</template>
<script>
export default{
name: "demo1",
data() {
return {
name: "gewen"
};
},
props: {
Event
},
methods: {
send() {
this.Event.$emit("message-a", this.name);
}
}
};
</script>
// 子组件2
<template>
<div class="center">
2.我的年龄是:{{age}}岁
<button @click="send">我给3组件赋值</button>
</div>
</template>
<script>
/* eslint-disable */
export default {
name: "demo2",
data() {
return {
age: "3"
};
},
props: {
Event
},
methods: {
send() {
this.Event.$emit("message-b", this.age);
}
}
};
</script>
// 子组件3
<template>
<div class="center">我的名字是{{name}},今年{{age}}岁</div>
</template>
<script>
export default{
name: 'demo3',
data() {
return {
name: '',
age: ''
};
},
props: {
Event
},
mounted() {
this.Event.$on('message-a', name => {
this.name = name;
});
this.Event.$on('message-b', age => {
this.age = age;
});
},
};
</script>
vuex
Vuex 实现了一个单项数据流,通过创建一个全局的 State 数据,组件想要修改 State 数据只能通过 Mutation 来进行,例如页面上的操作想要修改 State 数据时,需要通过 Dispatch (触发 Action ),而 Action 也不能直接操作数据,还需要通过 Mutation 来修改 State 中数据,最后根据 State 中数据的变化,来渲染页面。
Provider / Inject
Vue 2.2 版本以后新增了这两个 API , 这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。 简单来说,就是父组件通过 Provider 传入变量,任意子孙组件通过 Inject 来拿到变量
// 父组件
<template>
<div class="container">
<button @click="this.changeName">我要改名字了</button>
<p>我的名字:{{name}}</p>
<child1></child1>
</div>
</template>
<script>
import Child1 from './component/child1.vue';
export default {
name: 'demo',
data: function() {
return {
name: 'gewen'
};
},
// provide() {
// return {
// name: this.name //这种绑定方式是不可响应的
// };
// },
provide() {
return {
obj: this
};
},
components: {
Child1
},
methods: {
changeName() {
this.name = 'gewen前端';
}
}
};
</script>
// 子组件
<template>
<div>
<div class="center">
<!-- <p>子组件名字:{{name}}</p> -->
<p>子组件名字:{{this.obj.name}}</p>
</div>
<child2></child2>
</div>
</template>
<script>
import child2 from './child2.vue';
export default {
name: 'demo1',
data() {
return {};
},
props: {},
// inject: ["name"],
inject: {
obj: {
default: () => {
return {};
}
}
},
components: {
child2
},
};
</script>
需要注意的是: Provide 和 Inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的。
所以,如果采用的是我代码中注释的方式,父级的 name 如果改变了,子组件this.name 是不会改变的,仍然是 gewen ,而当采用代码中传入一个监听对象,修改对象中属性值,是可以监听到修改的。
Provider / Inject 在项目中需要有较多公共传参时使用还是颇为方便的。
children $refs
• children: 指定已创建的实例之父实例,在两者之间建立父子关系。子实例可以用 this.children 数组中。
• refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件。
// 父组件
<template>
<div class="container">
<p>我的title:{{title}}</p>
<p>我的name:{{name}}</p>
<child1 ref="comp1"></child1>
<child2 ref="comp2"></child2>
</div>
</template>
<script>
import Child1 from './component/child1.vue';
import Child2 from './component/child2.vue';
export default {
name: 'demo',
data: function() {
return {
title: null,
name: null,
content: '就是我'
};
},
components: {
Child1,
Child2
},
mounted() {
const comp1 = this.$refs.comp1;
this.title = comp1.title;
comp1.sayHello();
this.name = this.$children[1].title;
},
};
</script>
// 子组件1-ref方式
<template>
<div>
<div class="center">我的父组件是谁:{{content}}</div>
</div>
</template>
<script>
export default {
name: 'demo1',
data() {
return {
title: '我是子组件',
content: null
};
},
mounted() {
this.content = this.$parent.content;
},
methods: {
sayHello() {
window.alert('Hello');
}
}
};
</script>
// 子组件2-children方式
<template>
<div>
<div class="center"></div>
</div>
</template>
<script>
export default{
name: 'demo1',
data() {
return {
title: '我是子组件2'
};
},
};
</script>
通过例子可以看到这两种方式都可以父子间通信,而缺点也很统一,就是都不能跨级以及兄弟间通信。
总结
组件间不同的使用场景可以分为 3 类,对应的通信方式如下:
• 父子通信:Props / emit / attrs / parent / refs
• 兄弟通信:on,Vuex
• 隔代(跨级)通信:on,Vuex,provide / inject,listeners
大家可以根据自己的使用场景选择不同的通信方式,当然还是都自己写写代码,加深印象
网友评论