为什么会出现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 和单纯的全局对象有以下两点不同:

  1. Vuex的状态存储是响应式的。当Vuex的状态属性发生变化时,相应的组件也会更新。
  2. 无法直接修改Vuex的状态。只能通过显式的提交(commit)。

简单的Store


const store = new Vuex.Store({state: {count: 0},mutations: {increase(state) {state.count++;}}
});
store.commit('increase');
console.log(store.state.count); // 1

State

从上文我们知道Vuex是响应式的,我们如何在Vue实例中使用Vuex中的实例呢,自然离不开计算属性computed了。

在 Vue 组件中获得 Vuex 状态


//仓库
const store = new Vuex.Store({state: {count: 1}
});
//foo组件
const foo = {template: `<div>{{ count }}<div>`,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: `<div>{{ count }}<div>`,computed: {count() {// 不能使用箭头函数,不然this就不是Vue实例了// 通过this.$store获取到store实例return this.$store.state.count}}
};

如果有很多状态需要映射,我们岂不是要写好多代码,这时候需要用到mapState辅助函数,使用 mapState 辅助函数帮助我们生成计算属性。
辅助函数 实例1


const foo = {template: `<div>count: {{ count }}countAlias: {{ countAlias }}countPlusLocalCount: {{ countPlusLocalCount }}<div>`,data() {return {localCount: 2};},computed: Vuex.mapState({//只处理仓库中的countcount: 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({//只处理仓库中的countcount: 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: `<div>femaleList: {{ femaleList }}<div>`,computed: {// 属性名相同的不再阐述...Vuex.mapGetters({femaleList: 'getFemaleUsers'})}
};

Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
提交时额外的参数被称为载荷
普通风格提交方式


store.commit('mutationName', {params: '参数'
});

对象风格提交方式


store.commit({type: 'mutationName',params: '参数'
});

注意点:

  1. 设置对象新属性时
    方法1:Vue.set(obj, 'newVal', 100)
    方法2:obj = {...obj, newVal: 100};
  2. 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 } } }); ```

  3. 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')})}
};

Action

Action与Mutation的不同在于Action不直接修改状态只是做commit一个mutation、然后就是可以做异步操作。
简单的Action


const 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
  1. 模块内部的mutationgetter接收的第一个参数都是局部状态对象

    ``` 例如 const moduleA = { state: { price: 50, count: 2 }, getters: { totalPrice: state => state.price*state.count }, mutations: { decreaseCount(state) { state.count--; } } }; ```

  2. 模块内部的getter,根节点的状态会在第三个参数展示出来。
    注:根节点的状态就是指的store.state

    ``` const 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 ```

  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 ```

命名空间
默认情况下,模块内部的 actionmutationgetter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutationaction 作出响应。
如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为命名空间模块。当模块被注册后,它的所有 getteractionmutation 都会自动根据模块注册的路径调整命名。(摘自官网)
注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

  1. rootStaterootGetters会作为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']); ```

  2. 也会通过context.rootStatecontext.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);//INSIDEconsole.log(rootGetters.someOtherGetter);//ROOTdispatch('someOtherAction');//INSIDE_ACTIONdispatch('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'})
}
是不是比较繁琐,代码太过冗余。

当然mapStatemapGettersmapMutationsmapActions这些辅助函数都可以将空间名称字符串作为第一个参数传递,这样上面的例子可以简化成


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'})}
}

模块重用
有时我们可能需要创建一个模块的多个实例:

  1. 创建多个 store,他们公用同一个模块
  2. 在一个 store 中多次注册同一个模块

如果我们使用一个纯对象来声明模块的状态,那么这个状态对象会通过引用被共享,导致状态对象被修改时 store 或模块间数据互相污染的问题。

实际上这和 Vue 组件内的 data 是同样的问题。因此解决办法也是相同的——使用一个函数来声明模块状态


const MyReusableModule = {state () {return {foo: 'bar'}},// mutation, action 和 getter 等等...
}

原文地址:https://segmentfault.com/a/1190000012645384

转载于:https://www.cnblogs.com/lalalagq/p/9960288.html

