前言
组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。一般来说,组件可以有以下几种关系:
如上图所示,A 和 B、B 和 C、B 和 D 都是父子关系,C 和 D 是兄弟关系,A 和 C 是隔代关系(可能隔多代)。
-
props
/$emit
父组件A通过props的方式向子组件B传递,B to A 通过在 B 组件中 $emit, A 组件中 v-on 的方式实现。
//父组件
<template>
<div>
<button @click="fatherHandleClick">父向子传值</button>
<Child
:message="message"
@onChatting="chatHandleClick"
:callback="callback"
/>
</div>
</template>
<script>
import Child from "./child";
export default {
name: "HelloWorld",
components: { Child },
data() {
return {
message: "关于未来,您想对你的儿子说些什么?"
};
},
methods: {
fatherHandleClick() {
this.message = "儿子 永远相信美好的事情会发生";
},
chatHandleClick(val) {
this.message = val;
},
callback() {
return true;
}
}
};
</script>
//子组件
<template>
<div>
<button @click="childHandleClick">子修改父值</button>
{{ message }}
</div>
</template>
<script>
export default {
name: "",
props: {
message: {
type: String
},
callback: {
type: Function
}
},
data() {
return {};
},
methods: {
childHandleClick() {
console.log(this.callback());
this.$emit(
"onChatting",
"谢谢爸爸,我会对未来充满期待,永远相信美好的事情正在到来!"
);
}
}
};
</script>
总结:父组件通过props向下传递数据给子组件。注:组件中的数据共有三种形式:data、props、computed
-
依赖注入provide&inject
解决的场景问题:
根父组件A有一个方法getMap,该组件A下的所有子组件B,子组件C,子组件D,或者子组件B下的子组件E等层层嵌套情况下,在某种情况下,都需要访问父组件的getMap方法,那么常用的方法是子组件B:this.$parent.getMap(),组件E则是:this.$parent.$parent.getMap()以此类推;这种情况下,使用 $parent
属性无法很好的扩展到更深层级的嵌套组件上。
官方描述:这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。
provide
选项应该是一个对象或返回一个对象的函数。该对象包含可注入其子孙的 property。
inject
选项应该是一个字符串数组或者是一个对象,对象的 key 是本地的绑定名。
// 根组件
import father from "./components/father.vue";
export default {
name: "App",
components: {
father,
},
data() {
return {
message: "我是被子节点需要的",
};
},
//注入
provide: function() {
return {
message: this.message, //变量
modifyTheme: this.modifyTheme //方法
};
},
methods: {
modifyTheme(message) {
console.log(`我现在的数据是由${msg}传给我的`);
}
}
};
</script>
//父组件
<template>
<div>
<button @click="fatherHandleClick">父向子传值</button>
<div>父组件==={{ message }}</div>
<Child />
</div>
</template>
<script>
import Child from "./child";
export default {
name: "",
components: {
Child,
},
inject: ["message" , "modifyTheme"], //接收根节点定义的变量、方法
methods: {
fatherHandleClick() {
this.modifyTheme("父组件");
},
}
};
</script>
//子组件
<template>
<button @click="fatherHandleClick">子向父传值</button>
<div>子组件=={{message}}</div>
</template>
<script>
export default {
name: "",
inject: ["message" , "modifyTheme"],
data() {
return {};
},
methods: {
childHandleClick() {
this.modifyTheme("子组件");
},
},
};
</script>
总结:provide
和 inject
主要在开发高阶插件/组件库时使用。并不推荐用于普通应用程序代码中。
-
vuex
官方定义:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
什么情况下我应该使用 Vuex?
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。
vuex核心概念
State | 共享状态(即变量) |
Getter | 基于state的派生状态,可理解为组件中的计算属性 |
Mutation | 更改vuex的store中状态的唯一方法,通过提交mutation修改状态,同步操作(规则上是不允许异步操作的,虽然异步也可以执行,但是对devtool调试的状态跟踪或多个状态更改操作相互依赖是很不好的,所以不要觉得只要不报错我就可以这么用,还是尽量按照规则来比较好) |
Action | 类似mutation,不同之处,1.通过提交mutation修改状态 2.支持异步操作 |
Module | 模块,在大型项目中为了方便状态的管理和协作开发将store拆分为多个子模块(modules),每个子模块拥有完整的state、mutation、action、getter |
如何安装vuex
yarn add vuex
//或者
npm install vuex -S
运用步骤
- 新建store文件夹,暴露index.js
import Vue from "vue";
import Vuex from "vuex";
import common from "./common";
import shop from "./shop";
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
common,
shop
}
});
- 将store注入到入口文件main.js
import Vue from "vue";
import App from "./App";
import router from "./router";
import store from "./store";
Vue.config.productionTip = false;
new Vue({
el: "#app",
router,
store,
components: { App },
template: "<App/>"
});
- 分别在store目录新建common、shop文件夹并暴露住index.js
//common仓库
// 共享状态
const state = {
age: 26,
name: "吴亦凡"
};
const getters = {
getNewName: state => `${state.name}喜欢用电音唱歌`
};
const mutations = {
changeName(state, name) {
state.name = name;
}
};
const actions = {
updateName({ commit }, payload) {
commit("changeName", payload);
}
};
export default {
namespaced: true,
state,
getters,
mutations,
actions
};
//shop仓库
// 共享状态
const state = {
name: "",
numList: [1, 2, 3, 4, 5]
};
const getters = {
getNumList: ({ numList }) => numList.map(item => item * 2)
};
const mutations = {
asyncChangeName(state, name) {
state.name = name;
}
};
const actions = {
asyncUpdateName({ commit, rootState }, payload) {
console.log(rootState.common.age);
commit("asyncChangeName", payload);
}
};
export default {
namespaced: true,
state,
getters,
mutations,
actions
};
注意:
- namespaced这是属性用于解决不同模块的命名冲突问题
- rootState可以帮助我们从其他模块中取值
- 组件调用
/*
* 详解state、getters、mutations、actions的用法
*/
//state分模块
1、传统形式:this.$store.state.common.name
2、辅助函数:
import { mapState } from "vuex";
computed: {
...mapState("shop", ["name"]), //直接this.name使用
}
//state不分模块
1、传统形式:this.$store.state.name
2、辅助函数:
import { mapState } from "vuex";
computed: {
...mapState(["name"]), //直接this.name使用
}
========================================================================
//actions分模块
1、传统形式:this.$store.dispatch("shop/asyncUpdateName", "你好明天");
2、辅助函数:
import { mapActions } from "vuex";
methods: {
...mapActions("shop", ["asyncUpdateName"]),
fatherHandleClick(){
this.asyncUpdateName('你好明天')
}
}
//actions不分模块
1、传统形式:this.$store.dispatch("asyncUpdateName", "你好明天");
2、辅助函数:
import { mapActions } from "vuex";
methods: {
...mapActions(["asyncUpdateName"]),
fatherHandleClick(){
this.asyncUpdateName('你好明天')
}
}
========================================================================
//getters分模块
1、传统形式:this.$store.getters["common/getNewName"]
2、辅助函数:
import { mapGetters } from "vuex";
computed: {
...mapGetters("shop", ["getNumList"]), //直接this.getNumList使用
}
//getters不分模块
1、传统形式:this.$store.state.getNewName
2、辅助函数:
import { mapGetters } from "vuex";
computed: {
...mapGetters(["getNumList"]), //直接this.name使用
}
========================================================================
//mutations分模块
1、传统形式:this.$store.commit("shop/asyncChangeName", "你好明天");;
2、辅助函数:
import { mapMutations } from "vuex";
methods: {
...mapMutations("shop", ["asyncUpdateName"]),
fatherHandleClick(){
this.asyncUpdateName('你好明天')
}
}
//mutations不分模块
1、传统形式:this.$store.commit("asyncChangeName", "明天你好");;
2、辅助函数:
import { mapMutations } from "vuex";
methods: {
...mapMutations(["asyncUpdateName"]),
fatherHandleClick(){
this.asyncUpdateName('你好明天')
}
}
备注:组件通信方案还有很多,有需要可自行查阅资料。不足之处,欢迎批评指教!