前往 官方文档 查看详情。
为了与 Vue 3 初始化过程保持一致,Vuex 的安装方式已经改变了。用户现在应该使用新引入的 createStore
方法来创建 store 实例。
import { createStore } from 'vuex'
export const store = createStore({
state () {
return {
count: 1
}
}
})
要将 Vuex 安装到 Vue 实例中,需要用 store
替代之前的 Vuex 传递给 use
方法。
import { createApp } from 'vue'
import { store } from './store'
import App from './App.vue'
const app = createApp(App)
app.use(store)
app.mount('#app')
全新的“useStore”组合式函数
Vuex 4 引入了一个新的 API 用于在组合式 API 中与 store 进行交互。可以在组件的
setup
钩子函数中使用useStore
组合式函数来检索 store。
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
}
}
组合式API(重点)
可以通过调用
useStore
函数,来在setup
钩子函数中访问 store。这与在组件中使用选项式 API 访问this.$store
是等效的。
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
}
}
访问 State 和 Getter
为了访问 state 和 getter,需要创建
computed
引用以保留响应性,这与在选项式 API 中创建计算属性等效。
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
return {
// 在 computed 函数中访问 state
count: computed(() => store.state.count),
// 在 computed 函数中访问 getter
double: computed(() => store.getters.double)
}
}
}
访问 Mutation 和 Action
要使用 mutation 和 action 时,只需要在
setup
钩子函数中调用commit
和dispatch
函数。
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
return {
// 使用 mutation
increment: () => store.commit('increment'),
// 使用 action
asyncIncrement: () => store.dispatch('asyncIncrement')
}
}
}
项目结构
Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:
- 应用层级的状态应该集中到单个 store 对象中。
- 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
- 异步逻辑都应该封装到 action 里面。
只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation
└── modules
├── cart.js # 购物车模块
└── products.js # 产品模块
严格模式
在创建 store 的时候传入
strict: true
。
在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。
const store = createStore({
strict: true
})
开发环境与发布环境
不要在发布环境下启用严格模式!严格模式会深度监测状态树来检测不合规的状态变更——请确保在发布环境下关闭严格模式,以避免性能损失。
- 类似于插件,我们可以让构建工具来处理这种情况。
const store = createStore({
strict: process.env.NODE_ENV !== 'production'
})
表单处理
在严格模式中使用 Vuex 时,在属于 Vuex 的 state 上使用
v-model
会比较棘手,由于这个修改不是在 mutation 函数中执行的, 这里会抛出一个错误。
<input v-model="obj.message">
- 用“Vuex 的思维”去解决这个问题的方法是:给
<input>
中绑定 value,然后侦听input
或者change
事件,在事件回调中调用一个方法:
<template>
<input :value="message" @input="updateMessage">
</template>
<script>
export default {
setup() {
computed: {
...mapState({
message: state => state.obj.message
})
},
methods: {
updateMessage (e) {
this.$store.commit('updateMessage', e.target.value)
}
}
}
}
</script>
- mutation 函数
mutations: {
updateMessage (state, message) {
state.obj.message = message
}
}
- 双向绑定的计算属性
<template>
<input v-model="message">
</template>
<script>
export default {
setup() {
computed: {
message: {
get () {
return this.$store.state.obj.message
},
set (value) {
this.$store.commit('updateMessage', value)
}
}
}
}
}
</script>
TypeScript 支持
Vuex 提供了类型声明,因此可以使用 TypeScript 定义 store,并且不需要任何特殊的 TypeScript 配置。请遵循 Vue 的基本 TypeScript 配置 来配置项目。
但是,如果你使用 TypeScript 来编写 Vue 组件,则需要遵循一些步骤才能正确地为 store 提供类型声明。
Vue 组件中 $store
属性的类型声明
Vuex 没有为
this.$store
属性提供开箱即用的类型声明。如果你要使用 TypeScript,首先需要声明自定义的 模块补充(module augmentation)。
- 项目文件夹中添加一个声明文件(vuex.d.ts)来声明 Vue 的自定义类型
ComponentCustomProperties
。
import { ComponentCustomProperties } from 'vue'
import { Store } from 'vuex'
declare module '@vue/runtime-core' {
// 声明自己的 store state
interface State {
count: number
}
// 为 `this.$store` 提供类型声明
interface ComponentCustomProperties {
$store: Store<State>
}
}
useStore
组合式函数类型声明
Vuex 将store 安装到 Vue 应用中使用了 Vue 的 Provide/Inject 特性,这就是 injection key 是很重要的因素的原因。
- 定义类型化的
InjectionKey
。
引入
InjectionKey
并将其传入useStore
使用过的任何地方,很快就会成为一项重复性的工作。
- 将类型化的
InjectionKey
传给useStore
方法。
通过引入自定义的组合式函数,不用提供 injection key 和类型声明就可以直接得到类型化的 store。