组件之间的通信主要分为:
- 父子组件之间通信
- 非父子组件之间的通信(兄弟组件、隔代关系组件等)
八种通信方式 |
---|
1. props 、$emit |
2. c h i l d r e n 、 children 、 children、parent |
3. ref |
4. provide 、reject |
5. Vuex |
6. $attrs 与listenters |
7. eventBus |
8. localStorage 、sessionStorage |
props
详情: 用来接收来自父组件的数据,props可以是简单的数组或对象。
如果只是父子关系的组件数据交互,那么应该考虑使用props进行单向传递
基于对象的语法:
type: 可以是String、Number、Boolean、Array、Object、Date、Function、Symbol任何自定义构造函数、或上述内容组成的数组。
子组件会检查父组件传递过来的数据是否是给定的类型,否则抛出警告。
default:any: 默认情况,如果prop没有被传入,则为该prop指定一个默认值,对象或数组的默认值必须从一个工厂函数返回。
required:Boolean:在非生产环境中,如果prop没有传入,则控制台会抛出警告。
举个栗子:
父组件test.vue
<template>
<div class="hello">
<HelloWorld :date="date"></HelloWorld>
</div>
</template>
<script>
import HelloWorld from '../components/HelloWorld'
export default {
data () {
return {
date:new Date()
}
},
components:{
HelloWorld
}
}
</script>
子组件 HelloWorld.vue
<template>
<div class="hello">
{{date}}
</div>
</template>
<script>
export default {
props:['date'],
data () {
return {
}
}
}
</script>
如下图:
$emit:
作用:子组件给父组件传递参数或者触发父组件上定义的方法
如果涉及到子组件向父组件的数据传递,那么应该考虑使用 $emit 和 $on;
举个栗子:
父组件test.vue
<template>
<div class="hello">
<HelloWorld :date="date" v-on:childToSayHello="sayHello"></HelloWorld>
</div>
</template>
<script>
import HelloWorld from '../components/HelloWorld'
export default {
data () {
return {
date:new Date()
}
},
components:{
HelloWorld,
},
methods: {
sayHello(name){
console.log('hello world!');
console.log(name);
}
},
}
</script>
子组件 HelloWorld.vue
<template>
<div class="hello">
<button @click="toSayHello">sayHello</button>
</div>
</template>
<script>
export default {
props:['date'],
data () {
return {
}
},
methods: {
toSayHello(){
this.$emit('childToSayHello','liming');
}
}
}
</script>
如下图:
$children 和 $parent:
详情:
指定已创建的实例,在两者之间建立父子关系,子实例可以用this.$parent访问父实例,
子实例被推入父实例的$children数组中。
$children特点:
- 并不保证顺序,也不是响应式的。
建议: 节制地使用 $parent和$children,它们主要的目的是作为访问组件的应急方
法。更推荐用props和events实现父子组件通信。
举个栗子:
父组件test.vue
<template>
<div class="hello">
<HelloWorld></HelloWorld>
index: {{index}}
</div>
</template>
<script>
import HelloWorld from '../components/HelloWorld'
export default {
data () {
return {
number: 100,
index: '',
}
},
components:{
HelloWorld,
},
methods: {
},
mounted() {
this.index = this.$children[0].count;
},
}
</script>
子组件 HelloWorld.vue
<template>
<div class="hello">
msg: {{msg}}
</div>
</template>
<script>
export default {
data () {
return {
msg:'',
count: 99
}
},
methods: {
},
mounted () {
this.msg = this.$parent.number
}
}
</script>
如下图:
ref
作用:为子组件赋予一个ID引用,父组件用来访问子组件实例或子元素。
举个栗子:
父组件test.vue
<template>
<div class="hello">
<HelloWorld ref="child"></HelloWorld>
count:{{count}}
</div>
</template>
<script>
import HelloWorld from '../components/HelloWorld'
export default {
data () {
return {
count:''
}
},
components:{
HelloWorld,
},
methods: {
},
mounted() {
this.count = this.$refs.child.count;
this.$refs.child.sayHello();
},
}
</script>
子组件 HelloWorld.vue
<template>
<div class="hello">
hello
</div>
</template>
<script>
export default {
data () {
return {
count: 99
}
},
methods: {
sayHello(){
console.log('hello world!');
}
},
mounted () {
}
}
</script>
如下图:
provide 、reject
- provide: 选项是一个对象或返回一个对象的函数。该对象包含可注入其子孙的属性。
- inject:一个字符串数组或一个对象,value是在可用的注入内容
中搜索的key。
举个栗子:
父组件test.vue
<template>
<div class="hello">
<HelloWorld ></HelloWorld>
</div>
</template>
<script>
import HelloWorld from '../components/HelloWorld'
export default {
data () {
return {
}
},
provide:{
str_1:'67',
str: 'hello'
},
components:{
HelloWorld,
},
methods: {
},
mounted() {
},
}
</script>
子组件 HelloWorld.vue
<template>
<div class="hello">
{{str}}
</div>
</template>
<script>
export default {
// 如果有可选的值
inject:{
str: {
from: 'str_1',
default: 'str'
}
},
// 直接取值
// inject:['str'],
data () {
return {
}
},
methods: {
},
mounted () {
}
}
</script>
如下图:
Vuex
使用Vuex来进行数据管理,如果仅仅是传递数据,而不做中间处理,使用Vuex处理就有些大材小用。
特点:
存储的数据是响应式的,但是不会保存起来,刷新页面后又会恢复初始状态
main.js
import store from './store/index'
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
test.vue
<template>
<div class="hello">
<button @click="open">{{this.$store.state.app.flat?'关闭':'打开'}}</button>
</div>
</template>
<script>
import HelloWorld from '../components/HelloWorld'
export default {
data () {
return {
flat:''
}
},
provide:{
},
components:{
HelloWorld,
},
methods: {
sayHello(){
console.log('hello');
},
open(){
this.$store.commit('open');
console.log(this.$store.state)
},
},
mounted() {
},
}
</script>
同级目录下store文件夹
index.js
import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app'
import getters from './getters'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
app,
},
getters
})
export default store
getters.js
const getters = {
GET_FLAT: state => state.app.flat,
}
export default getters
state的状态不能直接修改,需要通过commit来实现
其中核心概念:
- state:页面状态管理容器对象
- commit:状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。
- mutations:存改变状态的操作方法
- getters:读取state中的数据
store/modules/app.js
const app = {
state: {
flat: false,
},
mutations: {
open (state) {
if(state.flat){
state.flat = false;
}else {
state.flat = true;
}
},
}
}
export default app
如下图:
目录结构:
$attrs 与listenters
假设有这样的场景,A组件调用了B组件,B组件调用了C组件,C组件想要调用A组件中的数据。那么可以通过B这个中间件进行数据传递。仅是A-B-C,我们可以使用props来传递,那如果是A-B-C-D-E-F甚至更多层的数据传递呢?就要不停的props,重复写很多代码,这时候可以使用$attrs 与listenters来解决
举个栗子(test是Helloworld的父组件,HelloWorld是demo的父组件)
父组件test.vue
<template>
<div class="hello">
<HelloWorld v-bind="$attrs" age='18'></HelloWorld>
</div>
</template>
<script>
import HelloWorld from '../components/HelloWorld'
export default {
data () {
return {
}
},
components:{
HelloWorld,
},
methods: {
},
mounted() {
},
}
</script>
子组件 HelloWorld.vue
<template>
<div class="hello">
我是HelloWorld组件
<Demo v-bind="$attrs"></Demo>
</div>
</template>
<script>
import Demo from './demo';
export default {
data () {
return {
}
},
methods: {
},
mounted () {
console.log(this.$attrs);//{age:"18"}
},
components:{
Demo
}
}
</script>
子组件 Demo.vue
<template>
<div>
我是demo
</div>
</template>
<script>
export default {
mounted () {
console.log(this.$attrs); //{age:"18"}
}
}
</script>
如下图:
原谅我没找到更好的解释listenters的教程
eventBus
事件总线
找不到很好的解释来说明这个 =_=!!!
举个栗子:
test.vue
<template>
<div class="hello">
<HelloWorld></HelloWorld>
<button @click="handleClick">确定</button>
</div>
</template>
<script>
import HelloWorld from '../components/HelloWorld'
import {EventBus} from '../components/js/event'
export default {
data () {
return {
num:0
}
},
provide:{
},
components:{
HelloWorld,
},
methods: {
handleClick(){
EventBus.$emit('add',{
num:this.num++
});
}
},
mounted() {
},
}
</script>
Helloworld.vue
如下图:
localStorage 、sessionStorage
- localStorage:在浏览器中存储 key/value对。没有过期时间。存放数据大小5MB(有时根据浏览器而定),仅在客户端(即浏览器)保存,不参与服务器的通信。
语法:
window.localStorage
保存数据语法:
localStorage.setItem("key", "value");
读取数据语法:
var lastname = localStorage.getItem("key");
删除数据语法:
localStorage.removeItem("key");
- sessionStorage: 在浏览器中存储 key/value 对。 在关闭窗口或标签页之后将会删除这些数据。存放数据大小5MB(有时根据浏览器而定),仅在客户端(即浏览器)保存,不参与服务器的通信。
语法
window.sessionStorage
保存数据语法:
sessionStorage.setItem("key", "value");
读取数据语法:
var lastname = sessionStorage.getItem("key");
删除指定键的数据语法:
sessionStorage.removeItem("key");
删除所有数据:
sessionStorage.clear();
总结
常见使用场景可以分为三类:
-
父子组件通信: props; $parent / $children; provide / inject ; ref ; $attrs / $listeners
-
兄弟组件通信: eventBus ; vuex
-
跨级通信: eventBus;Vuex;provide / inject 、$attrs / $listeners