首页 前端知识 Vuex的使用

Vuex的使用

2024-06-20 09:06:54 前端知识 前端哥 227 373 我要收藏

Vuex

  • 一. 介绍
    • Vuex 是什么
    • 安装
    • 引入
    • 使用
    • 示例
  • 二、state
    • 1. 在组件中展示状态
    • 2. mapState 辅助函数
    • 3. 对象运算展开符
    • 4. 组件仍然保有局部状态
  • 三、Getter
    • 1. 组件中通过属性访问
    • 2. 组件中通过方法访问
    • 3. mapGetters 辅助函数
  • 四、Mutation
    • 1. 提交载荷(Payload)
    • 2. 对象风格的提交方式
    • 3. 使用常量替代 Mutation 事件类型
    • 4. Mutation 必须是同步函数
    • 5. 在组件中提交Mutation
  • 五、Action
    • 1. 注册action
    • 2. 分发action
      • 2.1 action 里内部可执行异步
      • 2.2 action 支持载荷方式和对象方式分发
      • 2.3 在组件中分发 Action
    • 3. 组合 Action
  • 六、Module
    • 1. 使用
    • 2. 命名空间
      • 2.1 在带命名空间的模块内访问全局内容
      • 2.2 在带命名空间的模块注册全局 action
      • 2.3 在组件中与 mapState, mapGetters, mapActions 和 mapMutations 连用
    • 3. 模块动态注册
      • 3.1 注册模块
      • 3.2 注册嵌套模块
      • 3.2 卸载模块
      • 3.3 判断模块是否被注册到store
      • 3.4 保留 state
    • 4. 带命名空间的绑定函数
      • 4.1 使用模块的空间名称字符串
      • 4.2 使用createNamespacedHelpers
    • 5. 模块重用
  • 七. 总结
  • 八. 项目结构


一. 介绍

Vuex 是什么

Vuex是 Vue 的 共享状态管理,小型项目使用 store模式 即可,中大型项目需要使用 VueX。

安装

npm install vuex --save

注意:在vue2中,要用 vuex 的3版本;在vue3中,要用vuex的4版本。

引入

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

使用

Vuex应用的核心是 store(仓库),它包含应用中的状态state。Vuex和单纯的全局对象有两种不同:

  1. Vuex 的状态存储是 响应式 的,若store中的状态发生改变,相应的组件也会更新;
  2. 不能直接改变store中的状态,要通过mutation中的commit进行提交。

示例

下面是一个创建store并使用的示例:

  1. 新建 vuex-store.js(与main.js同目录):
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  // state: 存放变量
  state: {
    count: 1
  },
  // mutation: 存放方法
  mutations: {
    increment (state) {
      state.count++
    }
  }
})
  1. 组件中引入:

需要在Hello.vue中引入store才可以使用vuex。

<template>
  <div class="hello">{{state.count}}</div>
</template>

<script>
import store from '../vuex-store.js'
export default {
  name: 'HelloWorld',
  // 变量通常在计算属性中返回
  computed: {
    state () {
      return store.state
    }
  }
  methods: {
    increase () {
      store.commit('increment')
    }
  }
}
</script>
  1. 全局注入Store机制:

即使在组件中不引入store也可以使用vuex。

  • 首先,在main.js中注入:
import store from './vuex-store'

new Vue({
  el: '#app',
  // 注入: 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件 
  store,
  router,
  components: { App },
  template: '<App/>'
})
  • 组件中直接使用(子组件能通过 this.$store 访问到):
<template>
  <div>
    <div class="hello">{{ $store.state.count }}</div>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  methods: {
    increase () {
      this.$store.commit('increment')
      console.log(this.$store.state.count)
    }
  }
}
</script>

二、state

1. 在组件中展示状态

从 store 实例中 读取状态是通过 computed计算属性 中返回某个状态:

computed: {
	count() {
	    return this.$store.state.count;
	}
}

