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. java语言进阶(二)_Collection_泛型

    主要内容 Collection集合 迭代器 增强for 泛型 第一章 Collection集合 1.1 集合概述 在前面基础班我们已经学习过并使用过集合ArrayList ,那么集合到底是什么呢? 集 ...

  2. java语言基础(九)_final_权限_内部类

    final关键字 final关键字代表最终.不可改变的. 常见四种用法: 可以用来修饰一个类 可以用来修饰一个方法 还可以用来修饰一个局部变量 还可以用来修饰一个成员变量 1)修饰一个类 public ...

  3. Web前端开发未来的六大趋势

    说起Web前端开发想必你一定不会陌生,因为现在的前端开发学习的培训机构也是层出不穷.下面济南优就业IT培训给大家总结出了未来Web前端开发的六大趋势从中可以大致看出来Web前端未来的发展前景. 趋势一 ...

  4. redo log 与 binlog

    redo log 与 binlog   redo log redo log (重做日志)是处于存储引擎层的,是InnoDB引擎特有的 redo log 存储的是物理日志 --- 即,"在某个 ...

  5. (二)ELK Filebeat简介

    Filebeat简介   轻量级的日志传输工具,是一个日志文件托运工具,在你的服务器上安装客户端后,filebeat会监控日志目录或者指定的日志文件,追踪读取这些文件(追踪文件的变化,不停的读),并且 ...

  6. TallestCow

    简单解说 建立差分数组. 以最高的牛为高度基点,假设牛A和牛B能相互看见,就把牛A和牛B中间的牛高度都-1 最后对每头牛直接计算输出即可. 需要注意的是他给出的关系中:两头牛的顺序可能是颠倒的,而且关 ...

  7. 状压dp大总结1 [洛谷]

    前言 状态压缩是一种\(dp\)里的暴力,但是非常优秀,状态的转移,方程的转移和定义都是状压\(dp\)的难点,本人在次总结状压dp的几个题型和例题,便于自己以后理解分析状态和定义方式 状态压缩动态规 ...

  8. Mysql如何取当日的数据

    下面的sql语句可以取出当日的数据 SELECT * FROM table WHERE 时间字段 BETWEEN DATE_FORMAT(NOW(),'%Y-%m-%d 00:00:00') AND ...

  9. webpack正式、测试环境接口地址本地运行及打包命令配置

    声明:本文由w3h5原创,转载请注明出处:<webpack正式.测试环境接口地址本地运行及打包命令配置> https://www.w3h5.com/post/521.html 为了方便开发 ...

  10. 阿里云OSS 服务端签名后直传之分片上传(结合element-ui的upload组件)

    分片上传(结合element-ui的upload组件实现自定义上传) async uploadFree(content){ let data = await this.getOssToken(); / ...