什么是vuex
先来看官方介绍:
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能
vuex是mcv模式中的model层,数据通过 state->components->acticon->mutation,然后改变状态,在结合双向绑定实现页面更新
vue和vuex
先看代码
new Vuex.Store({
state:{},
mutations:{},
actions:{},
getters:{}
})
new Vue({
data:{},
methods:{},
computed:{}
})
可以看出vue和vuex差不多,
- state->data
- 获取数据 getters->computed
- 更改数据 mutations-> methods
通过修改mutations方法修改state数据。
说到mutations和actions不得不说commit和dispatch,通过点击事件触发methods的方法,当存在异步时,vuex通过dispatch方法触发actions中的commit来使用mutations中的方法
vuex源码分析
vuex借鉴了flux和redux等,把数据存放在全局的store中,再将store挂载到vue的组件实例中
vuex的store挂载
- 在vue中安装vuex
import Vuex from 'vuex';
Vue.use(vuex);// vue的插件机制
- 在vue.use(vuex)时,调用vuex的install方法
export function install (_Vue) {
if (Vue && _Vue === Vue) {
if (process.env.NODE_ENV !== 'production') {
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
)
}
return
}
Vue = _Vue
applyMixin(Vue)
}
- applyMixin方法,在vue生命周期beforeCreate钩子函数前混入vuexInit方法
Vue.mixin({ beforeCreate: vuexInit });
function vuexInit () {
const options = this.$options
// store injection
if (options.store) {
this.$store = typeof options.store === 'function'
? options.store()
: options.store
} else if (options.parent && options.parent.$store) {
this.$store = options.parent.$store
}
}
这步使用vue的mixin混入,实现store注入vue组件实例,注册vuex的store属性($store)
vuex中state和getters如何映射组件中更新
- vuex源码中resetStoreVM方法
function resetStoreVM (store, state, hot) {
const oldVm = store._vm
// 设置 getters 属性
store.getters = {}
const wrappedGetters = store._wrappedGetters
const computed = {}
// 遍历 wrappedGetters 属性
forEachValue(wrappedGetters, (fn, key) => {
// 给 computed 对象添加属性
computed[key] = partial(fn, store)
// 重写 get 方法
// store.getters.xx 其实是访问了store._vm[xx],其中添加 computed 属性
Object.defineProperty(store.getters, key, {
get: () => store._vm[key],
enumerable: true // for local getters
})
})
const silent = Vue.config.silent
Vue.config.silent = true
// 创建Vue实例来保存state,同时让state变成响应式
// store._vm._data.$$state = store.state
store._vm = new Vue({
data: {
$$state: state
},
computed
})
Vue.config.silent = silent
// 只能通过commit方式更改状态
if (store.strict) {
enableStrictMode(store)
}
}
可以看出vuex中state借用data,将state存入vue实例中的data,而vuex中的getters借用vue的computed实现数据监听
vuex中mutations如何更新
registerMutation方法中,获取store中mutation type函数集合,再将新的函数push进去,再通过原函数传入state。
// 注册对应模块的mutation,供state修改使用
module.forEachMutation((mutation, key) => {
const namespacedType = namespace + key
registerMutation(store, namespacedType, mutation, local)
})
// 注册对应模块的action,供数据操作、提交mutation等异步操作使用
module.forEachAction((action, key) => {
const namespacedType = namespace + key
registerAction(store, namespacedType, action, local)
})
// 注册对应模块的getters,供state读取使用
module.forEachGetter((getter, key) => {
const namespacedType = namespace + key
registerGetter(store, namespacedType, getter, local)
})
function registerMutation (store, type, handler, local) {
// 取出对应type的mutations-handler集合
const entry = store._mutations[type] || (store._mutations[type] = [])
// commit实际调用的不是我们传入的handler,而是经过封装的
entry.push(function wrappedMutationHandler (payload) {
// 调用handler并将state传入
handler(local.state, payload)
})
}
为什么state只能在mutations中修改
如果不开启严格模式,state可以直接修改
如果使用actions中修改,vuex会给与警报,因为源码中action只能有一个声明,在多个action 中修改,可能会产生异步错误。而直接修改也属于dispatch异步修改,所以官方会给与警告