2. mapState 辅助函数

当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余,我们可以使用 mapState 辅助函数帮助我们生成计算属性:

<template>
  <div>
    <div class="hello">{{ count }}</div>
    <div class="hello">{{ countAlias }}</div>
    <div class="hello">{{ total }}</div>
  </div>
</template>

<script>
// 引入辅助函数 mapState
import {mapState} from 'vuex'

export default {
  name: 'HelloWorld',
  data () {
    return {
      num: 10
    }
  },
  computed: mapState({
  	// 箭头函数可使代码更简练
    count: state => state.count,
    // 传字符串参数 'count' 等同于 `state => state.count`
    countAlias: 'count',
    // 为了能够使用 `this` 获取局部状态,必须使用常规函数
    total (state) {
      return state.count + this.num
    }
  })
}
</script>

注意:当映射的 计算属性 的名称与 state 中的变量名相同时,可以给 mapState 传一个字符串数组:

computed: mapState([
	// 映射 this.count 为 store.state.count
  'count'
])

3. 对象运算展开符

mapState 函数返回的是一个对象,当 局部计算属性 与 mapState 混合使用,可以使用 对象展开运算符 将此对象混入到外部对象中:

computed: {
  showNum () {
      return `数量是${this.num}`
    },
    // 对象展开运算符:将此对象混入到外部对象中
  ...mapState({
      count: state => state.count
    })
}

4. 组件仍然保有局部状态

使用 Vuex 并不意味着你需要将所有的状态放入 Vuex。虽然将所有的状态放到 Vuex 会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。

三、Getter

有时候我们需要从 store 中的 state 中派生出一些状态,例如 对列表进行过滤并计数:
vuex-store.js:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '文案1', done: true },
      { id: 2, text: '文案2', done: false }
    ]
  }
})

组件:

computed: {
  doneTodosCount () {
    return this.$store.state.todos.filter(todo => todo.done).length
  }
}

如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它——无论哪种方式都不是很理想。

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

vuex-store.js:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '文案1', done: true },
      { id: 2, text: '文案2', done: false }
    ]
  },
  
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})

1. 组件中通过属性访问

Getter 会暴露为 store.getters 对象,可以以 属性的形式 访问这些值:

<div>{{$store.getters.doneTodos.length}}</div>

this.$store.getters.doneTodos // -> [{ id: 1, text: '文案1', done: true }]
this.$store.getters.doneTodos.length // -> 1

getters 的参数

  • 第一个参数 :state ;
  • 第二个参数 :getters ;
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    },
    doneTodosCount: (state, getters) => {
      return getters.doneTodos.length
    }
  }

我们可以任何组件中使用它:

this.$store.getters.doneTodosCount // -> 1

注意:getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存。

2. 组件中通过方法访问

通过让 getter 返回一个函数,来实现给 getter 传参(对 store 里的数组进行查询时非常有用)。

vuex-store.js:

getters: {
  // ...
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}

组件中:

console.log(this.$store.getters.getTodosById(2));
-> { id: 2, text: '文案2', done: false }

注意:getter 在通过方法访问时,每次都会去进行调用,而不会项属性访问那样去缓存结果。

3. mapGetters 辅助函数

将 store 中的 getter 映射到局部计算属性,使用方法和 mapState 相同。

<template>
  <div>
    <div>{{ doneTodosCount }}</div>
  </div>
</template>

<script>
// 1.引入辅助函数 mapState
import {mapGetters} from 'vuex'

export default {
  name: 'HelloGetter',
  // 2. 传入数组
  computed: mapGetters([
    'doneTodos',
    'doneTodosCount',
    'getTodosById'
  ]),
  mounted () {
  	// 3. 无论是getters中的属性和方法都可以直接使用
    console.log(this.doneTodos)
    console.log(this.getTodosById(2))
  },
}
</script>

