vuex 依赖于promise
import Vue from 'vue'import Vuex from 'vuex'/*vuex 依赖于promise*/Vue.use(Vuex)
Stage
const store = new Vuex.Store({state: {count: 0},mutations: {increment (state) {state.count++}}})store.commit('increment')console.log(store.state.count) // -> 1
为了在 Vue 组件中访问this.$storeproperty,你需要为 Vue 实例提供创建好的 store。Vuex 提供了一个从根组件向所有子组件,以store选项的方式“注入”该 store 的机制
new Vue({el: '#app',store: store,// ...methods: {increment() {this.$store.commit('increment')console.log(this.$store.state.count)}}})
// ...computed: mapState({// 箭头函数可使代码更简练count: state => state.count,// 传字符串参数 'count' 等同于 `state => state.count`countAlias: 'count',// 为了能够使用 `this` 获取局部状态,必须使用常规函数countPlusLocalState (state) {return state.count + this.localCount}})computed: {localComputed () { /* ... */ },// 使用对象展开运算符将此对象混入到外部对象中...mapState({// ...})}
Getter
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
Getter 接受 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 }]
Getter 也可以接受其他 getter 作为第二个参数:
getters: {// ...doneTodosCount: (state, getters) => {return getters.doneTodos.length}}// ...store.getters.doneTodosCount // -> 1
通过方法访问
你也可以通过让 getter 返回一个函数,来实现给 getter 传参。在你对 store 里的数组进行查询时非常有用。
getters: {// ...getTodoById: (state) => (id) => {return state.todos.find(todo => todo.id === id)}}// 注意,getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
mapGetters 辅助函数
mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:
computed: {// 使用对象展开运算符将 getter 混入 computed 对象中...mapGetters(['doneTodosCount','anotherGetter',// ...])}
Mutation
vuex通过commit修改stage的值,你可以向store.commit 写入额外的入参
注意 ,Mutation 必须是同步函数
mutations: {increment (state, n) { // 通常n会是一个对象state.count += n}}store.commit("increment", 10);// 可以用这种风格mutations: {increment (state, payload) {state.count += payload.amount}}store.commit({type: 'increment',amount: 10})
你可以在组件中使用this.$store.commit(‘xxx’)提交 mutation,或者使用mapMutations辅助函数将组件中的 methods 映射为store.commit调用(需要在根节点注入store)。
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')`})}}
Action
可以通过action处理异步,Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
- Action 可以返回一个promise实例,完成链式调用action
```javascript
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
} }, actions: { increment (context) { // action也可以接收第二个参数state.count++
} } })// context.state// context.getterscontext.commit('increment');// 可以返回promise实例return Promise.resolver();
store.dispatch(‘increment’, { // action也可以接收第二个参数 amount: 10 })
// 以对象形式分发 store.dispatch({ type: ‘incrementAsync’, amount: 10 })
调用异步 API和分发多重 mutation:```javascriptactions: {checkout ({ commit, state }, products) {// 把当前购物车的物品备份起来const savedCartItems = [...state.cart.added]// 发出结账请求,然后乐观地清空购物车commit(types.CHECKOUT_REQUEST)// 购物 API 接受一个成功回调和一个失败回调shop.buyProducts(products,// 成功操作() => commit(types.CHECKOUT_SUCCESS),// 失败操作() => commit(types.CHECKOUT_FAILURE, savedCartItems))}}
你在组件中使用this.$store.dispatch(‘xxx’)分发 action,或者使用mapActions辅助函数将组件的 methods 映射为store.dispatch调用(需要先在根节点注入store):
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')`})}}
promise
actions: {async actionA ({ commit }) {commit('gotData', await getData())},async actionB ({ dispatch, commit }) {await dispatch('actionA') // 等待 actionA 完成commit('gotOtherData', await getOtherData())}}
Module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
const moduleA = {state: () => ({count: 0}),mutations: {add() {//...},...},actions: { ... },getters: { ... }}const moduleB = {state: () => ({ ... }),mutations: { ... },actions: { ... }}const store = new Vuex.Store({modules: {a: moduleA,b: moduleB}})store.state.a // -> moduleA 的状态store.state.b // -> moduleB 的状态this.$store.commit('a/add',10)
模块内部的 action,局部状态通过context.state暴露出来,根节点状态则为context.rootState:
对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:
const moduleA = {// ...actions: {incrementIfOddOnRootSum ({ state, commit, rootState }) {if ((state.count + rootState.count) % 2 === 1) {commit('increment')}}}}const moduleA = {// ...getters: {sumWithRootCount (state, getters, rootState) {return state.count + rootState.count}}}
命名空间
模块动态注册
import Vuex from 'vuex'const store = new Vuex.Store({ /* 选项 */ })// 注册模块 `myModule`store.registerModule('myModule', {// ...})// 注册嵌套模块 `nested/myModule`store.registerModule(['nested', 'myModule'], {// ...})// 之后就可以通过 store.state.myModule 和 store.state.nested.myModule 访问模块的状态。// store.unregisterModule(moduleName) 来动态卸载模块 只能卸载动态的,不能卸载静态的。// 你可以通过 store.hasModule(moduleName) 方法检查该模块是否已经被注册到 store
在注册一个新 module 时,你很有可能想保留过去的 state,例如从一个服务端渲染的应用保留 state。你可以通过preserveState选项将其归档:store.registerModule(‘a’, module, { preserveState: true })。
当你设置preserveState: true时,该模块会被注册,action、mutation 和 getter 会被添加到 store 中,但是 state 不会。这里假设 store 的 state 已经包含了这个 module 的 state 并且你不希望将其覆写。
dispatch 与 commit 的区别
Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。
dispatch 推送一个action
commit 推送一个mutation
注意事项
模块重用
有时我们可能需要创建一个模块的多个实例,例如:
- 创建多个 store,他们公用同一个模块 (例如当runInNewContext选项是false或’once’时,为了在服务端渲染中避免有状态的单例(opens new window))
- 在一个 store 中多次注册同一个模块
如果我们使用一个纯对象来声明模块的状态,那么这个状态对象会通过引用被共享,导致状态对象被修改时 store 或模块间数据互相污染的问题。
实际上这和 Vue 组件内的data是同样的问题。因此解决办法也是相同的——使用一个函数来声明模块状态(仅 2.3.0+ 支持):
const MyReusableModule = {state: () => ({foo: 'bar'}),// mutation, action 和 getter 等等...}new Vue({data: () => {return {...}}})
