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. 洛谷P3694 邦邦的大合唱站队【状压dp】

    状压dp 应用思想,找准状态,多考虑状态和\(f\)答案数组的维数(这个题主要就是找出来状态如何转移) 题目背景 \(BanG Dream!\)里的所有偶像乐队要一起大合唱,不过在排队上出了一些问题. ...

  2. asp.net 修饰符介绍(关于public、private、protected、internal)

    1.private修饰符 private修饰符用于设置类或类成员的访问权限仅为所属类的内部,private也被称为私有修饰符.某些时候需要访问私有类成员时,可通过get和set访问器读取或修改. 2. ...

  3. PKIX

    这是证书认证不通过的问题,对https协议免认证 http://blog.csdn.net/zziamalei/article/details/46520797 使用上面的方法时,使用spring的& ...

  4. Qt-绘图

    1  简介 参考视频:https://www.bilibili.com/video/BV1XW411x7NU?p=37 参考文档:<Qt教程.docx> 本文简单介绍Qt的绘图与绘图设备. ...

  5. JavaScript函数使用知识点回顾

    JS函数本质更像一个对象,有属性和方法. 将函数定义作为对象的属性,则称之为对象方法:函数如果用于创建新的对象,则称之为对象的构造函数. (1)JS使用关键字  function  定义函数. 函数可 ...

  6. Python-利用xlrd模块操作excel

    在工作中,无论是数据分析,还是批量导入数据,都会去操作excel,当然,数据分析有数据分析的方法,而我在开发中涉及到的是批量导入excel中的数据, 接下来介绍下如何利用python的xlrd模块来读 ...

  7. OutOfMemory相关问题(内存溢出异常OOM)

    OutOfMemory(内存溢出异常OOM) java.lang.OutOfMemoryError :Thrown when the Java Virtual Machine cannot alloc ...

  8. 通过注入DLL修改API代码实现钩取(一)

    通过注入DLL修改API代码实现钩取(一) Ox00 大致思路 通过CreateRemoteThread函数开辟新线程,并将DLL注入进去 通过GetProcessAddress函数找到需钩取的API ...

  9. JVM 专题十七:垃圾回收(一)简述

    1. 什么是垃圾 1.1 C++与Java 1.2 概述 垃圾收集,不是Java语言的伴生产物.早在1960年,第一门开始使用内存动态分配和垃圾收集技术的Lisp语言诞生. 关于垃圾收集有三个经典问题 ...

  10. Mysql基础(九):MySQL 事务

    一.含义事务:一条或多条sql语句组成一个执行单位,一组sql语句要么都执行要么都不执行二.特点(ACID)A 原子性:一个事务是不可再分割的整体,要么都执行要么都不执行C 一致性:一个事务可以使数据 ...