如果想将一个 getter 属性另取一个名字,可以使用对象形式:

  created () {
    console.log(this.getData(2))
  },

  computed: {
    ...mapGetters({
    // 把 `this.getData` 映射为 `this.$store.getters.getTodosById`
      getData: 'getTodosById'
    })
  }

四、Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

export default new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})

不能直接调用一个 mutation handler,当触发一个类型为 increment 的 mutation 时,需要以相应的 type 调用 store.commit 方法:

this.$store.commit('increment')

1. 提交载荷(Payload)

mutations 中的方法,第一个参数为 state,第二个参数就叫做 载荷(payload),通常为对象并且包含多个字段。

 // ...
  mutations: {
    increment (state, payload) {
      state.count += payload.amount
    }
  }

在组件中调用:

this.$store.commit('increment', {
    amount: 5
})

2. 对象风格的提交方式

提交 mutation 的另一种方式是直接使用包含 type 属性的对象:

this.$store.commit({
	type: 'increment',
	amount: 5
})

处理函数保持不变

3. 使用常量替代 Mutation 事件类型

使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。这样可以使 linter 之类的工具发挥作用,同时把这些 常量 放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然。

  • 创建一个 mutation-types.js:
export const [INCREMENT, DECREMENT] = ['INCREMENT', 'DECREMENT']
  • 在 store.js 中引入:
import {INCREMENT, DECREMENT} from './mutation-types'
...
  mutations: {
    // 可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
    [INCREMENT] (state, payload) {
      state.count += payload.amount
    },
    [DECREMENT] (state, payload) {
      state.count -= payload.amount
    }
  }
  • 在组件中使用:
  methods: {
    onAddClick () {
     // INCREMENT
      this.$store.commit(INCREMENT, {
        amount: 5
      })
    },
    
    onDelClick () {
      // DECREMENT
      this.$store.commit({
        type: DECREMENT,
        amount: 1
      })
    }
  }

用不用常量取决于你——在需要多人协作的大型项目中,这会很有帮助。但如果你不喜欢,你完全可以不这样做。

4. Mutation 必须是同步函数

mutation 中不允许有回调函数,因为任何在回调函数中进行的状态的改变都是不可追踪的。

5. 在组件中提交Mutation

你可以在组件中使用 this.$store.commit(‘xxx’) 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。
vuex-store.js:

export default new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      state.count++
    },
    decrement (state) {
      state.count--
    },
    incrementBy (state, payload) {
      state.count += payload.amount
    },
    decrementBy (state, payload) {
      state.count -= payload.amount
    }
  }
})

组件:

import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
      'decrement', // 将 `this.decrement()` 映射为 `this.$store.commit('decrement')`
      // `mapMutations` 也支持载荷:
      'incrementBy', 
      // 将 `this.incrementBy({amount: 5})` 映射为 `this.$store.commit('incrementBy',{amount: 5})`
      'decrementBy' 
      // 将 `this.decrementBy({amount: 1})` 映射为 `this.$store.commit('decrementBy',{amount: 1})`
    ]),
    ...mapMutations({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
      del: 'decrement' // 将 `this.del()` 映射为 `this.$store.commit('decrement')`
    })
  }
}

五、Action

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态,而 Mutation是直接改变状态;
  • Action 可以包含任意异步操作,Mutation 包含的必须是同步操作。

1. 注册action

  actions: {
    increment (context) {
      context.commit('increment')
    }
  }

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。当我们在之后介绍到 Modules 时,你就知道 context 对象为什么不是 store 实例本身了。

通常会使用ES5参数解构来简化代码:

  actions: {
    increment ({ commit }) {
      commit('increment')
    }
  }

2. 分发action

Action 通过 store.dispatch 方法触发:

this.$store.dispatch('increment')

2.1 action 里内部可执行异步

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 300)
  }
}

2.2 action 支持载荷方式和对象方式分发

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
},
  
actions: {
  increment ({ commit }, payload) {
    commit('increment', payload)
  }
}

(1) 载荷方式分发:

