import { forEachValue } from "../src/vuex/util"
源码的第一步
vuex 的安装 重点的逻辑点是什么 重点的代码是什么
// 重点的逻辑是:
// 1. vuex 任何插件都有install的方法, Vue.use() 就会默认调用这个安装方法
// 2. 从 使用上看 还有有 Store这个 因为 需要 new Vue ({
// store
// }) 这样保证每一个组件的注入store这个属性 ,所以还需要导出这个类
// 重点的代码是:
let Vue // 搞一个全局的Vue
const install = (_Vue) => {
Vue= _Vue // _Vue是 Vue的构造函数
// 需要根据组件中的store注入每一个组件(子组件) Vue.mixin
applyMixin(Vue)
}
function applyMixin (Vue) {
Vue.mixin({
beforeCreate: vuexInit
})
}
function vuexInit () {
// 给所有组件增加 $store属性 指向我们创建store 的实例
const option = this.$options
if (options.store) { // 说明这个是根实例
this.store
} else if (options.parent && options.parent.$store) {
this.store
}
}
源码的第二步
vue 中 的状态实现 状态state
1。 首先这个 options 明白是什么
export class Store { // 容器的初始化
constructor (options) { // options就是你 new Vuex.$Store({state, mutations, actions 这些})
const state = options.state // 数据变化更新视图, Vue的核心逻辑是依赖收集
// 添加状态逻辑
// 这里的依据 是 Vue 中的 data 都是相应式的 所以把这个$$state 放在里边,也会是响应式的 但是 Vue 属性如果是$开头, 默认不会将这个属性挂载到 vm上
this._vm = new Vue({
data: { // Vue 属性如果是$开头, 默认不会将这个属性挂载到 vm上
state 对应的对象 都通过 defineProperty来进行数据劫持
},
})
}
get state () {
return this._vm_dat.$$state
}
}
2. 其次要明白 state 怎么变成响应式的数据
源码的第三步 简单版的实现 主要是 getters mutations 这个
gettes实现
export class Store { // 容器的初始化
constructor (options) { // options就是你 new Vuex.$Store({state, mutations, actions 这些})
const state = options.state // 数据变化更新视图, Vue的核心逻辑是依赖收集
// 添加状态逻辑
// 这里的依据 是 Vue 中的 data 都是相应式的 所以把这个$$state 放在里边,也会是响应式的 但是 Vue 属性如果是$开头, 默认不会将这个属性挂载到 vm上
this._vm = new Vue({
data: { // Vue 属性如果是$开头, 默认不会将这个属性挂载到 vm上
state 对应的对象 都通过 defineProperty来进行数据劫持
},
})
this.getters = {}
const computed = {}
// 通过遍历options即是传入的 getters 循环 拿到 里边的方法变成 this.getters的属性 然后 这个属性 通过 defineProperty 变为 一个响应式 并且把state传入
Object.keys(options.getters).forEach(key => {
Object.defineProperty(this.getters, key, {
get: () => options.getters[key](this.state)
})
})
// 为了更优化代码 抽离出这个工具函数
const forEachValue = (obj, callback) => {
Object.keys(obj).forEach(key => callback(obj[key], key))
}
// 所以 变为
forEachValue(options.getters,(fn,key) => {
Object.defineProperty(this.getters, key, {
get: () => fn(this.state)
})
})
}
get state () {
return this._vm_dat.$$state
}
}
// 目前的问题就是 getters 这些调用一次就会执行一次 没有缓存,这里用到 computed
export class Store { // 容器的初始化
constructor (options) { // options就是你 new Vuex.$Store({state, mutations, actions 这些})
const state = options.state // 数据变化更新视图, Vue的核心逻辑是依赖收集
// 添加状态逻辑
// 这里的依据 是 Vue 中的 data 都是相应式的 所以把这个$$state 放在里边,也会是响应式的 但是 Vue 属性如果是$开头, 默认不会将这个属性挂载到 vm上
const computed = {}
this.getters = {}
// 所以 变为
forEachValue(options.getters,(fn,key) => {
computed[key] = () => { // 将用户的getters 放在 Vue的实例上
return fn(this.state)
}
Object.defineProperty(this.getters, key, {
get: () => this._vm[key]// 当我取值时候用的 是计算属性的逻辑
})
})
// 这里 特别注意顺序问题, 要先放在计算属性中,在创造实例
this._vm = new Vue({
data: { // Vue 属性如果是$开头, 默认不会将这个属性挂载到 vm上
state 对应的对象 都通过 defineProperty来进行数据劫持
},
computed
})
}
get state () {
return this._vm_dat.$$state
}
}
mutations actions 实现
export class Store { // 容器的初始化
constructor (options) { // options就是你 new Vuex.$Store({state, mutations, actions 这些})
const state = options.state // 数据变化更新视图, Vue的核心逻辑是依赖收集
// 添加状态逻辑
// 这里的依据 是 Vue 中的 data 都是相应式的 所以把这个$$state 放在里边,也会是响应式的 但是 Vue 属性如果是$开头, 默认不会将这个属性挂载到 vm上
const computed = {}
this.getters = {}
// 所以 变为
forEachValue(options.getters,(fn,key) => {
computed[key] = () => { // 将用户的getters 放在 Vue的实例上
return fn(this.state)
}
Object.defineProperty(this.getters, key, {
get: () => this._vm[key]// 当我取值时候用的 是计算属性的逻辑
})
})
// 这里 特别注意顺序问题, 要先放在计算属性中,在创造实例
this._vm = new Vue({
data: { // Vue 属性如果是$开头, 默认不会将这个属性挂载到 vm上
state 对应的对象 都通过 defineProperty来进行数据劫持
},
computed
})
this.mutations = {}
this.actions = {}
forEachValue (options.mutations, (fn, key) => {
this.mutations[key] = (payload) => fn(this.state, payload)
})
// 在严格模式下 mutations 和 actions是有区别的
forEachValue (options.actions, (fn, key) => {
this.actions[key] = (payload) => fn(this, payload) // this 其实就是这个store实例 是因为这个可以解构出 {commit}
})
}
commit = (type, payload) => { //箭头函数保证this 指向当前store的实例 type对应的就是 mutations 里边的 函数
// 调用commit 就是 去找刚刚绑定好的 mutation
this.mutations[type](payload)
}
dispatch = (type, payload) => {
this.actions[type](payload)
}
get state () {
return this._vm_dat.$$state
}
}
源码的第四步 模块收集
重点的逻辑点是 要把数据格式化成 一个 树形的数据 然后对这个树形的数据进行解析
树形结构 是这样的
// this.root = {
// _raw: '根模块',
// _children: {
// a: {
// _raw: 'a模块',
// _children: {
// c ...
// },
// state: 'a的状态'
// },
// a: {
// _raw: 'a模块',
// _children: {
// c ...
// },
// state: 'a的状态'
// }
// },
// state: '根模块自己的状态'
// }
class ModuleCollectioin {
constructor (options) {
this.register([], options) // 这里的数组是栈
}
register(path, rootModule) {
// let newModule = {
// _raw: rootModule,
// state: rootModule.state,
// _children: {}
// }
let newModule = new Module(rootModule)
if (path.length === 0) { // s说明是根
this.root = newModule
} else { // rootModule.modules 这个条件说明 有儿子
// 这里就需要寻找 需要拼的路径 [root, a, c]
let parent = path.slice(0, -1).reduce((memo,current) => {
// return memo._children[current]
return memo.getChild(current)
}, this.root)
// parent._children[path[path.length-1]] = newModule
parent.addChild(path[path.length -1], newModule)
}
if (rootModule.modules) {
// 这里就需要递归寻找
forEachValue(rootModule.modules, (module, moudleName => {
this.register(path.concat(moudleName), module)
}))
}
}
}
源码第五步 需要模块的数据出来了 需要对模块进行 遍历安装
这里就是遍历 我们得倒的模块树的 actions muatioins getters 定义在我们 最外层store上
然后将所有子模块的状态安装到父级模块上
const installModule = (store, rootState, path, module) => {
if (path.length > 0) {
let parent = path.slice(0, -1).reduce((memo, current) => {
return memo[current]
}, rootState)
Vue.set(parent, path[path.length - 1], module.state )
}
module.forEachMutations ((mutation, key ) => {
store._mutations[key] = store._mutations[key] || []
store.mutations[key].push((payload) => {
mutation.call(store, module.state, payload)
})
})
module.forEachActions ((actions, key) => {
store._action[key] = store._action[key] || []
store._actions[key].push((payload) => {
actions.call(store, store, payload)
})
})
module.forEachGetter((getter, key) => {
// 模块中 getter 的名字重复会被覆盖
store._wrappedGetter[key] = function () {
return getter(module.state)
}
})
moudle.forEachChild((child, key) => {
// 递归加载模块
installModule(store, rootState, path.concat(key), child)
})
}
// 将状态 和 getters 都定义在当前 vm上 监听???
resetStoreVM(this, state)
function resetStoreVM(store, state) {
const computed = {}
store.getters = {}
forEachValue(store._wrappedGetters, (fn, key) => {
computed[key] = () => {
return fn()
}
Object.defineProperty(store.getters, key, {
get : () => store._vm[key]
})
})
store._vm = new Vue({
data: {