vue 组件之间通信的六种方式
1、props 和 $emit(子 触发 父)、ref(父 调用 子)
- 父组件 给 子组件 数据传递,一般使用 props 关键字。
- 子组件 给 父组件 传递数据,一般使用 $emit 方式触发 父组件 的事件;因为 子组件 是 不能直接修改 props 中的值(即 父组件 传递的值)的,若需要修改,则要通过触发 父组件 的事件,告知父组件进行修改。
- 父组件 直接调用 子组件 的方法,可用 $refs 获取到子组件的实例,从而调用其方法。
示例代码如下:
// Parent.vue
<template>
<div>
<span>我是Parent组件</span>
<Child ref="child" :parentValue="value" @emitEdit="edit"></Child>
</div>
</template>
<script>
import Child from "./Child.vue";
export default {
components: { Child },
data() {
return {
value: "我是父组件"
}
},
methods: {
pFn() {
console.log("我是父组件的方法");
// 调用子组件的方法
this.$refs.child.cFn();
},
// 子组件触发,修改 value 的值
edit(msg) {
this.value = msg;
}
}
}
</script>
// Child.vue
<template>
<div>
<span>我是Child组件</span>
<span>父组件传的值:{{this.parentValue}}</span>
<button @click="editParentValue">修改 parentValue</button>
</div>
</template>
<script>
export default {
props: {
parentValue: String
},
methods: {
cFn() {
console.log("我是子组件的方法");
}
editParentValue() {
this.$emit("emitEdit", "子组件修改了我");
}
}
}
</script>
2、Event Bus(\(on、\)emit)
- Event Bus(事件总线)方式主要用于无父子关系的组件之间传值,比如兄弟组件之间
- 在 组件A 中使用 $on 注册监听事件,在 组件B 中使用 $emit 触发事件
示例代码如下:
// 新建一个vue实例,作为事件总线;可将其注册到全局或者vue的propotype上
vue.propotype.eventBus = new Vue();
// CompA.vue
<template>
<div>
<span>我是CompA组件</span>
<span>value的值:{{ value }}</span>
</div>
</template>
<script>
export default {
data() {
return {
value: "我是CompA组件"
}
},
mounted() {
this.eventBus.$on("edit", (msg) => {
this.value = msg;
})
}
}
</script>
// CompB.vue
<template>
<div>
<span>我是CompB组件</span>
<button @click="editValue">修改 CompA 的value</button>
</div>
</template>
<script>
export default {
methods: {
editValue() {
this.eventBus.$emit("edit", "CompB 修改了我");
}
}
}
</script>
疑问点:为什么不能使用 this.\(emit 和 this.\)on ,而要重新 new 一个空的 vue 实例?
3、provide 和 inject
- provide 和 inject 方式通常用于祖孙组件之间的通信,主要用于祖先组件向子组件传值
- provide 用于在 祖先组件 中定义可向子组件注入的属性或方法
- inject 用于在 子组件 中注入属性和方法等依赖
示例代码:
// CompA.vue
<template>
<div>
<span>我是CompA组件</span>
<span>value的值:{{ value }}</span>
<!--其中,CompA组件不一定是CompB组件的直接父级,也可以是CompB组件的祖先组件,中间可以有多层的组件引用-->
<CompB ref="compB"></CompB>
</div>
</template>
<script>
import CompB from "./CompB.vue";
export default {
components: { CompB },
data() {
return {
value: "我是CompA组件"
}
},
provide() {
return {
// 向外暴露这两个变量
aValue: this.value,
aFn: this.fn
}
},
// 使用 Vue.observable() 可使provide传递的值变成响应式,即在祖先组件中修改之后,子组件中的值也会同步变化
provide() {
this.aData = Vue.observable({
value: this.value,
aFn: this.fn
});
return {
aData: this.aData
};
},
methods: {
fn() {
console.log("我是 CompA 的方法");
}
}
}
</script>
// CompB.vue
<template>
<div>
<span>我是CompB组件</span>
<button @click="aFn">调用 CompA 的fn</button>
</div>
</template>
<script>
export default {
// 注入依赖
inject: ["aValue", "aFn"],
mounted() {
console.log("CompA 组件的 value", this.aValue);
}
}
</script>
4、$parent 和 $children
- $parent 和 $children 用于有 直接父子关系 的 组件 之间的通信
- $parent 用于 获取组件的父组件实例
- $parent 用于 获取组件的子组件实例
5、$attrs 和 $listeners
- $attrs 和 $listeners 主要用于 获取 父组件 的属性和方法
- $attrs 用于获取父组件中 没有在子组件的props中接收的属性
- $listeners 用于获取父组件中的自定义事件
6、Vuex
Vuex 类似于一个仓库,可存放一些数据供全局调用,但是刷新页面就会消失,若需解决此问题,可选择将数据存储到 Vuex 的同时,将其存放到 sessionStorage 或者 localStorage 中即可。
Vuex 中包括 state、mutations、actions、getters、modules 几个部分。其中
- state:定义变量
- getters:获取state中变量的值
- mutations:修改 state 中的值;使用 commit 触发;只能进行同步操作
- actions:进行各种操作,可通过 dispatch 触发;可以进行异步操作
- modules:项目较大时,可对变量的管理进行拆分,拆分成多个js文件,每个js文件作为单个的module,然后统一注册到 index.js 中,供全局调用
示例代码:
// module.js
const state = {
modelCreateDataJsonStr: "",
modelSelectDataJsonStr: "",
modelClassDataJsonStr: ""
};
const getters = {
getModelCreateDataJsonStr: state => {
return state.modelCreateDataJsonStr;
},
getModelSelectDataJsonStr: state => {
return state.modelSelectDataJsonStr;
},
getModelClassDataJsonStr: state => {
return state.modelClassDataJsonStr;
}
};
const mutations = {
SET_MODEL_CREATE_DATA_JSON_STR(state, modelCreateDataJsonStr) {
state.modelCreateDataJsonStr = modelCreateDataJsonStr;
},
SET_MODEL_SELECT_DATA_JSON_STR(state, modelSelectDataJsonStr) {
state.modelSelectDataJsonStr = modelSelectDataJsonStr;
},
SET_MODEL_CLASS_DATA_JSON_STR(state, modelClassDataJsonStr) {
state.modelClassDataJsonStr = modelClassDataJsonStr;
}
};
const actions = {
setModelCreateDataJsonStr({commit}, modelCreateDataJsonStr) {
commit("SET_MODEL_CREATE_DATA_JSON_STR", modelCreateDataJsonStr);
},
setModelSelectDataJsonStr({commit}, modelSelectDataJsonStr) {
commit("SET_MODEL_SELECT_DATA_JSON_STR", modelSelectDataJsonStr);
},
setModelClassDataJsonStr({commit}, modelClassDataJsonStr) {
commit("SET_MODEL_CLASS_DATA_JSON_STR", modelClassDataJsonStr);
}
};
// 暴露仓库
export { state, getters, mutations, actions };
// index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
/**
* 批量导入module下的所有模块
* 如果module下面有自己新建的文件夹 文件夹里面的JS文件不能用index命名 容易冲突
*/
const path = require('path');
const files = require.context('./module', true, /\.js$/);
const modules = {};
files.keys().forEach(key => {
const name = path.basename(key, '.js'); // 返回key的最后一部分
modules[name] = files(key).default || files(key);
});
Vue.use(Vuex);
const store = new Vuex.Store({
modules: modules,
});
export default store;