什么是 Vuex ?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
使用 Vue CLI 脚手架安装的 Vue 3.0 已经默认集成了 Vuex,下面记录的是 Vue 2.0 安装使用 Vuex 教程。
安装:
npm:
npm install vuex --save
yarn:
yarn add vuex
通过 Vue.use()
安装:
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex)
状态管理
当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏。
多个视图依赖于同一状态。
来自不同视图的行为需要变更同一状态
对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码
我们可以把组件的共享状态抽取出来,以一个全局单例模式管理。
开始使用:
安装 Vuex 之后,在 /src
目录下 创建一个 store
文件夹,然后在该文件夹内创建一个 index.js
文件。
./src/store/index.js:
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } } })
然后在 /main.js
中引入并初始化 sotre
:
import Vue from 'vue' import App from './App.vue' import store from './store' Vue.config.productionTip = false new Vue({ render: h => h(App), store: store, }).$mount('#app')
这样就可以在任意页面调用 store
中的参数了:
console.log(this.$store.state.count) # 0
核心实现:
每一个 Vuex 应用的核心就是 store
(仓库)。“store
”基本上就是一个容器,它包含着你的应用中大部分的状态 (state
)。Vuex 和单纯的全局对象有以下两点不同:
1、Vuex 的状态存储是响应式的。当 Vue 组件从 store
中读取状态的时候,若 store
中的状态发生变化,那么相应的组件也会相应地得到高效更新。
2、不能直接改变 store
中的状态。改变 store
中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
最简单的 Store
State
、Getter
、Mutation
、Action
、Module
export default new Vuex.Store({ modules: { }, state: { }, getters: { }, mutations: { }, actions: { } })
State 单一状态树:
单一状态树,用一个对象就包含了全部的应用层级状态,作为一个“唯一数据源 (SSOT)”而存在。因此,每个应用将仅仅包含一个 store
实例。可以让我们直接地定位任一特定的状态片段,在调试过程中轻易地取得整个当前应用状态的快照。
this.$store.state.<name>
最佳实践:使用计算属性获得每次 state 中变化 :
computed: { titleName () { return this.$store.state.name } },
Getters :
可以认为是 store
的计算属性,getter
的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
this.$store.getters.gettersFunc()
store
的 computed
可以接受两个参数:state
、getters
:
getters: { countAdd: (state) => (num) => { return state.count + num }, countRise: (state, getters) => (num) => { return state.count * num + getters.countAdd(5) } },
也可以在前端任意位置直接调用:
console.log(this.$store.getters.countAdd(2)) // 2
Mutation、Action Mutation(译:突变):
更改 Vuex 的 store
中的状态的唯一方法是提交 mutation
。Vuex 中的 mutation
非常类似于事件:每个 mutation
都有一个字符串的事件类型 (type) 和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state
作为第一个参数。
this.$store.commit('mutationFunc')
注意:虽然可以直接用 state
赋值的方式修改 state
的值,但是 Vuex 中只可以用 Mutation
来修改 state
的值。
不要用下面这种方法:
// 错误示范 this.$store.state.count = 3
提交载荷(Payload):
可以向 store.commit
传入额外的参数,即 mutation
的载荷(payload):
state
为要改变的 state
对象,option
(payload) 为参数对象(负载对象)
//定义 mutations: { increment (state, payload) { state.count += payload.amount } }
//调用 this.$store.commit('increment', { amount: 10 })
对象风格的提交方式:
提交 mutation
的另一种方式是直接使用包含 type
属性的对象:
store.commit({ type: 'increment', amount: 10 })
当使用对象风格的提交方式,整个对象都作为载荷传给 mutation
函数,因此 handler
保持不变:
mutations: { increment (state, payload) { state.count += payload.amount } }
建议:使用常量替代 Mutation
事件类型
mutations: { SOME_MUTATION_1 (state, options) { //... } SOME_MUTATION_2 (state, options) { //... } }
注意:Mutation
必须是同步函数。
对象展开/辅助函数:
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用辅助函数帮助我们生成计算属性,节省代码。
例如:我们可以使用 mapState
辅助函数帮助我们生成计算属性:
mapState 辅助函数:
作用:帮助我们生成计算属性
在 computed
中使用 mapState
展开函数,就可以直接用 this.count
来调用了。
这里的 this.count
就等价于 return this.$store.state.count
:
import { mapState } from 'vuex' computed: { titleName () { return this.count }, ...mapState(['count']) //映射哪些字段就填入哪些字段 },
注意:建议仅将全局使用的属性放入 state
,局部变量或组件级变量还是放入局部或组件作用域中。
mapGetters 辅助函数:
将 store
中的 getter
映射到局部计算属性。
import { mapGetters } from 'vuex' //默认名称 ...mapGetters(['countAdd', 'countRise']),
mapGetters
支持自定义名称:
//自定义名称 ...mapGetters({ myRise: 'countRise' })
mapMutations 辅助函数:
提交 mutations
:
直接使用 store
提交:
this.$store.commit('SOME_MUTATION_1')
在 methods
中使用 mapMutations
辅助函数:
import { mapMutations } from 'vuex' //methods methods: { //注册 ...mapMutations(['CHANGE_NAME']), //按钮点击 btnClick: function () { this.CHANGE_NAME({ name: 'newName' }) } }
Action(译:行动):
Action
类似于 mutation
,不同在于:
1、Action
提交的是 mutation
,而不是直接变更状态。
2、Action
可以包含任意异步操作。
通过 store.dispatch
方法触发:
this.$store.dispatch('changeNameAction', { name: 'XXX' })
通过 mapActions
:
import { mapActions } from 'vuex' ...mapActions(['changeNameAction']), this.changeNameAction({ name: 'WTF' })
组合 Action :
actionA: ({ commit }, option) => { return new Promise((resolve, reject) => { setTimeout(() => { commit('CHANGE_NAME', option) console.log('运行了actionA') resolve() }, 3000) }) }, actionB: ({ commit }, option) => { return new Promise((resolve, reject) => { setTimeout(() => { commit('CHANGE_NAME', option) console.log('运行了actionB') resolve() }, 5000) }) }, actionC: async ({ commit, dispatch }, option) => { await dispatch('actionA', option) await dispatch('actionC', { name: 'CCC' }) }
...mapActions(['actionC']), this.actionC({ name: 'AAA' }) // AAA / CCC
Modules(模块) :
store
为单一对象,内容过多,影响执行效率及代码量过多 解决:分包(模块)
1、创建 modules
目录,以业务区分不同文件进行模块划分:
src |__store |__index.js |__modules |__sectionA.js |__sectionB.js |__sectionC.js |__...
2、在 store/index.js
中引入:
import sectionA from './modules/sectionA' import sectionB from './modules/sectionB' import sectionC from './modules/sectionC' export default new Vuex.Store({ modules: { sectionA, sectionB, sectionC }, })
模块动态注册
store.registerModule('sectionA', { // ... }) store.registerModule(['sectionB','sectionC'], { // ... })
以上就是 Vuex 入门教程,有任何问题可以在下方进行评论。
未经允许不得转载:前端资源网 - w3h5 » Vue状态管理模式:Vuex入门教程