手写Vuex源码
Vuex原理解析
Vuex是基于Vue的响应式原理基础,所以无法拿出来单独使用,必须在Vue的基础之上使用。
1.Vuex使用相关解析
main.js
import store form './store' // 引入一个store文件
new Vue({
// 在Vue初始化的过程中,注入一个store属性,内部会将这个属性放到每个组件的$store上
store,
})
store.js
import Vuex from 'Vuex'
Vue.use(Vuex)
// 通过Vuex中的一个属性 Store 创建一个store的实例
export default new Vuex.Store({
state: { // 单一数据源
age: 10
},
mutations: { //
// payload 载荷
syncChange(state,payload) { // 修改状态的方法 同步更改
state.age+= payload
}
},
actions: {
asyncChange({commit}, payload) {
setTimeout(() => {
commit('syncChange',payload)
},1000)
}
}
})
//mutations中增加异步操作 严格模式下会直接报错,普通模式下不会报错但不合法
2.Vuex原理解析实现
首先我们要清楚Vuex的定位,它是一个插件。且必须基于之上Vue来使用,为什么这么说呢,因为他的数据响应是基于Vue的。
1.Vuex核心概念
① state 驱动应用的数据源。
② Getter getter的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生变化了改变才会被重新计算(由此你是不是想到了计算属性,对完全可以这么理解)。
③ Mutation 进行Vuex中store状态的更改,也是官方规定更改状态的唯一途径。
④ Action 进行异步操作的场所,但是更改数据还是需要commit提交。
⑤ Module 单一状态树对象比较复杂,Vuex允许我们将Store分割成多模块,每个模块拥有自己独立的内容。
2.实现Vuex
store.js
先创建一个入口文件
import Vue from 'vue'
// import Vuex from 'vuex' 官方的Vuex插件
import Vuex from './vuex/index1' // 自己写的Vuex
Vue.use(Vuex) // 默认会执行当前插件的install方法
// 通过Vuex中的一个属性 Store 创建一个store的实例
export default new Vuex.Store({
// 定义数据
modules: {
a: {
state: {
age: 'a100'
},
mutations: {
syncChange() {
console.log('a');
}
},
},
b: {
state: {
age: 'b100'
},
mutations: {
syncChange() {
console.log('b');
}
},
modules: {
c: {
state: {
age: 'c100'
},
mutations: {
syncChange() {
console.log('c');
}
},
}
}
}
},
state: {
age: 10
},
mutations: {
syncChange(state, payload) {
state.age += payload
}
},
actions: {
asyncChange({ commit }, payload) {
setTimeout(() => {
commit('syncChange', payload)
}, 1000)
}
},
getters: {
myAge(state) {
return state.age + 20
}
}
})
index1.js
这边会暴露出一个install方法,Vue.sue()的时候会调用它。还有一个提供实例化的Store类
let vue
const install = (_Vue) => {
Vue = _Vue // install方法调用时,会将Vue作为参数传入 Vue.mixin({ // 全局注册一个混入,影响注册以后的每一个创建的Vue实例。
beforeCreate() {
// 判断当前根实例上有没有store,有的话把根组件的的store属性 放到每个组件的实例上
// 这样每个组件上都能直接实现this.$store去访问store里面的东西
if(this.$option.store) {
this.$store = this.$options.store
} else {
this.$store = this.$parent && this.$parent.$store
}
}
})
}
class Store { // 用户获取的是这个store类的实例
constructor(options) {
// 创建Vue的实例 保证更新状态可以刷新视图
this.vm = new Vue({
data: {
state: optons.state
}
})
} // es6 的访问器
get state() {
return this.vm.state
} this.getters = {}
this.mutations = {}
this.actions = {}
// 1、需要将用户传入的数据进行格式化操作
this.moudules = new ModulesCollections(options)
// 2、递归的安装模块
installModule(this,this.state,[], this.modules.root)
// 调用
commit = (mutationName, payload) => {
// es7写法 这个里面的this 永远指向当前的store实例
this.mutaions[mutationName].forEach(fn =>fn(payload))
} dispath = (actionName, payload) => {
this.actions[actionName].forEach(fn =>fn(payload))
}
}
// 定义一个forEach遍历当前对象属性然后执行一个回调函数,后面要用到
const forEach = (obj, callback) => {
Object.keys(obj).forEach(key => {
callback(key, obj[key])
})
}
// 格式化用户数据
class ModuleCollection {
constructor(options) {
// 深度将所有的子模块都遍历一遍
this.register([], ooptions)
}
register(path, rootModule) {
let rawModule = {
_raw: rootModule,
_children: {},
state: rootModule.state
}
if(!this.root) {
this.root = rawModule
} else {
// 找到要定义的模块,将这个模块定义他父亲的_children属性里
let parentModule = path.slice(0,-1).reduce((root, current) => {
return root._children[current]
}, this.root)
parentModule._childen[path[path.length - 1]] = rawModule
} // 如果有子模块
if(rootModule.modules) {
forEach(rootModule.modules,(moduleName, module) => {
this.register(path.concat(moduleName), module)
})
}
}
}
// 递归安装模块
function installModule(store, rootState, path, rawModeule) {
// 如果有子模块,安装子模块的状态
if(path.length > 0) {
let parentState = path.slice(0,-1).reduce((root, current) => {
return root[current]
}, rootState)
Vue.set(parentState, path[path.length -1],rawModule.state)
} let getters = rawModule._raw.getters // 取用户的getter
if(getters) {
forEach(getters, (getterName, value) => {
Object.defineProperty(store.getters, getterName, {
get: () => {
return value(rawModule.state)
}
})
})
} let mutations = rawModule.raw.mutations // 取用户的mutation
if(mutations) {
forEach(mutations, (mutationName, value) => {
let arr = store.mutations[mutationName] || (store.mutaons[mutationName] = [])
})
arr.push((plyload) => {
value(rawModule.state, payload)
})
} let actions = rawModule._raw.actions // 取用户的action
if(actions) {
forEach(actions, (actionName, value) => {
let arr = store.actions[actionName] || (store.actions[actionName] = [])
arr.push((payload) => {
value(store, payload)
})
})
} // 递归
forEach(rawModule._childen, (moduleName, rawModule) => {
installModule(store, rootState, path.concat(moduleName),rawModule)
})
}
3.实现步骤总结:
1、作为插件引入,执行install方法调用Vue.mixin在Vue全局生命周期混入一个方法,将Vuex中定义的数据源挂载到this.$store,即当前组件的实例上。
2、state 直接new Vue实例,将数据源传入。完成数据源响应式操作。
3、getters 递归遍历用户传入的getters对象,拿到每个里面每一个函数,通过Object.definedProperty属性处理。当get函数读取compile,触发get调用相应函数(函数内部自动传入当前数据源state作为参数),完成数据响应。
4、mutations 递归遍历用户传入的mutations 对象,将相同名称下的函数都挂载到当前实例的mutations数组中,完成订阅。commit的时候拿到对应的函数名称进行遍历mutations数组调用对应名称函数,完成发布。
5、actiosns 操作和mutations一样。
6、module 是将用户传入的数据进行格式化,格式化好以后执行上面的安装模块的方法。具体查看上方installModule方法的详细操作。
手写Vuex源码的更多相关文章
- Spring学习之——手写Spring源码V2.0(实现IOC、D、MVC、AOP)
前言 在上一篇<Spring学习之——手写Spring源码(V1.0)>中,我实现了一个Mini版本的Spring框架,在这几天,博主又看了不少关于Spring源码解析的视频,受益匪浅,也 ...
- 手写Redux-Saga源码
上一篇文章我们分析了Redux-Thunk的源码,可以看到他的代码非常简单,只是让dispatch可以处理函数类型的action,其作者也承认对于复杂场景,Redux-Thunk并不适用,还推荐了Re ...
- 手写koa-static源码,深入理解静态服务器原理
这篇文章继续前面的Koa源码系列,这个系列已经有两篇文章了: 第一篇讲解了Koa的核心架构和源码:手写Koa.js源码 第二篇讲解了@koa/router的架构和源码:手写@koa/router源码 ...
- 手写Tomcat源码
http://search.bilibili.com/all?keyword=%E6%89%8B%E5%86%99Tomcat%E6%BA%90%E7%A0%81 tomcat源码分析一:https: ...
- Spring源码 20 手写模拟源码
参考源 https://www.bilibili.com/video/BV1tR4y1F75R?spm_id_from=333.337.search-card.all.click https://ww ...
- vuex源码阅读分析
这几天忙啊,有绝地求生要上分,英雄联盟新赛季需要上分,就懒着什么也没写,很惭愧.这个vuex,vue-router,vue的源码我半个月前就看的差不多了,但是懒,哈哈.下面是vuex的源码分析在分析源 ...
- 网狐6603 cocos2dx 棋牌、捕鱼、休闲类游戏《李逵捕鱼》手机端完整源码分析及分享
该资源说明: cocos2d 棋牌.捕鱼.休闲类游戏<李逵捕鱼>手机端完整源码,网狐6603配套手机版源码,可以选桌子,适合新手学习参考,小编已亲测试,绝对完整可编译手机端,下载后将文件考 ...
- Vuex 源码学习(一)
(一)Vuex 是什么? Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态, 并以相应的规则保证状态以一种可预测的方式发生变化. —— 来自 V ...
- vuex源码 安装依赖问题
今天下载vuex源码时 安装依赖出现以下问题 > chromedriver@2.32.3 install /Users/bao/Desktop/vue-store/vuex/node_modul ...
随机推荐
- C++中i++和++i的区别
目录 效果上的区别 性能上的区别 分析汇编代码 进行性能实验 二者的选择 效果上的区别 i++是对变量i递增,但返回原值,++i是对变量i进行递增,并返回终值. 可以用以下代码加以验证: int i ...
- 字符串String和list集合判空验证
1`字符串判断处理: 结论: 当if判断条件为两个,并且它们两个为或的关系,如果第一个条件为false,则继续第二个条件的判断:如果第一个条件为true,该例子不足以说明是否判断第二个条件, 最终可以 ...
- eclipse 导入下载或拷贝的java Web项目时报错 ,或者是报错Unbound classpath container: 'JRE System Library
在Problems里报错Description Resource Path Location Type Unbound classpath container: 'JRE System Library ...
- 《UNIX环境高级编程》(APUE) 笔记第七章 - 进程环境
7 - 进程环境 Github 地址 1. main 函数 C 程序总是从 main 函数 开始执行: int main(int argc, char *argv[]); \(argc\) 为命令行参 ...
- sql:主键(primary key)和唯一索引(unique index)区别
主键一定是唯一性索引,唯一性索引并不一定就是主键. 所谓主键就是能够唯一标识表中某一行的属性或属性组,一个表只能有一个主键,但可以有多个候选索引. 因为主键可以唯一标识某一行记录,所以可以确保执行数据 ...
- 泛型<T>,是你肿莫了,还是我错了...
委托自定义数组排序 项目一共三个文件如下. CSort.cs using System; using System.Collections.Generic; using System.Linq; us ...
- (私人收藏)SQLite 全面手册以及教程
SQLite 全面手册以及教程 https://pan.baidu.com/s/1VKzCWjF79fAiOLjAlx-3zwnfkp SQLite 全面手册以及教程 SQLite 简介 SQLite ...
- 【蓝桥杯】2018年第九届蓝桥杯C/C++B组省赛——B题 等差素数列
题目 标题:等差素数列 2,3,5,7,11,13,....是素数序列. 类似:7,37,67,97,127,157 这样完全由素数组成的等差数列,叫等差素数数列. 上边的数列公差为30,长度为6. ...
- 深入了解JVM-方法区
本文首发于微信公众号[猿灯塔],转载引用请说明出处 今天呢!灯塔君跟大家讲: 深入了解JVM-方法区 当JVM使用类装载器装载某个类时,它首先要定位对应的class文件,然后读入这个class文件,最 ...
- expected single matching bean but found 2: menusServiceImpl,IMenusService
问题如下: 接口也作为匹配的bean? 有点迷惑了....... 经过在网上找资料,发现和@MapperScan这个注解有关系,具体源码不止.但是这个注解会扫描路径下的所有类. 去掉这个注解就可以正常 ...