摘要:的出现就是为了解决这一状况。基本上就是一个容器,它包含着你的应用中大部分的状态。无法直接修改的状态。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。因此解决办法也是相同的使用一个函数来声明模块状态和等等
为什么会出现Vuex
非父子关系的组件如何进行通信?(Event Bus)
bus.js
import Vue from "vue"; export default new Vue();
foo.vue
import bus from "./bus.js"; export default { methods: { changeBroData() { bus.$emit("changeBarData"); } } }
bar.vue
import bus from "./bus.js"; export default { created() { bus.$on("changeBarData",() => { this.count++; }); } }
查看效果
但是当我们需要修改这个操作的时候,我们需要动3个地方,倘若项目小的话还倒好说,但是对于大项目组件间交互很多的概况,Event Bus就会表现的很吃力。Vuex的出现就是为了解决这一状况。
Vuex介绍安装:
script标签引入
https://unpkg.com/vuex
NPM
npm install vuex --save-dev
import Vue from "vue"; import Vuex from "vuex"; Vue.use(Vuex);
每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:
Vuex的状态存储是响应式的。当Vuex的状态属性发生变化时,相应的组件也会更新。
无法直接修改Vuex的状态。只能通过显式的提交(commit)。
简单的Store
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increase(state) { state.count++; } } }); store.commit("increase"); console.log(store.state.count); // 1State
从上文我们知道Vuex是响应式的,我们如何在Vue实例中使用Vuex中的实例呢,自然离不开计算属性computed了。
在 Vue 组件中获得 Vuex 状态
//仓库 const store = new Vuex.Store({ state: { count: 1 } }); //foo组件 const foo = { template: `{{ count }}`, computed: { count: () => store.state.count } };这时候问题就来了,如果这样进行引入store的话,组件就会以来全局状态单例。
Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中(需调用 Vue.use(Vuex))://挂载App new Vue({ el: "#app", //注入store选项 store, render: h => h(App) }); //foo组件 const foo = { template: `{{ count }}`, computed: { count() { // 不能使用箭头函数,不然this就不是Vue实例了 // 通过this.$store获取到store实例 return this.$store.state.count } } };如果有很多状态需要映射,我们岂不是要写好多代码,这时候需要用到mapState辅助函数,使用 mapState 辅助函数帮助我们生成计算属性。
辅助函数 实例1const foo = { template: `count: {{ count }} countAlias: {{ countAlias }} countPlusLocalCount: {{ countPlusLocalCount }}`, data() { return { localCount: 2 }; }, computed: Vuex.mapState({ //只处理仓库中的count count: state => state.count*2, //映射 countAlias: "count", //需要用到Vue实例的话不能使用箭头函数不然this无法获取到Vue实例 countPlusLocalCount(state) { return state.count + this.localCount; } }) };当然当映射名与state中属性名相同时,可以通过mapState(["count"])这种形式书写。
因为组件本身也有许多计算属性直接使用mapState的话无法扩充computed了,这时候我们可以使用ES新属性: 对象展开运算符computed: { localComputed() { return this.count; }, ...Vuex.mapState({ //只处理仓库中的count count: state => state.count*2, //映射 countAlias: "count", //需要用到Vue实例的话不能使用箭头函数不然this无法获取到Vue实例 countPlusLocalCount(state) { return state.count + this.localCount; } }) }ATT:使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。
Getter有时候我们需要过滤/处理state中的属性值,就像Vue实例中我们需要处理data数据一样,Vue有computed。Vuex同样提供给我们一个属性getter,getter就是Vuex的“计算属性”。
Getter接收state作为第一个参数//仓库 const store = new Vuex.Store({ state: { users: [{ name: "jason", id: 1, female: false }, { name: "molly", id: 2, female: true }, { name: "steven", id: 3, female: false }] }, getters: { // 过滤所有属性中female是true的对象 getFemaleUsers: state => state.users.filter(user => user.female) } }); console.log(store.getters.getFemaleUsers); //[{ "name": "molly", "id": 2, "female": true }]当然Getter同样有辅助函数 mapGetters将 store 中的 getter 映射到局部计算属性
直接上对象展开运算符了
辅助函数 实例1//foo组件 const foo = { template: `femaleList: {{ femaleList }}`, computed: { // 属性名相同的不再阐述 ...Vuex.mapGetters({ femaleList: "getFemaleUsers" }) } }; Mutation更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
提交时额外的参数被称为载荷
普通风格提交方式store.commit("mutationName", { params: "参数" });对象风格提交方式
store.commit({ type: "mutationName", params: "参数" });注意点:
设置对象新属性时
方法1:Vue.set(obj, "newVal", 100)
方法2:obj = {...obj, newVal: 100};
Mutations的事件类型尽量使用常量,并用一个文件进行维护。方便协同开发以及维护。
// 存储事件名mutations-types.js export const MUTATIONS_GETDATA = "MUTATIONS_GETDATA"; export const MUTATIONS_SUCCESS = "MUTATIONS_SUCCESS";import { MUTATIONS_GETDATA ,MUTATIONS_SUCCESS } from "path/mutations-types.js"; const store = new Vuex.Store({ mutations: { [MUTATIONS_GETDATA]() { // todo }, [MUTATIONS_SUCCESS]() { // todo } } });Mutations中的函数必须都是同步函数,异步函数都要写在Actions中。
在组件中提交Mutation
$store.commit("xxx","yyy")
当然也有对应的辅助函数帮助我们映射到对应的methods方法中import { mapMutations } from "vuex"; export default { methods: { ...mapMutations([ "event1", "event2" ]), ...mapMutations({ eventAlias: "event3" //将this.eventAlias()映射为this.$store.commit("event3") }) } };ActionAction与Mutation的不同在于Action不直接修改状态只是做commit一个mutation、然后就是可以做异步操作。
简单的Actionconst INCREASE_COUNT = "INCREASE_COUNT"; const store = new Vuex.Store({ state: { count: 0 }, mutations: { [INCREASE_COUNT](state) { state.count++; } }, actions: { add({ commit }) { commit(INCREASE_COUNT); } } });看到这我们或许以为Actions的作用与Mutations不一样么,对到现在为止作用是一样的,但是Actions可以执行异步操作
const INCREASE_COUNT = "INCREASE_COUNT"; const store = new Vuex.Store({ state: { count: 0 }, mutations: { [INCREASE_COUNT](state) { state.count++; } }, actions: { add({ commit }) { setTimeout(() => { commit(INCREASE_COUNT); }, 1000); } } });Action的分发也有两种方式
普通风格分发方式store.dispatch("actionName", { params: "参数" });对象风格分发方式
store.dispatch({ type: "actionName", params: "参数" });辅助函数mapActions用法与mapMutations无异
代码链接methods: { ...mapActions({ add: "add" }) }组合Action
actions: { actionA ({ commit }) { return new Promise((resolve, reject) => { setTimeout(() => { commit("someMutation") resolve() }, 1000) }) }, // ... actionB ({ dispatch, commit }) { return dispatch("actionA").then(() => { commit("someOtherMutation") }) } } store.dispatch("actionA").then(() => { // ... }) 或者 store.dispatch("actionB");Module如果项目庞大的话我们需要维护很多状态,这时候store会变得非常庞大,我们就需要store分割成很多模块(Module),每个模块同样拥有自己的state,getters,mutations,actions以及modules
const moduleA = { state: { a: "A" } }; const moduleB = { state: { a: "B" } }; const store = new Vuex.Store({ modules: { moduleA, moduleB } }); console.log(store.state.moduleA.a); //A console.log(store.state.moduleB.a); //B
模块内部的mutation与getter接收的第一个参数都是局部状态对象
例如 const moduleA = { state: { price: 50, count: 2 }, getters: { totalPrice: state => state.price*state.count }, mutations: { decreaseCount(state) { state.count--; } } };
模块内部的getter,根节点的状态会在第三个参数展示出来。
注:根节点的状态就是指的store.stateconst moduleB = { state: { count: 2 }, getters: { sum: (state, getters, rootState) => state.count+rootState.count } }; const store = new Vuex.Store({ state: { count: 1 }, modules: { moduleB } }); console.log(store.getters.sum);// 3
模块内部的action,局部状态:context.state根部状态:context.rootState
const moduleC = { state: { count: 2 }, mutations: { increaseCount(state) { state.count++; } }, actions: { addCount({ commit, state, rootState }) { let sum = state.count + rootState.count; if(sum % 3 === 0) { commit("increaseCount"); } } } }; const store = new Vuex.Store({ state: { count: 1 }, modules: { moduleC } }); console.log(store.state.moduleC.count);// 2 store.dispatch("addCount"); console.log(store.state.moduleC.count);// 3命名空间
默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为命名空间模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。(摘自官网)
注1:特别提出:模块内的state是嵌套的,namespaced属性不会对其产生丝毫影响。
注2:没加namespaced: true的模块会继承父模块的命名空间
代码链接const store = new Vuex.Store({ modules: { moduleA: { namespaced: true, getters: { count: () => 0 // store.getters["moduleA/count"] }, modules: { moduleB: { getters: { count1: () => 1 //继承了父模块的命名空间 store.getters["moduleA/count1"] } }, moduleC: { namespaced: true, getters: { count2: () => 2 //继承了父模块的命名空间 store.getters["moduleA/moduleC/count1"] } } } } } }); console.log(store.getters["moduleA/count"]);// 0 console.log(store.getters["moduleA/count1"]);// 1 console.log(store.getters["moduleA/moduleC/count2"]);// 2在命名空间模块内部访问全局内容
模块内部希望使用全局state与全局getter
rootState与rootGetters会作为getter第三个第四个参数传入
如
someGetter: (state, getters, rootState, rootGetters) => { //todo }const store = new Vuex.Store({ getters: { someOtherGetter: state => "ROOT" }, modules: { moduleA: { namespaced: true, getters: { someGetter(state,getters,rootState,rootGetters) { return getters.someOtherGetter; // INSIDE // return rootGetters.someOtherGetter; // ROOT }, someOtherGetter: state => "INSIDE" } } } }); console.log(store.getters["moduleA/someGetter"]);
也会通过context.rootState与context.rootGetts传入
如someAction:(context) => { //todo } 或者 someAction:({ commit, dispatch, rootState, rootGetters }) => { //todo }模块内部希望使用全局mutation与全局action,只需要在执行分发或者提交的时候在第三个参数位置传入{ root: true }
如dispatch("someOtherAction", null, {root: true}); commit("someOtherMutation", null, {root: true});栗子(没有写mutation与state的可以自行尝试)
const store = new Vuex.Store({ getters: { someOtherGetter: state => "ROOT" }, actions: { someOtherAction() { console.log("ROOT_ACTION"); } }, modules: { moduleA: { namespaced: true, getters: { someGetter(state,getters,rootState,rootGetters) { return getters.someOtherGetter; // INSIDE // return rootGetters.someOtherGetter; // ROOT }, someOtherGetter: state => "INSIDE" }, actions: { someAction({ dispatch, getters, rootGetters }) { console.log(getters.someOtherGetter);//INSIDE console.log(rootGetters.someOtherGetter);//ROOT dispatch("someOtherAction");//INSIDE_ACTION dispatch("someOtherAction", null, {root: true});//ROOT_ACTION }, someOtherAction() { console.log("INSIDE_ACTION"); } } } } }); console.log(store.getters["moduleA/someGetter"]); store.dispatch("moduleA/someAction");当我们将store里的状态映射到组件的时候,会出现下面的问题
computed: { ...mapState({ b1:state => state.moduleA.moduleB.b1, b2:state => state.moduleA.moduleB.b2 }) }, methods: { ...mapActions({ "moduleA/moduleB/action1", "moduleA/moduleB/action2" }) } 是不是比较繁琐,代码太过冗余。当然mapState、mapGetters、mapMutations、mapActions这些辅助函数都可以将空间名称字符串作为第一个参数传递,这样上面的例子可以简化成
computed: { ...mapState("moduleA/moduleB", { b1:state => state.b1, b2:state => state.b2 }) }, methods: { ...mapActions("moduleA/moduleB", { "action1", "action2" }) }当然还有一个方法,使用Vuex提供的createNamespacedHelpers创建基于某个命名空间函数,它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数。
import { createNamespacedHelpers } from "vuex"; const { mapState , mapActions } = createNamespacedHelpers("moduleA/moduleB"); export default { computed: { ...mapState({ b1:state => state.b1, b2:state => state.b2 }) }, methods: { ...mapActions({ "action1", "action2" }) } }模块重用
有时我们可能需要创建一个模块的多个实例:创建多个 store,他们公用同一个模块
在一个 store 中多次注册同一个模块
如果我们使用一个纯对象来声明模块的状态,那么这个状态对象会通过引用被共享,导致状态对象被修改时 store 或模块间数据互相污染的问题。
实际上这和 Vue 组件内的 data 是同样的问题。因此解决办法也是相同的——使用一个函数来声明模块状态
const MyReusableModule = { state () { return { foo: "bar" } }, // mutation, action 和 getter 等等... }文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/90526.html
相关文章
Vuex10分钟入门
摘要:它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。写需要的组件创建一个组件,怼下面的内容,你喜欢彬哥哪一点创建一个展示组件我喜欢彬哥打开,删掉没用的东西,直接怼下面的代码,到这里,架子就搭好了。 通过本文你将: 1.知道什么是Vuex. 2.知道为什么要用Vuex. 3.能跑一个Vuex的例子。 4.了解相关概念,面试的时候能说出一个所以然 5...
Vue.js状态管理模式 Vuex
摘要:是一个专为应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。通过方法触发在内部执行异步操作对象形式传参以载荷形式分发 showImg(https://segmentfault.com/img/bVDxBu?w=701&h=551); vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理...
vue 项目实战 (vue全家桶之--- vuex)
摘要:官方文档对的解读是是一个专为应用程序开发的状态管理模式。而就是为了解决这样的问题,把组件的共享状态抽取出来,以一个全局单例模式管理。我们还可以传入参数传入一个对象可以修改中的状态,但是并不是说随随便便修改的比如说老规矩先安装 npm install vuex --save 在看下面内容之前 你应该大概的看了一边vuex官方的文档对vuex有个大概对了解 首先 vuex 是什么? vuex...
发表评论
0条评论
Freeman
男|高级讲师
TA的文章
阅读更多
seq2seq
阅读 3402·2023-04-26 02:41
pypi
阅读 2444·2023-04-26 00:14
NameSilo域名优惠码集合汇总(无套路且可支付宝付款)
阅读 2822·2021-08-11 10:22
百万创作人的视频创意灵感,也离不开“云”
阅读 1274·2019-12-27 11:38
记一次用vue做的活动页
阅读 3570·2019-08-29 18:34
前端实例练习 - 图片覆盖层
阅读 2374·2019-08-29 12:13
使用babel7构建完美的纯ts开发环境
阅读 2950·2019-08-26 18:26
UCloud云主机自定义镜像
阅读 1833·2019-08-26 16:49
阅读需要支付1元查看