Vuex-一个专为 Vue.js 应用程序开发的状态管理模式相关推荐

  1. uvex快速入门 Vue.js 应用程序开发的状态管理模式 + 库

    参考视频 vue参考文档 VueX 大型项目 管理公共数据的工具 作用: 独立与组件系统之外的,状态管理工具,采用集中式存储管理所有的组件状态,解决多组件数据通信(跨组件通信 A组件修改数据其他组件都 ...

  2. 理解vuex -- vue的状态管理模式

    2019独角兽企业重金招聘Python工程师标准>>> vuex是什么? 先引用vuex官网的话: Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管 ...

  3. Vue学习笔记(五)—— 状态管理Vuex

    介绍 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.Vuex 也集成到 Vue 的官方调试 ...

  4. vuex状态管理模式:入门demo(状态管理仓)

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.Vuex 也集成到 Vue 的官方调试工具 ...

  5. mount 返回状态_状态管理模式 — Vuex如何使用?

    Extract 试想当我们在开发一个Vue应用程序时,如果在一个项目中频繁的使用组件传参的方式来同步data中的值,一旦项目结构变得复杂,管理和维护这些数据将变得十分繁琐,为此,Vue为这些被多个组件 ...

  6. Vuex 状态管理模式(使用)

    一.Vuex简介 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 二.使用 1.安装依赖 ...

  7. 状态管理模式 - vuex 的使用介绍

    前言 大家好,不知道大家在用 vue 进行开发过程中有没有遇到这样一种场景,就是有些时候一些数据是一种通用的共享数据(比如登录信息),那么这类数据在各个组件模块中可能都会用到,如果每个组件中都去后台重 ...

  8. tmdb电影票房_TMDb Vue.js应用程序:电影数据库应用程序

    tmdb电影票房 TMDb Vue.js应用 (TMDb Vue.js app) TMDb Movie Search is a responsive Vue.js app. The Movie Dat ...

  9. Vuex状态管理模式-M

    Vuex Vuex 是一个专为 Vue.js 开发的状态管理模式.主要是是做数据交互,父子组件传值可以很容易办到,但是兄弟组件间传值(兄弟组件下又有父子组件),页面多并且一层嵌套一层的传值,非常麻烦, ...

最新文章

  1. 青少年编程竞赛交流群周报(第043周)
  2. 机器学习工业复现的 12 个要素!
  3. python import问题
  4. python精进之路 -- open函数
  5. C语言 下标运算符和指针运算符
  6. ARM再出手,软银攀登新高山
  7. 最好的android one手机,最高配置的Android One手机登场 LG推出G7 One与G7 F
  8. python爬虫网络请求超时_Python网络爬虫编写5-使用代理,处理异常和超时
  9. zabbix自定义SNMP监控项
  10. 最简单的进制转换(三张图片搞定)
  11. 测试实践:华为测试流程总结
  12. 计算机放大电路的基础知识,集成运算放大器基础知识及示例电路
  13. qemu运行linux内核很慢,bugfix: Qemu 运行 ARM Linux 5.0 必现启动死机
  14. 如何获取计算机网络地址,如何查看电脑网卡获取的IP地址是多少?
  15. windows x64和x86区别
  16. 1.thrift概述
  17. 苹果CMS对接APP源码NVUE原生渲染
  18. loadrunner 12 --录制手机app脚本
  19. 【FPGA入门十二】1bit全加器实现计算8位二进制数中1的个数
  20. 栈和队列以及线性表的区别

热门文章

  1. 2022-2028年中国茶几玻璃行业市场研究及前瞻分析报告
  2. gcc 自动识别的文件扩展名,gcc/g++ -x 选项指定语言,不同 gcc 版本 -std 编译选项支持列表
  3. win10 4步快速安装vue
  4. TCP三次握手和四次挥手的解释
  5. python中的新式类与旧式类的一些基于descriptor的概念(上)
  6. win10系统的“管理员”
  7. python -m参数
  8. 关于python 中的__future__模块
  9. 解决plsql中文显示问号(???)问题
  10. 本机无法访问虚拟机上的nignx