持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第18天,点击查看活动详情
Vuex 作为 Vue 生态中用于状态管理的一种模式,被广泛应用于 Vue 单页应用开发中。
Vuex 的几个核心概念:
Store: Vuex 使用 Store 对象管理应用的状态,一个 Store 包括 State, Getter, Mutation, Action, modules五个属性。
使用 vue-cli 创建项目时,配置项中选择 Vuex,脚手架就会自动生成并配置。
import { createStore } from 'vuex'
export default createStore({
state: {
},
mutations: {
},
actions: {
},
getters: {
},
modules: {
}
})
State:“状态”,是 Vuex 状态管理的数据源。
Getter:作用与 filters 有些相似,可以将 State 进行过滤后输出。
Mutation:是 Vuex 中改变 State 的唯一途径(严格模式下),只能是同步操作。Mutaion 使得状态变得可追踪,配合vue devtools 可以实现 time-travel 的调试体验。
Action:对 State 的异步操作可以放在 Action 中,并通过在 Action 提交 Mutaion 变更状态。
Module:当 Store 对象过于庞大时,可根据具体的业务需求分为多个 Module。
我们可以在组件中触发 Action,Action 则会提交 Mutation,Mutaion 会对 State 进行修改,组件再根据 State 、Getter 渲染页面。
什么样的应用场景下需要 Vuex
vuex 一般用于中大型 web 单页应用中对应用的状态进行管理。如果不打算开发大型单页应用,那最好不要使用 Vuex,一般通过一个空的 Vue 实例作为中转站,也可以称之为事件中心、event bus。就足够所需了。
Vuex 解决跨组件通信问题,对于多层级组件嵌套等较为复杂的场景。Vuex 是通过将 state 作为数据中心、各个组件共享 state 实现跨组件通信的,此时的数据完全独立于组件,因此将组件间共享的数据置于 State 中能有效解决多层级组件嵌套的跨组件通信问题。
可以把组件之间全局共享的数据,通过后端异步请求的数据会放在 State 中。
import { createStore } from 'vuex';
export default createStore({
state: {
userInfo: {}
},
mutations: {
UPDATE_USER_INFO(state, payload) {
state.userInfo = payload
}
},
actions: {
async getInfo(payload) {
const result = await getMpUserDetail();
payload.commit('UPDATE_USER_INFO', result)
}
},
getters: {
getUserName(state) {
return state.userInfo.filter((s) => s.age > 20)
}
},
modules: {
}
});
如何实现一个简单的Vuex
import { createApp } from 'vue'
import App from './App.vue'
import { createStore } from 'vuex'
const store = createStore({
state: {
},
mutations: {
},
actions: {
},
modules: {
}
})
createApp(App).use(store).mount('#app')
将 Vuex 以 Vue 插件的形式引入到项目中,创建一个 Store 实例,将 Store 实例添加到 Vue 根组件实例的选项对象中。
执行 Vue.use(Vuex) 时,通过 Vue.mixin (全局混入),将 store 对象挂载到每个组件实例的$store属性上。这样就可以在每个组件实例中以this.$store的形式访问到 store 对象。
let Vue
class Store {}
// 在 install 方法中实现混入逻辑。
// 当调用 Vue.use 方法时 Vue 会将 Vue 的构造函数作为参数传入插件的 install 方法中
// 这样可以很方便使用全局混入方法
function install(_Vue) {
Vue = _Vue
Vue.mixin({
beforeCreate() {
let options = this.$options
// 当当前组件实例为根组件实例时进入if代码块中
if (options.store) {
this.$store = options.store
// 当组件实例不是根组件实例时
// 就从该组件实例的父组件实例中取出store对象并赋值给this.$store
} else if (options.parent && options.parent.store) {
this.$store = options.parent.store
}
}
})
}
export default {
Store,
install
}
将 store.state 中的数据变为响应式数据。当我们读取 store.state 中的数据时,就会收集到依赖(渲染函数),变更 store.state 中的数据时就会触发渲染函数,导致组件重新渲染。
创建一个 Vue 的实例并把 store.state 挂载到 Vue 选项对象的 data 属性中,这样当创建实例时 store.state 就会被转变为响应式数据。
class Store {
constructor(options) {
this._vm = new Vue({
data: {
$$state: options.state
}
})
}
get state() {
return this._vm.$date.$$state
}
}
getter 属性相当于 vue 中的计算属性,只有当它所依赖的 state 中的数据发生变化时才会被重新求值。通过 Vue 的计算属性来实现 Vuex 中的 getters。
将 Store 构造函数中传入的 getters 属性中的函数进行包装,将包装后的 getters 对象赋值给 Vue 选项对象的 compted 属性。
class Store {
constructor(options) {
this._vm = new Vue({
computed: this.initGetters(options.getters)
})
}
get getters() {
return this._vm
}
}
Store.prototype.initGetters = function(getters) {
let computed = {}
Object.keys(getters).forEach(key => {
computed[key] = () => getters[key](this.state)
})
return computed
}
当调用 commit 函数时,根据 commit 函数的第一个参数来判断是调用 mutatuions 中的哪个函数然后传参入参数并调用即可。
class Store {
constructor(options) {
//....
this.mutations = options.mutations
}
commit = (type, payload) => {
this._mutations[type](this.state, payload)
}
}
Vuex 中 action 的实现本质上就是对 dispatch 方法的实现,dispatch 与 commit 原理差不多,最本质的区别是 dispatch 在调用对应的 action 函数时会将 commit 方法作为参数传入到 action 函数中去,这样便可以让 action 方法做完异步操作后再调用 commit 方法实现对 store.state 中数据的修改。
class Store {
constructor(options) {
// ....
this._actions = options.actions
}
// ....
dispatch = (type, payload) => {
let context = {
commit: this.commit,
state: this.state
}
this._actions[type](context, payload)
}
}
Vuex 源码