this.$store.dispatch('increment', {
	amount: 5
})

(2) 对象方式分发:

this.$store.dispatch({
	type: 'increment',
	amount: 5
})

2.3 在组件中分发 Action

使用 mapActions 辅助函数

// 1. 引入 mapActions 辅助函数
import { mapActions } from 'vuex'

export default {
  // ...
  methods: {
    ...mapActions([
      'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`

      // `mapActions` 也支持载荷:
      'decrement' // 将 `this.decrement(amount)` 映射为 `this.$store.dispatch('decrement', amount)`
    ]),
    ...mapActions({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
    })
  }
}

3. 组合 Action

action 通常是异步的,通过组合多个 action,以处理更加复杂的异步流程。

首先, store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise:

actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  }
}

现在你可以:

store.dispatch('actionA').then(() => {
  // ...
})

在另外一个 action 中也可以:

actions: {
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}

最后,如果我们利用 async / await (opens new window),我们可以如下组合 action:

// 假设 getData() 和 getOtherData() 返回的是 Promise
actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}

一个 store.dispatch 在不同模块中可以触发多个 action 函数。在这种情况下,只有当所有触发函数完成后,返回的 Promise 才会执行。

六、Module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module),每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块(modules)。

作用是 模块化管理,处理各自的业务逻辑。

1. 使用

(1)首先,创建a.js 和 b.js;

a.js:

export default {
  state: () => ({
    count: 1
  }),

  getters: { ... }
  mutations: { ... },
  actions: { ... }
}

b.js:

export default {
  state: () => ({
    count: 2
  }),

  getters: { ... }
  mutations: { ... },
  actions: { ... }
}

(2)然后,在 store.js 中引入 a.js 和 b.js:

import moduleA from './module/a'
import moduleB from './module/b'

export default new Vuex.Store({
  state: 0,
  ...
  modules: {
    a: moduleA,
    b: moduleB
  }
})

(3)在组件中获取 state 中的 count :

this.$store.state.count    // -> 全局store 中的count: 0
this.$store.state.a.count  // -> moduleA 中的count: 1
this.$store.state.b.count  // -> moduleB 中的count: 2

(4)在组件中使用 mutations:
在这里插入图片描述

this.$store.commit('increment')

在这里插入图片描述

会发现触发了全局store,a,b 中三个全部的mutations,如果只想触发 moduleA 中的 increment,需要引入命名空间的概念。

2. 命名空间

默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。

如果希望你的模块具有更高的封装度和复用性,可以通过添加 namespaced: true 的方式使其成为带命名空间的模块,再通过模块路径去触发。
这样当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。

a.js:

export default {
  // 开启命名空间
  namespaced: true,
  
  state: () => ({
    count: 1
  }),
  getters: { ... }
  mutations: { ... },
  actions: { ... }
}

在这里插入图片描述

在组件中去触发 a 中的 increment 方法:

this.$store.commit('a/increment')

在这里插入图片描述

在组件中去触发 b 中的 increment 方法:

this.$store.commit('b/increment')

在这里插入图片描述
同理 getters,actions 提交 mutations 也需要添加路径:

this.$store.getters('a/getOdd')
this.$store.dispatch('a/increment')

2.1 在带命名空间的模块内访问全局内容

rootStaterootGetters 作为参数传入,即可访问全局 store 中的 state 和 getters 。

a.js:

export default {
  namespaced: true,
  state: () => ({
    count: 1,
    arr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  }),

  getters: {
    getOdd (state) {
      return state.arr.filter(item => {
        return item % 2 === 0
      })
    },
    getOddLength (state, getters) {
      return getters.getOdd.length
    },
    // 第三/四个参数:rootState / rootGetters
    getOddTotalSum (state, getters, rootState, rootGetters) {
      return getters.getOddLength + rootState.arr.length
    }
  },

  mutations: {
    increment (state, payload) {
      state.count += payload.amount
    }
  },

  actions: {
  // 传入 rootState , rootGetters                                                                                                                                                                                                 
    increment ({commit, rootState, getters, rootGetters}, payload) {
      commit('increment', payload)
      // console.log(rootState.count) -> 0
      // console.log(getters.getOdd)  -> 'a/getOdd'
      // console.log(rootGetters.getOdd) // -> 'getOdd'
    }
  }
}

