参考文档:
Vuex是什么
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
这个状态自管理应用包含以下几个部分:
- 状态,驱动应用的数据源;
- 视图,以声明方式将状态映射到视图;
- 操作,响应在视图上的用户输入导致的状态变化。
Vuex的工作原理如下图所示:
Vuex4.x的简单用法
创建一个 store 实例
import { createApp } from 'vue'
import { createStore } from 'vuex'
// 创建一个新的 store 实例
const store = createStore({
state () {
return {
count: 0
}
},
getters: {
double(state){
return state.count * 2
}
},
mutations: {
increment (state, payload) {
state.count++
}
},
actions: {
AsyncIncrement(state, payload){
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation', payload)
resolve()
}, 1000)
})
}
}
})
const app = createApp({ /* 根组件 */ })
// 将 store 实例作为插件安装
app.use(store)
通过组合式API的方法使用
访问 State 和 Getter
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
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
return {
// 使用 mutation
increment: () => store.commit('increment'),
// 使用 action
asyncIncrement: () => store.dispatch('asyncIncrement')
}
}
}
从用法上对比Vuex3.x
- 创建
store
的方法不同,之前是通过new Store()
来创建一个store
实例。现在是通过createStore
方法来创建。 - Vuex4.x兼容之前的使用方法。但是在Vue3.x中如果我们想要通过组合式API的方法使用Vuex,我们需要借助与
useStore
这个函数。
实现一个简单的 Vuex4.x;
实现一个 createStore
方法
store实际上还是基于一个Class
来实现的,只是没有直接将 Store
这个类暴露出来,而是包装成了一个函数,直接返回实例。
import { reactive } from 'vue'
export function createStore(options){
return new Store(options)
}
class Store{
constructor(options){
const store = this;
// 使用reactive使state是响应式的
store._state = reactive({data: options.state})
}
get state(){
return this._state.data
}
}
通过 Object.defineProperty
来重新定义getters
将用户传入的getters
全部定义到store实例的getters属性上,在获取值的时候调用
function forEachValue(obj, fn){
Object.keys(obj).forEach(key => fn(obj[key], key))
}
class Store{
constructor(options){
const store = this;
// 使用reactive使state是响应式的
store._state = reactive({data: options.state})
// 保存getters
const _getters = options.getters;
store.getters = {};
forEachValue(_getters, function(fn, key){
Object.defineProperty(store.getters, key, {
get: ()=> fn(store.state),
enumerable: true
})
})
}
}
基于发布订阅模式实现 mutations
和 actions
class Store{
constructor(options){
// ...
const store = this;
store._mutations = Object.create(null)
const _mutations = options.mutations;
forEachValue(_mutations, (mutation, key)=>{
store._mutations[key] = (payload) => {
mutation.call(store, store.state, payload)
}
})
}
commit = (type, payload) => {
this._mutations[type](payload)
}
}
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作
由于action可以执行异步函数,所以action在执行完成后应该返回一个Promise实例,对dispact的执行结果进行判断,如果不是一个Promise,需要给处理成一个promise。
class Store{
constructor(options){
// ...
const store = this;
store._actions = Object.create(null)
const _actions = options.actions;
forEachValue(_actions, (action, key)=>{
store._actions[key] = (payload) => {
const res = action.call(store, store.state, payload)
if(!isPromise(res)){
return Promise.resolve(res)
}
return res
}
})
}
dispatch = (type, payload) => {
this._actions[type](payload)
}
}
useStore
通过inject
来获取注册的store实例
import {inject} from 'vue'
export function useStore(injectKey = null) {
return inject('store')
}
install
在Vue项目中使用Vuex,我们需要通过use()
来注册store
const app = createApp({ /* 根组件 */ })
// 将 store 实例作为插件安装
app.use(store)
class Store{
install(app, injectKey){
// Vue3.x createApp().use(store)
// 全局暴露一个 变量,暴露的是store的实例
app.provide('store')
// Vue2.x Vue.prototype.$store = this
// 增添$store属性 使得可以直接在模板中使用 $store.state.count
app.config.globalProperties.$store = this;
}
}