Vuex
vuex初始
vuex 定义 vuex 是一个状态管理的插件,可以解决不同组件之间的数据共享和数据持久化。通俗点的说法就是全局的数据仓库,在组件里面的data是组件里的数据仓库也就是局部的数据仓库,全局数据仓库里面的数据可以在任意组件中共享。
vuex 是一个专门为 vue.js 设计的集中式状态管理架构。状态?我把它理解为在 data 中的属性需要共享给其他 vue 组件使用的部分,就叫做状态。
简单的说它就是全局 data.
流程
State 的数据渲染到 Component 中,Component dispatch(分发)
Actions(异步操作,与 BackEnd(后端) API 相关的操作),再由 Actions commit(提交)
Mutations(要执行的同步操作),Mutations 再修改 State 的数据,State 的数据再渲染到 Component 中,如此循环。
当然中间如果没有异步操作,可以跳过 dispatch 到 Actions 的操作,直接 commit Mutations 执行同步操作。
当然直接修改 State 的数据也可以实现,但这样修改不会被反应在 Devtools 中,Devtools 不会保存数据修改的快照,所以不要这样。只有是 Mutations 的操作,才会被反应到 Devtools,所以这也是 Mutations 中不要写异步操作的原因(它也不会被记录到 Devtools 中),这样 Devtools 的数据(和快照)就会不准确,和页面显示不一致。
安装vuex
npm install vuex --save
- 在 main.js 的同级目录新建store文件夹 里面新建一个 index.js
- 在 index.js 里面引入 vuex
1 | import Vue from 'vue' |
- 向外暴露vuex实例
1 | const store = new Vuex.Store({ // 注意调用的是 Vue.Store() 方法来创建实例 |
- state 一- vuex store 实例的根状态对象,用于定义共享的状态变量,就像 Vue 实例中data
- mutations 一 可以理解为 store的methods(同步操作 )
- getters 一 读取器,外部程序通过它获取变量的具体值,或者在取值前做一些计算(可以认为是 store 的计算属性)
- actions 以理解为 store的methods(异步操作 如ajax)
- modules 以模块化的方式写vuex (如购物车对应一个module 个人中心对应一个module )
- 挂载到vue实例类上
1 | import store from './store' |
开始
获取state里面的值
- 在state里面放入数据
1 | export default new Vuex.Store({ |
- 在某个组件获取这个数据 用computed来接收这个数据
1 | computed: { |
更改state里面的值 mutations(同步提交)
更改state里面的值 更改 Vuex 的 store 中的状态的 唯一方法 是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。
“同步模式”就是上一段的模式,后一个任务等待前一个任务结束,然后再执行。
每个 mutation 都建议执行单一功能,比如在购物车中,可能会有 addCart (添加购物车) 的 mutation,它里面可能会有 商品数量 + 1 ,和添加新商品两种功能。我们应该把这两种功能写成两个 mutation,而不是被整合到 addCart 中。而判断数量 + 1还是添加新商品的逻辑,建议放到 action 中,它不止可以处理异步,还可以处理复杂的逻辑。
在mutation里面写入更改的函数 函数的第一个参数代表 state 更改vuex中的state是通过更改这个参数实现的
Mutation 必须是同步函数
为什么必须是同步函数呢 因为在 devtools 日志里面监测不到数据的变化
1 | export default new Vuex.Store({ |
提交载荷(Payload) 其实就是提交的时候附带参数 只能提交一个载荷
1 | export default new Vuex.Store({ |
提交多个载荷, vuex 不支持提交多个载荷 提交多个载荷需换种写法, 换成对象或数组
1 | export default new Vuex.Store({ |
mutations 的另一种提交风格
在此之前的 this.$store.commit('mutationName')
是普通的提交风格,还有另外一种提交风格,这样就是提交 payload(载荷) 了。
1 | <!-- 某个组件 --> |
action(异步操作,之后再通过 mutation 来更改state)
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。 // action 里面提交数据是提交mutation 在mutation里面是直接修改state
- Action 可以包含任意异步操作。
- 它会传入一个 context 上下文对象作为第一个参数(如果是写在 new 出来的 Vue.Store 里,那就是 new 出来的 Vue.Store,如果是写在 module 里,那就指模块的上下文,可以打印一下,相对于根来说,多出来来了 rootState 和 rootGetters 的属性),commit 的方法,只会在当前的上下文中进行寻找,第二个参数可选,是 payload,传入的参数(如果是第二种提交风格就会拿到一个对象,正常提交就会拿到传入的值)。
this.$store.dispatch(actionName, payload)
最后将返回传入的 action 函数的结果,如果没有 return ,就是 undefined
1 | // 简单用法 |
异步回调
完成异步操作后,如果我们想要执行一个回调函数,可以这样写
1 | <!-- 某个组件 --> |
但这样写传递不了信息,所以就有了下面的对象写法。
1 | <!-- 某个组件 --> |
我们发现,上面这种写法虽然实现了回调函数,但由于携带的信息(message)和回调的函数(success)写到一起去了,不够优雅。所以下面用了 Promise 来分离它们。
1 | <!-- 优雅地执行完成 action 的回调函数 --> |
getters (相当于计算属性)
当我们在多个组件里都用到了 State 里的属性,并把它做成了一个 computed 属性,并在页面中使用,但每个组件都写一遍 computed ,非常麻烦,我们可以在全局声明一次 getters 即可在各个组件中达到声明了 computed 一样的效果。它接受两个参数,第一个是 state,第二个是 getters。
1 | export default new Vuex.Store({ |
和 computed 一样,它也可以直接返回一个方法,用来接收传入值
1 | export default new Vuex.Store({ |
Module vuex模块化
vuex官方解释
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。
通俗点的说法
当我们写大型的项目的时候 我们所有的需要共享的信息或状态都写在同一个 state 里面 导致 state 或其他的操作比较臃肿 那么 为了解决这个问题 vuex提出了模块化 modules.
下面是我们模块化的写法,在 main.js 的同级新建一个 store 文件夹 在这个 store 文件夹新建一个 index.js 里面写上一些内容。
1 | import Vue from 'vue' |
在 index.js 的同级新建一个 modules 的文件夹 这个文件夹分别有两个 js 分别为 moduleA 和 moduleB 内容分别为以下内容,再在 main.js 里面引入 index.js ,这个模块化的写法就已经完成 。
1 | const moduleA = { // moduleA |
注意,没有开启命名空间时,可以直接写 this.$store.commit('addNum')、this.$store.dispatch('asyncAddNum', 1)、this.$store.getters.addNums
这样类似的写法,而不需要像访问模块里的 state 一样,加上 .模块的前缀,如 this.$store.state.a.num
,因为没有开启命名空间时,前面的三种属性的访问都会先去根 Store 中寻找,找不到就会自动继续去子模块中寻找,直到找到为止。
模块化注意点
- 对于上面的模块化的写法 只有state是模块化的 其他的诸如 getters mutations actions不是模块化
- 如果想实现getters mutations actions实现模块化 那么 得在每个模块的js里面加入 *命名空间 *
namespaced: true,
- 启用命名空间之后 获取getters mutations actions的写法都要变
1 | const moduleA = { // moduleA |
- 启用命名空间后辅助函数 mapState mapGetters mapMutations mapActions 的写法
- 启用命名空间后的mapState的写法
1 | ...mapState({ |
- 启用命名空间后的mapGetters的写法
1 | ...mapGetters({ |
- 启用命名空间后的mapMutations的写法
1 | ...mapMutations({event : "b/addNum"}) // this.event(3) 附带参数的写法 |
- 启用命名空间后的mapActions的写法
1 | ...mapActions({events : "b/syncAddNum"}), // this.events(4) 附带参数的写法 |
vuex辅助函数 帮助我们节省代码的语法糖
- mapState – 当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键。(其实就是我们在vuex里面定义的变量 接收的时候还要再定义变量去接收 很麻烦 vuex辅助函数呢 可以帮我们解决这种麻烦)
- mapGetters – 将store中的多个getter映射到局部组件的计算属性中
- mapMutations 将组件中的 methods 映射为 store.commit 调用。
- mapActions 将组件的 methods 映射为 store.dispatch 调用
mapState 的三种写法
1 | export default { |
mapGetters getters的辅助函数
1 | import HelloWorld from '@/components/HelloWorld.vue' |
mapMutations mutations的辅助函数的两种写法
1 | methods: { |
mapActions actions的辅助函数
1 | methods: { |
基本使用 demo
1 | // store/index.js |
Modules , 用于解决文件过于臃肿的问题, 把它们拆分成各个对应的子模块 namespace: true
1 | // store/index.js |