2.2 在带命名空间的模块注册全局 action

若需要在带命名空间的模块注册全局 action,可添加 root: true,并将这个 action 的定义放在函数 handler 中。
a.js:

  actions: {
  	// 之前的 increment 是函数形式,现在是对象形式
    increment: {
      // 全局注册
      root: true,
      // handler => 之前的 increment
      handler ({commit}, payload) {
        commit('increment', payload)
      }
    }
  }

vuex-store.js:

  actions: {
	// 模块中的increment不可与全局store中的increment重名
	// 所以我们给它命名为 `root_increment`
    root_increment ({dispatch}, payload) {
      // 调用在a.js中全局注册的 increment action
      dispatch('increment', payload)
    }
  }

在组件中调用 root_increment:

this.$store.dispatch('root_increment', {
    amount: 1
})

在这里插入图片描述
在这里插入图片描述

2.3 在组件中与 mapState, mapGetters, mapActions 和 mapMutations 连用

vuex-store.js:

<template>
  <div>
      <div>全局store:{{ count }}</div>
      <div>a:{{ count_a }}</div>
      <div>b:{{ count_b }}</div>
      <button @click="add({amount: 1})">+1</button>
      <button @click="decrement({amount: 1})">-1</button>
  </div>
</template>

<script>
import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'

export default {
  name: 'HelloModule',
  computed: {
    ...mapState({
      count: state => state.count,
      count_a: state => state.a.count,
      count_b: state => state.b.count
    }),
    ...mapGetters({
      getOddTotalSum: 'a/getOddTotalSum'
    })
  },
  methods: {
    ...mapMutations({
      add: 'a/increment'
    }),
    ...mapActions([
      'decrement'
    ])
  },
  created () {
    console.log(this.getOddTotalSum)
  }
}
</script>

3. 模块动态注册

3.1 注册模块

在 store 创建之后,可以使用 store.registerModule 方法注册模块。

vuex-store.js:

import Vuex from 'vuex'

const store = new Vuex.Store({ /* 选项 */ })

