Vuex 状态管理
一、为什么使用vuex : https://www.cnblogs.com/goloving/p/9080005.html
vuex的功能 和 localstorage 的作用是一样,把数据在一个所有页面都可以存取的地方。但是vuex的数据具有响应式(类似数据双向绑定),而 localstorage 的数据是固定的,必须手动设置。
- 使用场景【不同组件中】:祖孙组件件的数据传递
- Vuex 的缺点:vuex是存储在内存中的,刷新页面 或 跳转到 项目外的页面就会清除页面内存的数据,导致页面不能正常显示。【
app虽然没有刷新功能,但是跳到外联,在回退到页面。也是相当于刷新了页面。
注意:跳出当前的单页应用,即不同html文件。再次返回时,也是相当于刷新了页面。 - 解决Vuex的这个缺点:这个问题,只要设置Vuex值时,保存到localstorage 中。在刷新页面后 从localstorage 取出,再次 赋值进去就可以了。
Vuex数据丢失的解决方案【刷新,跳转外链再返回】:https://blog.csdn.net/liuliuliu_666/article/details/93862880。
二、vuex 的使用:
https://www.cnblogs.com/hermit-gyqy/p/11270315.html(更直观) 或 https://blog.51cto.com/9161018/2351075(说明更详细) 或 https://vuex.vuejs.org/zh/guide/(官网)
2.1、State:
存储在 Vuex 中的数据和 Vue 实例中的 data
遵循相同的规则,例如状态对象必须是纯粹 (plain) 的。参考 vue的data
- 在vue组件中获得Vuex状态:由于 Vuex 的状态存储是响应式的,组件中要保持同步响应式
// 创建一个 Counter 组件 const Counter = { template: `<div>{{ count }}</div>`, computed: { count () { return this.$store.state.count } } }
- mapState 辅助函数:
mapState
函数返回的是一个对象,要将它和组件局部计算属性混合使用 就 需要用到 对象扩展运算符。
// 组件中 import { mapState } from 'vuex' computed: { // 使用对象展开运算符将此对象混入到外部对象中 ...mapState({ // ... }) }
2.2、Getters:
通常用在数据的二次处理(过滤数据...),可以理解为state的计算属性
const store = new Vuex.Store({ state: { todos: [ { id: 1, text: '...', done: true }, { id: 2, text: '...', done: false } ] }, getters: { doneTodos: state => { return state.todos.filter(todo => todo.done) } } })
-
通过属性访问:
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
-
通过方法访问:
这种方法,就是 使 getters 方法返回的是一个函数。这时通过属性访问到的就是一个方法,通过给方法传递参数就可以获取不同的数据。getters: { // ... getTodoById: (state) => (id) => { return state.todos.find(todo => todo.id === id) } } store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
注意,getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。
感悟:实践中,一次性将项目用到的所有字典表拉取过来,就是通过getter方法访问的方式,筛选对应的字典列表数据的。 -
mapGetters 辅助函数:
mapGetters
辅助函数仅仅是将 store 中的 getter 映射到局部计算属性.import { mapGetters } from 'vuex' export default { // ... computed: { // 使用对象展开运算符将 getter 混入 computed 对象中 ...mapGetters([ 'doneTodosCount', 'anotherGetter', // ... ]) } }
防止和 局部计算属性同名,可以给 映射的getter属性重命名
...mapGetters({ // 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount` doneCount: 'doneTodosCount' })
2.3、mutations:
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
const store = new Vuex.Store({ state: { count: 1 }, mutations: { increment (state) { // 变更状态 state.count++ } } })
- 提交载荷(Payload):
你可以向store.commit
传入额外的参数,即 mutation 的 载荷(payload)
mutations: { increment (state, payload) { state.count += payload.amount } }
如上 mutilation,我们有2种提交方式:
- 载荷方式的提交方式:【推荐】
store.commit('increment', { amount: 10 })
- 对象风格的提交方式:
store.commit({ type: 'increment', amount: 10 })
- 载荷方式的提交方式:【推荐】
- 使用常量替代 Mutation 事件类型
- 在组件中提交 Mutation:
使用 mapMutations
辅助函数将组件中的 methods 映射为store.commit
调用import { mapMutations } from 'vuex' export default { // ... methods: { ...mapMutations([ 'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')` // `mapMutations` 也支持载荷: 'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)` ]), ...mapMutations({ add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')` }) } }
2.4、Action:
- 分发 Action:
Actions 支持同样的载荷方式和对象方式进行分发
- 载荷方式:
store.dispatch('incrementAsync', { amount: 10 })
- 对象方式:
store.dispatch({ type: 'incrementAsync', amount: 10 })
- 载荷方式:
- 在组件中分发 Action:
使用mapActions
辅助函数将组件的 methods 映射为store.dispatch
调用import { mapActions } from 'vuex' export default { // ... methods: { ...mapActions([ 'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')` // `mapActions` 也支持载荷: 'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)` ]), ...mapActions({ add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')` }) } }
- 组合 Action:
需要明白store.dispatch
可以处理被触发的 action 的处理函数返回的 Promise,并且store.dispatch
仍旧返回 Promise。actions: { async actionA ({ commit }) { commit('gotData', await getData()) }, async actionB ({ dispatch, commit }) { await dispatch('actionA') // 等待 actionA 完成 commit('gotOtherData', await getOtherData()) } }
2.5、Modules:
Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。
- 模块的局部状态:
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。const moduleA = { state: () => ({ count: 0 }), mutations: { increment (state) { // 这里的 `state` 对象是模块的局部状态 state.count++ } }, getters: { doubleCount (state) { return state.count * 2 } } }
同样,对于模块内部的 action,局部状态通过
context.state
暴露出来,根节点状态则为context.rootState
const moduleA = { // ... actions: { incrementIfOddOnRootSum ({ state, commit, rootState }) { if ((state.count + rootState.count) % 2 === 1) { commit('increment') } } } }
对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:
const moduleA = { // ... getters: { sumWithRootCount (state, getters, rootState) { return state.count + rootState.count } } }
- 命名空间:
默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
如果希望你的模块具有更高的封装度和复用性,你可以通过添加namespaced: true
的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
-
在带命名空间的模块内访问全局内容:
如果你希望使用全局 state 和 getter,rootState
和rootGetters
会作为第三和第四参数传入 getter,也会通过context
对象的属性传入 action。 - 在带命名空间的模块注册全局 action:
若需要在带命名空间的模块注册全局 action,你可添加root: true
,并将这个 action 的定义放在函数handler
中 - 带命名空间的绑定辅助函数:【推荐使用 createNamespacedHelpers 创建基于命名空间的 辅助函数】
当使用mapState
,mapGetters
,mapActions
和mapMutations
这些函数来绑定带命名空间的模块时,写起来可能比较繁琐。可以将模块的空间名称字符串作为第一个参数传递给上述函数computed: { ...mapState('some/nested/module', { a: state => state.a, b: state => state.b }) }, methods: { ...mapActions('some/nested/module', [ 'foo', // -> this.foo() 'bar' // -> this.bar() ]) }
也可以通过使用
createNamespacedHelpers
创建基于某个命名空间辅助函数。import { createNamespacedHelpers } from 'vuex' const { mapState, mapActions } = createNamespacedHelpers('some/nested/module') export default { computed: { // 在 `some/nested/module` 中查找 ...mapState({ a: state => state.a, b: state => state.b }) }, methods: { // 在 `some/nested/module` 中查找 ...mapActions([ 'foo', 'bar' ]) } }
-
- 模块动态注册:
在 store 创建之后,你可以使用store.registerModule
方法注册模块:import Vuex from 'vuex' const store = new Vuex.Store({ /* 选项 */ }) // 注册模块 `myModule` store.registerModule('myModule', { // ... }) // 注册嵌套模块 `nested/myModule` store.registerModule(['nested', 'myModule'], { // ... })
- 模块重用:【前端渲染不会用到,服务端渲染可能会用到。了解下就可以】
三、Vuex规范目录结构:
| - store | - actions.js | - getters.js | - index.js | - mutations.js | - mutations_type.js # # 该项为存放 mutaions 方法常量的文件,按需引入。 | | - modules | _ Astore.js
说明:1、mutations_type.js 文件(mutations的事件类型,即方法名称)的目的就是为了方便多个地方使用他,可能出现的拼写错误。https://www.jianshu.com/p/9802842448bc/
2、modules 中的 store 一般直接放在一个文件就可以了,如果 Astore 局部模块状态多 也 可以进行细分。