第一次尝试写本文,还是去年年底(2019-11-26),后来入职新公司忙的要死,也就鸽了。今天重新梳理,争取弄得清楚一些:带你读 Vuex
源码。2020-05-05 11:54:35
基础使用
Vuex是什么
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。 它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
- 集中式存储
- 状态以可预测的方式变化,改值得走流程使用提前写好的方法。方便追溯相关方法的调用来观察状态的流转过程。
用法
用法:
- state
- mutations
- actions
- getters
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
counter: 0
},
mutations: {
add(state) {
state.counter++
}
},
actions: {
add({commit}) {
setTimeout(() => {
commit('add')
}, 1000);
}
},
getters: {
doubleCount(state){
return state.count*2
}
}
})
new Vue({
store
})
在页面中:
this.$store.commit('add'); // 同步
this.$store.dispatch('add'); // 异步
this.$store.state.counter; // state获取
this.$store.getters.doubleCounter // getters
其他用法,写了很多都删了,具体看官方文档就好了。
如何阅读这张图片?
- 我们再vue组件中调用 dispatch 使用Action来发起异步请求,得到的结果
- Mutation 只接受同步函数,修改 State
- 状态State的修改会引起组件的渲染render
合理使用vuex
源码基础实现
既然是Vue生态里的插件,使用Vue.use(Vuex)
,new Vuex.Store({})
来初始化插件,源码结构就需要实现 install
方法,也需要导出 Store
。当然,因为vuex依赖vue,所以也是使用了 形参。
使用 Vue.mixin 方法,在 beforeCreate 时期混入vue
// step1
let Vue
class Store {
constructor(options) {}
}
// vue2里是这样使用的new Vuex.Store({})
function install(_Vue) {
Vue = _Vue
Vue.mixin({
// 筛选 判断 添加this.$store
beforeCreate() {
if (this.$options.store) {
Vue.prototype.$store = this.$options.store
}
},
})
}
// 导出 install 和 Store
export default {
install,
Store,
}
用户会传入 state
, mutations
, actions
, gettter
等配置对象。
state
初始化时候会对
store._vm=new Vue({
data:{
$$state: state
},
compouted
})
所以访问 $store.state
实际上是访问的 this._vm._data.$$state
先来实现 state
,用户可以访问 state,但不能修改。
这里解决响应式,没有使用router中使用的util方法,而是直接使用了 new Vue
依赖promise
// step2
class Store{
constructor(options){
// 保证是响应式的,上次router使用了`Vue.util.defineReactive`
// 这里直接使用了 Vue 构造函数来保证数据是响应式的
// this._vm = new Vue({$$state=options.state})
this.config = new Vue({
data: {
// state = options.state
// 这里用了$$state 一个小技巧,不会挂载,只读,黑科技
$$state = option.state
}
})
}
get state(){
// return this._vm._data.$$state
return this.config._data.$$state
}
set state(val){
console.error('不能直接修改')
}
}
mutation
用户调用 this.$store.commit('add')
class Store{
constructor(options){
// ... 略过
// 找到方法集合
this._mutations = options.mutations
this.commit = this.commit.bind(this) // 时刻绑定this
}
commit(type,payload){
// 要找到对应的方法
const target = this._mutations[type]
// 找不到怎么办
if(!target){
console.log('没找到')
return;
}
target(this.state,payload)
// mutations: {
// add(state) {
// state.counter++
// }
// }
},
}
}
action
处理异步,和commit
区别不大。
class Store{
constructor(options){
this._actions = options.actions
this.dispatch = this.dispatch.bind(this)
}
dispatch(type, payload) {
const entry = this._actions[type]
if (!entry) {
console.error("未知action类型")
return
}
// 把整个 this 放进去
entry(this, payload)
// actions: {
// add({commit}) {
// setTimeout(() => {
// commit('add')
// }, 1000);
// }
// }
},
}
}
getter
getter是一个快捷选项,类似 computed
官方在 resetStoreVM
做了说明
class Store{
constructor(options){
this._xGetter = options.getters
this.xGetter = this.xGetter.bind(this)
}
xGetter(type){
this.getters = {} // 定义对象
const target = this._xGetter[type]
}
}
class Store {
constructor(opitons){
this.xGetters(options.getters)
}
xGetters(types){
this.getters={} // 定义了对象,然后遍历
Object.keys(types).forEach(key=>{
// 利用 define 进行映射
Object.defineProperty(this.getters,key,{
get:()=>{
return types[key](this.state)
}
})
})
}
}
wrapgetters
插件Plugin
下面这个函数会在每次Mutation 之后调用,这样也就实现了记录的作用。
function actionLogPlugin(store){
store.subscribe((mutation,state)=>{})
}
如何实现 plugin?就比如大名鼎鼎的 vuex-persistedstate,原理就很简单,
每次 mutation变化都会把 state 存入 localStorage.setItem 里
const myPlugin = store =>{
store.subscribe((mutation,state)=>{})
}
- 本文部分参考 vue技术揭秘