// 动态注册模块 `c`
store.registerModule('c', {
  state: {
    count: 4
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

export default store

在组件中访问 c 模块下的count变量:

console.log(this.$store.state.c.count);   // 4

3.2 注册嵌套模块

注册嵌套模块, store.registerModule 方法第一个参数传入数组。

vuex-store.js:

// 注册嵌套模块 `c/myModule`
store.registerModule(['c', 'myModule'], {
  // ...
})

// 注册嵌套模块 `c/myModule/nested`
store.registerModule(['c', 'myModule','nested'], {
  // ...
})

在组件中访问嵌套模块下的count变量:

console.log(this.$store.state.c.myModule.count);   
console.log(this.$store.state.c.myModule.nested.count);   

模块动态注册功能使得其他 Vue 插件可以通过在 store 中附加新模块的方式来使用 Vuex 管理状态。例如,vuex-router-sync 插件就是通过动态注册模块将 vue-router 和 vuex 结合在一起,实现应用的路由状态管理。

3.2 卸载模块

在动态创建模块之后,可以使用 store.unregisterModule(path) 方法来动态卸载模块。
vuex-store.js:

// 卸载模块 `c`
store.unregisterModule('c')

// 卸载嵌套模块 `c/myModule/nested`
store.unregisterModule(['c', 'myModule','nested'])

注意:

  • 参数 path 可以是字符串,也可以是数组;
  • store.unregisterModule() 只能卸载动态注册的模块,不可卸载store中声明的静态模块(即创建 store 时声明的模块,例如:模块a,b)。

3.3 判断模块是否被注册到store

可以通过 store.hasModule(path) 方法检查该模块是否已经被注册到 store。

vuex-store.js:

// 检查是否注册模块 `c`
console.log(store.hasModule('c'));   // true

// 检查是否注册嵌套模块 `c/myModule/nested`
console.log(store.hasModule(['c', 'myModule','nested']));    // true

3.4 保留 state

当设置 preserveState: true 时,嵌套模块会保留上一级模块的state。

vuex-store.js:

store.registerModule('c', {
  preserveState: true
})

当设置 preserveState: true 时,该模块会被注册,action、mutation 和 getter 会被添加到 store 中,但是 state 不会。这里假设 store 的 state 已经包含了这个 module 的 state 并且你不希望将其覆写。

4. 带命名空间的绑定函数

当有许多嵌套模块时,mapState, mapGetters, mapActions 和 mapMutations 写起来会比较繁琐:

vuex-store.js:

store.registerModule(['c', 'myModule', 'nested'], {
  namespaced: true,
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment ({commit}) {
      commit('increment')
    }
  }
})

组件中:

  computed: {
    ...mapState({
      nested: state => state.c.myModule.nested.count
    }),
    
  methods: {
    ...mapActions({
      nestedAdd: 'c/myModule/nested/increment'
    })
  }    

4.1 使用模块的空间名称字符串

对于上述情况,我们可以简化为:

组件中:

  computed: {
    ...mapState('c/myModule/nested',{
      nested: state => state.count
    }),
    
  methods: {
    ...mapActions('c/myModule/nested', {
      nestedAdd: 'increment'
    })
  }    

将模块 路径 作为第一个参数传递给上述函数,这样所有绑定都会自动将该模块作为上下文。

4.2 使用createNamespacedHelpers

可以通过使用 createNamespacedHelpers 创建基于某个命名空间辅助函数。它返回一个对象,对象里有新的绑定在给定命名空间值上的组件绑定辅助函数。
组件中:

// 1. 引入 createNamespacedHelpers
import { createNamespacedHelpers } from 'vuex'

// 2. 创建辅助函数
// 注释之前的引入代码
// import {mapState, mapActions} from 'vuex'
const {mapState, mapActions} = createNamespacedHelpers('c/myModule/nested')

// 3. 使用
export default {
  computed: {
    ...mapState({
      nested: state => state.count
    })
  },
  methods: {
    ...mapActions([
      'increment'
    ])
  }
}

5. 模块重用

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

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

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

 const myModule = {
  state: () => ({
    count: 1
  }),
  // mutation, action 和 getter 等等...
}

七. 总结

辅助函数的位置:

  • mapState, mapGetters :写在computed中;
  • mapMutations, mapActions :写在methods中。

代表的意义:

  • state: 变量;
  • getters: state的计算属性;
  • mutations: 同步;
  • actions: 异步。

Getters中的参数:

  • state: 如果在模块中定义,则为局部的state;
  • getters: 如果在模块中定义,则为局部的getters;
  • rootState: 相当于全局的state,即store.state;
  • rootGetters: 相当于全局的getters,即store.getters。

八. 项目结构

Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:

  1. 应用层级的状态应该集中到单个 store 对象中。
  2. 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
  3. 异步逻辑都应该封装到 action 里面。

只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。

对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:

├── index.html
├── main.js
├── api
│   └── ... # 抽取出API请求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 我们组装模块并导出 store 的地方
    ├── actions.js        # 根级别的 action
    ├── mutations.js      # 根级别的 mutation
    └── modules
        ├── cart.js       # 购物车模块
        └── products.js   # 产品模块

官网链接: Vuex

转载请注明出处或者链接地址:https://www.qianduange.cn//article/13001.html
标签
评论
发布的文章

JQuery中的load()、$

2024-05-10 08:05:15

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!