手写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 ...
随机推荐
- ELK安装 docker-compose.yml
version: '2.2' services: cerebro: image: lmenezes/cerebro:0.8.3 container_name: cerebro ports: - &qu ...
- java语言基础(九)_final_权限_内部类
final关键字 final关键字代表最终.不可改变的. 常见四种用法: 可以用来修饰一个类 可以用来修饰一个方法 还可以用来修饰一个局部变量 还可以用来修饰一个成员变量 1)修饰一个类 public ...
- pandas 模拟生成数据集的快速方法
快速生成一个DataFrame的方法: #模拟生成数据集的方法 import pandas as pd import numpy as np boolean=[True,False] gender=[ ...
- 「杂烩」精灵魔法(P1908逆序对弱化版)
「杂烩」精灵魔法(P1908逆序对弱化版) 题面: 题目描述 \(Tristan\)解决了英灵殿的守卫安排后,便到达了静谧的精灵领地--\(Alfheim\) .由于$ Midgard$ 处在$ Al ...
- 状压DP之吃奶酪
题目 传送们 思路 1≤n≤15,妥妥的状压,数据这么小, 这道题的状压思路还是很好想的,我们定义f[i][s]代表以i为起点,吃掉状态为s的奶酪所需要跑的最短距离,那么显然,我们先枚举状态s,然后枚 ...
- docker安装,卸载和入门
Docker 简介 背景 开发和运维之间因为环境不同而导致的矛盾 集群环境下每台机器部署相同的应用 DevOps(Development and Operations) 简介 Docker是一个开源的 ...
- Spring IoC 公共注解详解
前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 什么是公共注解?公共注解就是常见的Java ...
- (一)pandas的两种对象
将鱼图像数据进行操作,使用numpy知识 import numpy as np import matplotlib.pyplot as plt %matplotlib inline #咱们可以不用sh ...
- Python-02 可视化之tkinter介绍
1 控件介绍 1.1 Label import tkinter as tk # 使用Tkinter前需要先导入 window = tk.Tk() window.title('My Window') w ...
- python面试题六: 剑指offer
面试题3 二维数组中的查找 LeetCode题目:二维数组中,每行从左到右递增,每列从上到下递增,给出一个数,判断它是否在数组中思路:从左下角或者右上角开始比较 def find_integer(ma ...