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源码的更多相关文章

  1. Spring学习之——手写Spring源码V2.0(实现IOC、D、MVC、AOP)

    前言 在上一篇<Spring学习之——手写Spring源码(V1.0)>中,我实现了一个Mini版本的Spring框架,在这几天,博主又看了不少关于Spring源码解析的视频,受益匪浅,也 ...

  2. 手写Redux-Saga源码

    上一篇文章我们分析了Redux-Thunk的源码,可以看到他的代码非常简单,只是让dispatch可以处理函数类型的action,其作者也承认对于复杂场景,Redux-Thunk并不适用,还推荐了Re ...

  3. 手写koa-static源码,深入理解静态服务器原理

    这篇文章继续前面的Koa源码系列,这个系列已经有两篇文章了: 第一篇讲解了Koa的核心架构和源码:手写Koa.js源码 第二篇讲解了@koa/router的架构和源码:手写@koa/router源码 ...

  4. 手写Tomcat源码

    http://search.bilibili.com/all?keyword=%E6%89%8B%E5%86%99Tomcat%E6%BA%90%E7%A0%81 tomcat源码分析一:https: ...

  5. Spring源码 20 手写模拟源码

    参考源 https://www.bilibili.com/video/BV1tR4y1F75R?spm_id_from=333.337.search-card.all.click https://ww ...

  6. vuex源码阅读分析

    这几天忙啊,有绝地求生要上分,英雄联盟新赛季需要上分,就懒着什么也没写,很惭愧.这个vuex,vue-router,vue的源码我半个月前就看的差不多了,但是懒,哈哈.下面是vuex的源码分析在分析源 ...

  7. 网狐6603 cocos2dx 棋牌、捕鱼、休闲类游戏《李逵捕鱼》手机端完整源码分析及分享

    该资源说明: cocos2d 棋牌.捕鱼.休闲类游戏<李逵捕鱼>手机端完整源码,网狐6603配套手机版源码,可以选桌子,适合新手学习参考,小编已亲测试,绝对完整可编译手机端,下载后将文件考 ...

  8. Vuex 源码学习(一)

    (一)Vuex 是什么? Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态, 并以相应的规则保证状态以一种可预测的方式发生变化. —— 来自 V ...

  9. vuex源码 安装依赖问题

    今天下载vuex源码时 安装依赖出现以下问题 > chromedriver@2.32.3 install /Users/bao/Desktop/vue-store/vuex/node_modul ...

随机推荐

  1. JedisUtils工具类模板

    redis.properties配置文件 redis.maxIdle=30 redis.minIdle=10 redis.maxTotal=100 redis.url=192.168.204.128 ...

  2. PL/SQL 美化器&规则解释&优化代码

    前言 PLSQL有非常强大的自定义设置功能,比如美化文件规则, 使用者可以自行定义编辑规则,以便更好的优化SQL语句,增加可读性. 例如以下的部分代码,又长,分段不好,空格太多,结构散乱,还没有注释. ...

  3. python之类与对象(一)

    1.改变对象的字符串显示,要改变一个实例的字符串表示,可重新定义它的 str () 和 repr () 方法 class Pair: def __init__(self, x, y): self.x ...

  4. java面试题——对于多态你是怎么理解的呢?不一样的角度,带你重新看java

    java面试的时候经常会被问到一个问题,那就是java三大特性:继承,封装和多态.那么这三者的含义究竟是什么你真的清楚吗?我看网上大多都是人云亦云.所以我想把我的想法记录下来供大家参考-今天先聊一个, ...

  5. Cypress与TestCafe WebUI端到端测试框架简介

    近期接触了Cypress和TestCafe,两个测试框架都基于Node.js,都不再使用Selenium+WebDriver,而且开箱即用,非常轻量级,就冲着不再使用WebDriver这一点,极大地勾 ...

  6. python 实现汉诺塔

    汉诺塔:汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具.大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘. 大梵天命令婆罗门把圆盘从下面开始按大小顺 ...

  7. UVA 11383 Golden Tiger Claw 题解

    题目 --> 题解 其实就是一个KM的板子 KM算法在进行中, 需要满足两个点的顶标值之和大于等于两点之间的边权, 所以进行一次KM即可. KM之后, 顶标之和就是最小的.因为如果不是最小的,就 ...

  8. Window - 安装 Jenkins

    安装方式 war 包放 Tomcat 下 直接安装 jenkins.msi 环境准备 安装 jdk.tomcat,参考:https://www.cnblogs.com/poloyy/p/1326781 ...

  9. classpath路径(转)

    src不是classpath, WEB-INF/classes,lib才是classpathWEB-INF/ 是资源目录, 客户端不能直接访问, 这话是没错,不过现在的IDE编译器在编译时会把src下 ...

  10. MYSQL 之 JDBC(二): 数据库连接(二)通过DriverManager获取数据库连接

    通过DriverManager获取数据库连接 修改一下配置文件 driver=com.mysql.cj.jdbc.Driver jdbcUrl=jdbc:mysql://localhost:3306/ ...