1. Vuexoverview
Vuex is Vue.js 官方statusmanagementlibrary, 它providing了一个集in式storemanagementapplication 所 has component status, 并以相应 规则保证status以一种可预测 方式发生变化.
1.1 Vuex core concepts
- State: storeapplicationstatus object
- Getter: from Stateinfork出 new status
- Mutation: modifyState 唯一方式, 必须 is synchronizationfunction
- Action: processingasynchronousoperation, 可以submittingMutation
- Module: 将Store分割成module, 便于management
1.2 Vuex 适用场景
Vuex适合用于in big 型application, 特别 is 当您遇 to 以 under circumstances时:
- many 个component共享同一status
- component间通信 complex , 涉及 many 层级 or 跨component通信
- 需要记录status变化history, supportrevert/重做functions
- 需要implementationstatus 持久化store
1.3 installationVuex
可以throughnpm or yarninstallationVuex:
# usingnpminstallation
npm install vuex@4
# usingyarninstallation
yarn add vuex@4
提示
Vuex 4 is for Vue 3 design , such as果你using is Vue 2, 应该installation Vuex 3: npm install vuex@3
2. basicStoreconfiguration
让我们开始creation一个basic Vuex Store.
2.1 creationStoreinstance
// src/store/index.js
import { createStore } from 'vuex'
// creationStoreinstance
const store = createStore({
// status
state() {
return {
count: 0,
todos: [
{ id: 1, text: 'LearningVuex', done: false },
{ id: 2, text: '构建application', done: false }
]
}
},
// Getter
getters: {
doneTodos: (state) => {
return state.todos.filter(todo => todo.done)
},
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
},
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
},
// Mutation
mutations: {
increment(state) {
state.count++
},
decrement(state) {
state.count--
},
addTodo(state, todo) {
state.todos.push(todo)
},
toggleTodo(state, todoId) {
const todo = state.todos.find(todo => todo.id === todoId)
if (todo) {
todo.done = !todo.done
}
}
},
// Action
actions: {
incrementAsync({ submitting }) {
setTimeout(() => {
submitting('increment')
}, 1000)
},
addTodoAsync({ submitting }, todo) {
return new Promise((resolve) => {
setTimeout(() => {
submitting('addTodo', todo)
resolve()
}, 1000)
})
}
}
})
export default store
2.2 挂载Storeinstance
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
const app = createApp(App)
// 挂载Storeinstance
app.use(store)
app.mount('#app')
3. State
State is storeapplicationstatus object, 它 is Vuex Store core.
3.1 in componentin访问State
可以throughthis.$store.state in componentin访问State:
<template>
<div>
<p>当 before 计数: {{ $store.state.count }}</p>
<ul>
<li v-for="todo in $store.state.todos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
</div>
</template>
3.2 usingmapState辅助function
可以usingmapState辅助function将Statemap to component 计算propertyin:
<template>
<div>
<p>当 before 计数: {{ count }}</p>
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'CounterView',
computed: {
// objectunfold运算符将mapState返回 object混入computedobjectin
...mapState(['count', 'todos'])
// or 者usingobject形式, 自定义计算property名称
/* ...mapState({
counter: 'count',
todoList: 'todos'
}) */
}
}
</script>
4. Getter
Getter用于 from Stateinfork出 new status, class似于component 计算property.
4.1 in componentin访问Getter
可以throughthis.$store.getters in componentin访问Getter:
<template>
<div>
<p>已completion 待办事项: {{ $store.getters.doneTodosCount }}</p>
<ul>
<li v-for="todo in $store.getters.doneTodos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
<p>ID for 1 待办事项: {{ $store.getters.getTodoById(1).text }}</p>
</div>
</template>
4.2 usingmapGetters辅助function
可以usingmapGetters辅助function将Gettermap to component 计算propertyin:
<template>
<div>
<p>已completion 待办事项: {{ doneTodosCount }}</p>
<ul>
<li v-for="todo in doneTodos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'TodoView',
computed: {
// objectunfold运算符将mapGetters返回 object混入computedobjectin
...mapGetters(['doneTodos', 'doneTodosCount'])
// or 者usingobject形式, 自定义计算property名称
/* ...mapGetters({
completedTodos: 'doneTodos',
completedCount: 'doneTodosCount'
}) */
}
}
</script>
5. Mutation
Mutation is modifyState 唯一方式, 它必须 is synchronizationfunction.
5.1 submittingMutation
可以throughthis.$store.submitting()methodsubmittingMutation:
<template>
<div>
<p>当 before 计数: {{ $store.state.count }}</p>
<button @click="increment">增加</button>
<button @click="decrement">reducing</button>
<button @click="addTodo">添加待办事项</button>
</div>
</template>
<script>
export default {
name: 'CounterView',
methods: {
increment() {
// submittingMutation
this.$store.submitting('increment')
},
decrement() {
this.$store.submitting('decrement')
},
addTodo() {
// submittingMutation并传递parameter
this.$store.submitting('addTodo', {
id: 3,
text: ' new 待办事项',
done: false
})
}
}
}
</script>
5.2 submitting风格
除了直接传递parameter out , 还可以usingobject风格submittingMutation:
// 直接传递parameter
this.$store.submitting('addTodo', {
id: 3,
text: ' new 待办事项',
done: false
})
// object风格submitting
this.$store.submitting({
type: 'addTodo',
id: 3,
text: ' new 待办事项',
done: false
})
5.3 usingmapMutations辅助function
可以usingmapMutations辅助function将Mutationmap to component methodin:
<template>
<div>
<p>当 before 计数: {{ $store.state.count }}</p>
<button @click="increment">增加</button>
<button @click="decrement">reducing</button>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
name: 'CounterView',
methods: {
// objectunfold运算符将mapMutations返回 object混入methodsobjectin
...mapMutations(['increment', 'decrement'])
// or 者usingobject形式, 自定义method名称
/* ...mapMutations({
add: 'increment',
sub: 'decrement'
}) */
}
}
</script>
warning
Mutation必须 is synchronizationfunction, 不要 in Mutationin执行asynchronousoperation, asynchronousoperation应该放 in Actionin.
6. Action
Action用于processingasynchronousoperation, 可以submittingMutation.
6.1 分发Action
可以throughthis.$store.dispatch()method分发Action:
<template>
<div>
<p>当 before 计数: {{ $store.state.count }}</p>
<button @click="incrementAsync">asynchronous增加</button>
<button @click="addTodoAsync">asynchronous添加待办事项</button>
</div>
</template>
<script>
export default {
name: 'CounterView',
methods: {
incrementAsync() {
// 分发Action
this.$store.dispatch('incrementAsync')
},
async addTodoAsync() {
// 分发Action并processingPromise
await this.$store.dispatch('addTodoAsync', {
id: 3,
text: 'asynchronous添加 待办事项',
done: false
})
console.log('待办事项添加成功')
}
}
}
</script>
6.2 Action parameter
Action接收一个 and Storeinstance具 has 相同method and property on under 文object, 以及一个可选 payloadparameter:
actions: {
// on under 文objectpackage含submitting, state, gettersetc.property
incrementAsync(context) {
setTimeout(() => {
context.submitting('increment')
}, 1000)
},
// 解构 on under 文object
addTodoAsync({ submitting }, todo) {
return new Promise((resolve) => {
setTimeout(() => {
submitting('addTodo', todo)
resolve()
}, 1000)
})
}
}
6.3 usingmapActions辅助function
可以usingmapActions辅助function将Actionmap to component methodin:
<template>
<div>
<p>当 before 计数: {{ $store.state.count }}</p>
<button @click="incrementAsync">asynchronous增加</button>
</div>
</template>
<script>
import { mapActions } from 'vuex'
export default {
name: 'CounterView',
methods: {
// objectunfold运算符将mapActions返回 object混入methodsobjectin
...mapActions(['incrementAsync'])
// or 者usingobject形式, 自定义method名称
/* ...mapActions({
asyncAdd: 'incrementAsync'
}) */
}
}
</script>
7. Module
Module允许我们将Store分割成 many 个module, 每个module都 has 自己 State, Getter, Mutation and Action, 便于management big 型application status.
7.1 定义Module
// src/store/modules/counter.js
// 定义countermodule
const counterModule = {
namespaced: true, // 开启namespace
state() {
return {
count: 0
}
},
getters: {
doubleCount: (state) => {
return state.count * 2
}
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
incrementAsync({ submitting }) {
setTimeout(() => {
submitting('increment')
}, 1000)
}
}
}
export default counterModule
// src/store/modules/todo.js
// 定义todomodule
const todoModule = {
namespaced: true, // 开启namespace
state() {
return {
todos: [
{ id: 1, text: 'LearningVuex', done: false },
{ id: 2, text: '构建application', done: false }
]
}
},
getters: {
doneTodos: (state) => {
return state.todos.filter(todo => todo.done)
}
},
mutations: {
addTodo(state, todo) {
state.todos.push(todo)
}
},
actions: {
addTodoAsync({ submitting }, todo) {
setTimeout(() => {
submitting('addTodo', todo)
}, 1000)
}
}
}
export default todoModule
7.2 registerModule
// src/store/index.js
import { createStore } from 'vuex'
import counterModule from './modules/counter'
import todoModule from './modules/todo'
const store = createStore({
modules: {
counter: counterModule,
todo: todoModule
}
})
export default store
7.3 usingnamespaceModule
当开启namespace after , 访问module State, Getter, Mutation and Action需要加 on module名称:
// 访问module State
this.$store.state.counter.count
// 访问module Getter
this.$store.getters['counter/doubleCount']
// submittingmodule Mutation
this.$store.submitting('counter/increment')
// 分发module Action
this.$store.dispatch('counter/incrementAsync')
7.4 using辅助function访问namespaceModule
可以using辅助function访问namespaceModule, 需要指定module名称:
<template>
<div>
<p>当 before 计数: {{ count }}</p>
<p>双倍计数: {{ doubleCount }}</p>
<button @click="increment">增加</button>
<button @click="incrementAsync">asynchronous增加</button>
</div>
</template>
<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
export default {
name: 'CounterView',
computed: {
// usingmodule名称访问State
...mapState('counter', ['count']),
// usingmodule名称访问Getter
...mapGetters('counter', ['doubleCount'])
},
methods: {
// usingmodule名称访问Mutation
...mapMutations('counter', ['increment']),
// usingmodule名称访问Action
...mapActions('counter', ['incrementAsync'])
}
}
</script>
8. Vuex best practices
8.1 Statedesignbest practices
- 单一datasources: 所 has applicationstatus集in in 一个Storein
- State is 只读 : 唯一modifyState 方式 is submittingMutation
- using常量命名Mutation: 便于maintenance and debug
- module化design: 将 big 型application State分割成 many 个module
8.2 Mutationdesignbest practices
- Mutation必须 is synchronizationfunction: asynchronousoperation应该放 in Actionin
- Mutation应该 is atomicity : 每个Mutation只modify一个status
- using常量命名Mutation: 便于maintenance and debug
8.3 Actiondesignbest practices
- Action用于processingasynchronousoperation: such asAPIrequest, 定时器etc.
- Action可以返回Promise: 便于processingasynchronous流程
- Action可以组合: 一个Action可以分发另一个Action
8.4 Getterdesignbest practices
- Getter用于forkstatus: such asfilterlist, 计算propertyetc.
- Getter可以接收parameter: through返回functionimplementation
- Getter可以组合: 一个Getter可以using另一个Getter
8.5 module化designbest practices
- 开启namespace: 避免module间 命名conflict
- 按functions划分module: such asusermodule, 商品module, 购物车moduleetc.
- module in 部status独立: 每个module只management自己 status
- module间通信: through根State or Actionimplementation
9. Vuex and Composition API
in Vue 3in, 可以usingComposition API and Vuex结合using:
<template>
<div>
<p>当 before 计数: {{ count }}</p>
<p>双倍计数: {{ doubleCount }}</p>
<button @click="increment">增加</button>
<button @click="incrementAsync">asynchronous增加</button>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
// 获取Storeinstance
const store = useStore()
// 访问State
const count = computed(() => store.state.counter.count)
// 访问Getter
const doubleCount = computed(() => store.getters['counter/doubleCount'])
// submittingMutation
const increment = () => {
store.submitting('counter/increment')
}
// 分发Action
const incrementAsync = () => {
store.dispatch('counter/incrementAsync')
}
</script>
10. Vuex 替代solutions
除了Vuex out , 还 has 一些other statusmanagementsolutions可以选择:
10.1 Pinia
Pinia is Vue 3 官方statusmanagementlibrary, 它providing了更简洁 API, 更 good TypeScriptsupport, 以及更flexible architecture.
10.2 Composition API + provide/inject
for 于 small 型application, 可以usingComposition API结合provide/inject来managementstatus, 避免引入额 out 依赖.
10.3 event总线
for 于 simple component间通信, 可以usingevent总线, 但不适合management complex applicationstatus.
练习 1: basicVuex Store
- creation一个Vueproject, 并installationVuex.
- creation一个basic Store, package含countstatus, increment and decrement Mutation.
- creation一个component, usingStore status and Mutation.
- testcomponent is 否能正常modify and 显示status.
练习 2: asynchronousAction
- in Storein添加一个incrementAsync Action, latency1秒 after submittingincrement Mutation.
- in componentin添加一个按钮, 点击时分发incrementAsync Action.
- testasynchronousAction is 否能正常工作.
练习 3: module化Store
- creation两个module: countermodule and todomodule.
- countermodulemanagement计数status, package含increment and decrement Mutation.
- todomodulemanagement待办事项list, package含addTodo and toggleTodo Mutation.
- in componentinusing这两个module status and Mutation.
- testmodule化Store is 否能正常工作.