手写Redux核心原理
手写Redux核心原理
一.createStore核心逻辑实现
createStore的三个参数
- reducer:根据actions类型对store中数据状态进行更改,返回一个新的状态
- preloadedState:预存储store的状态(初始状态)
- enhancer:对store的功能进行增强
createStore的三个返回值
- getState:获取状态
- dispatch:触发action
- subscribe:订阅数据状态的变化
具体实现
function createStore (reducer,preloadedState){// currentState:store对象中存储的状态,需要用闭包对其进行缓存var currentState = preloadedState// 用来存放订阅者函数var currentListeners = []// 获取状态并闭包缓存currentStatefunction getState () {return currentState;}// 触发actionfunction dispatch (action) { currentState = reducer(currentState,action)// 循环数组for (var i= 0 ; i < currentListeners.length ;i++ ){// 获取订阅者const listen = currentListeners[i]// 调用订阅者listen()}}// 订阅数据状态的变化function subscribe (listener) {currentListeners.push(listener)}return { getState,dispatch,subscribe }
}
具体使用
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=<device-width>, initial-scale=1.0"><title>Document</title>
</head>
<body><button id="increment">+1</button><span id="count">0</span><button id="decrement">-1</button><script src="./myRedux.js"></script><script>// 创建reduce函数function reducer (state,action) {switch (action.type) {case 'increment':return state+1case 'decrement':return state-1default:return state}}// 创建storevar store = createStore(reducer,0)document.getElementById('increment').onclick = function(){// 触发action 让状态+1store.dispatch({type:'increment'})}document.getElementById('decrement').onclick = function(){// 触发action 让状态-1store.dispatch({type:'decrement'})}// 订阅store.subscribe(function(){document.getElementById('count').innerHTML = store.getState()})console.log(store.getState())</script>
</body>
</html>
二. 参数类型约束
约束reduce必须是一个函数
if (typeof reducer !== 'function') throw new Error('reducer必须是函数')
约束action必须是一个对象
if (!isPlainObject(action)) throw new Error('action不是一个对象')
怎么判断一个数据是否是对象
- 排除基本数据类型和null
- 区分数组和对象 采用原型对象对比的方式
- 如果当前传入的对象的原型链全等于proto的最顶层原型链 那么就肯定是对象
function isPlainObject (obj) {// 排除基本数据类型和nullif (typeof obj !== 'object' || obj === null) return false;// 区分数组和对象 采用原型对象对比的方式var proto = obj;while (Object.getPrototypeOf(proto) !== null){// 获取到proto的最顶层原型链proto = Object.getPrototypeOf(proto)}// 如果当前传入的对象的原型链全等于proto的最顶层原型链 那么就肯定是对象return Object.getPrototypeOf(obj) === proto
}
约束action必须有type属性
if(typeof action.type === 'undefined') throw new Error('action对象中必须有type属性')
三.enhancer功能增强
它属于createStore的第三个参数,它本质上就是对dispatch当增强。(比如异步处理、中间件)
enhancer特性
- 目的是让用rudux库的人能可以对返回的store对象进行功能增强操作
- 是createStore的第三个参数
- 可传可不传,如果传enhancer约束必须是一个函数它的形参接收createStore
- enhancer内部返回一个函数接收reducer和preloadedState,在函数内部定义了一个增强型函数
_dispatch
,最后赋值给dispatch - 最后同样返回
{ getState,dispatch,subscribe }
在createStore中对其约束
function createStore (reducer,preloadedState,enhancer){// 判读enhancer参数是否存在,判断它是不是函数if (typeof enhancer !== "undefined") {if (typeof enhancer !== 'function') {throw new Error('enhancer必须是函数')}return enhancer(createStore)(reducer,preloadedState)}...
增强dispatch代码实现异步处理
function enhancer (createStore) {return function(reducer,preloadedState) {var store = createStore(reducer,preloadedState)var dispatch = store.dispatch;function _dispatch (action) {if(typeof action === 'function') {// 这里就可以处理异步了return action(dispatch)}dispatch(action)}return {...store,dispatch:_dispatch}}
}
// 创建store
var store = createStore(reducer,0,enhancer)
document.getElementById('increment').onclick = function(){// 触发action 让状态+1store.dispatch(function(dispatch){setTimeout(function(){store.dispatch({type:'increment'})},1000)})
}
四.实现applyMiddleware
就是一个enhancer当实现函数,作为中间件
中间件原理
在视图中触发action
时会先被中间件接收到,中间件一个一个执行完成之后才会触发原始的dispath
进入reducer. 中间件就是允许我们在
action被发出之后在
reducer`接收之前让我们去做一些事情
applyMiddleware实现
- applyMiddleware其实就是一个内置的enhancer函数
- 它的参数是接收若干中间件
- 它的返回值和enhancer一样,只不过dispatch是第一层中间件返回函数
// applyMiddleware其实就是一个内置的enhancer函数,它用来对store增强
function applyMiddleware (...middlewares) {return function (createStore) {return function(reducer,preloadedState) {// 创建store 给中间件传参var store = createStore(reducer,preloadedState)// 阉割版的 storevar middlewareAPI = {getState : store.getState,dispatch : store.dispatch}// 调用中间件的第一层函数 传递阉割版的store对象 把第二层函数缓存到chain中var chain = middlewares.map(middleware => middleware(middlewareAPI))// 为什么要穿dispatch呢? 因为中间件最后一个next就是dispatchvar dispatch = compose(...chain)(store.dispatch)// 最后返回:所以在第一个调用dispatch时 其实执行的是logger最里层返回的函数->然后在函数内部去调用thunk->最后调用原始的dispath进入reduxreturn {...store,dispatch}}}
}
实现compose给第二层函数传参
function compose () {var funcs = [...arguments]return function(dispatch) {for (let index = funcs.length-1; index >= 0; index--) {// 倒着调用第二层函数,得到它的返回值 当做参数传给上一层dispatch = funcs[index](dispatch);}// 这个dispatch就是第一个中间件函数return dispatch}
}
logger
function logger (store){// logger的下一个中间件函数是thunk 所以next就是thunk最里层的函数return function (next){return function (action){console.log('logger')next(action)}}
}
thunk
function thunk (store){// thunk是最后一个中间件函数 所以next就是reducereturn function (next){return function (action){console.log('thunk')next(action)// => 其实就是 dispatch({ type: "increment" });}}
}
使用方式
// 创建store
var store = createStore(reducer,0,applyMiddleware(logger,thunk))
store.dispatch({type:'increment'}) // 输出 logger thunk
五.bindActionCreators函数
主要用于父组件给子组件传参,子组件直接调用函数即可。而不需要知道Redux的使用方式。
bindActionCreators特性
- 将
actionCreators
函数转换成可以触发actions的函数 - 第一个参数接收一个函数对象,第二个参数接收的是dispatch
实现原理
// 将actionCreators函数转换成可以触发actions的函数
function bindActionCreators (actionCreators,dispatch) {var boundActionCreators = {};for (const key in actionCreators) {(function(key){// 沙盒模式 缓存keyboundActionCreators[key] = function(){ dispatch(actionCreators[key]())}})(key)}return boundActionCreators;
}
使用方式
function increment () {return {type:'increment'}
}
function decrement () {return {type:'decrement'}
}
var actions = bindActionCreators({increment,decrement},store.dispatch)actions.increment() //+1
actions.decrement() //-1
五.实现combineReducers
可以理解为是对reducer的一个增强,将一个个小的reducer汇总成一个大的reducer,同时将所有小的state也汇总在一起。
特性
combineReducers
将一个个小的reduce转换成一个大的reduce- 返回值是一个reducer函数 reducer函数它有两个参数一个是state一个是action
- 第一件事是循环第一个参数对象 拿到reducer 看是不是函数类型
- 第二件事依然循环它 并调用小的reducer 并把对应的状态赋值给count 最后把它赋值给一个大的对象 并返回
实现
function combineReducers (reducers) {var reduceKeys = Object.keys(reducers)// 第一件事是循环第一个参数对象 拿到reduce 看是不是函数类型 for (let index = 0; index < reduceKeys.length; index++) {const key = reduceKeys[index];if (typeof reducers[key] !== 'function') throw new Error('reducer必须是一个函数')}// 返回的是一个增强型当reducer,在dispatch里调用return function (state,action) {var nextState = {}// 第二件事依然循环它 并调用小的reduce 并把对应的状态赋值给count 最后把它赋值给一个大的对象 并返回for (let index = 0; index < reduceKeys.length; index++) {const key = reduceKeys[index];nextState[key] = reducers[key](state[key],action)}console.log(nextState)return nextState}
}
使用
var rootReducer = combineReducers({counter:reducer})
var store = createStore(rootReducer,{counter:100},applyMiddleware(logger,thunk))
手写Redux核心原理相关推荐
- babel原理_手写webpack核心原理,再也不怕面试官问我webpack原理
手写webpack核心原理 一.核心打包原理 1.1 打包的主要流程如下 1.2 具体细节 二.基本准备工作 三.获取模块内容 四.分析模块 五.收集依赖 六.ES6转成ES5(AST) 七.递归获取 ...
- 手写Vuex核心原理,再也不怕面试官问我Vuex原理
手写Vuex核心原理 文章目录 手写Vuex核心原理 一.核心原理 二.基本准备工作 三.剖析Vuex本质 四.分析Vue.use 五.完善install方法 六.实现Vuex的state 七.实现g ...
- 手写Hooks核心原理
手写实现useState useEffect useReducer 一. useState 钩子函数的实现原理 // import { useState } from 'react' import R ...
- 定时器和promise_手写Promise核心原理,再也不怕面试官问我Promise原理
整体流程的介绍 整体流程的介绍 1. 定义整体结构 2. 实现Promise构造函数 3. 实现then方法 3.实现catch方法 4. 实现Promise.resolve 5.实现Promise. ...
- 05. 手写Spring核心框架
目录 05 手写Spring核心框架 Pt1 手写IoC/DI Pt1.1 流程设计 Pt1.2 基础配置 application.properties pom.xml web.xml Pt1.3 注 ...
- 一起手写Vue3核心模块源码,掌握阅读源码的正确方法
最近和一个猎头聊天,说到现在前端供需脱节的境况.一方面用人方招不到想要的中高级前端,另一方面市场上有大量初级前端薪资要不上价. 特别是用 Vue 框架的,因为好上手,所以很多人将 Vue 作为入门框架 ...
- 【springboot】手写SpringBoot核心流程
通过手写模拟实现一个Spring Boot,就能以非常简单的方式就能知道Spring Boot大概是如何工作的. 工程与依赖 建一个工程,两个Module: spring-boot模块:表示sprin ...
- 手写识别底层原理_LinkedList底层原理和手写单链表
2.1 单链表技能点 · 认识单链表 o 特点 数据元素的存储对应的是不连续的存储空间,每个存储结点对应一个需要存储的数据元素. 每个结点是由数据域和指针域组成. 元素之间的逻辑关系通过存储节点之间的 ...
- 手写promise核心功能
手写promise 源码地址:https://github.com/CONOR007/Handwriting-Promise.git 手写promise首先需要掌握promise的基本用法,然后根据用 ...
最新文章
- 图像滤波常用算法实现及原理解析
- java使用url和tns两种方式连接数据库执行存储过程
- PHP5操作MySQL数据库
- python有哪些作用-python的函数有什么作用
- TensorBoard使用
- codeigniter钩子的使用
- 解决eclipse中Findbugs检查不生效的问题
- C语言中的回调函数(Callback Function)
- Android.mk的一些FAQ
- Github Projects 项目管理 怎么用
- innodb_file_per_table参数
- 《物联网通信》知识提纲-第1章 概述
- 几句代码让Ios系统内核崩溃
- 码农、程序员、工程师这三者之间有什么区别?
- matlab空间杜宾模型命令,空间计量模型,包括空间滞后模型、空间误差模型、空间杜宾模型的matlab代码...
- HDU - 5510 Bazinga
- VS将复制过来的文件或文件夹显示到解决方案管理
- 10分钟!Mac配置Win主机上的共享打印机
- AD9910使用心得-fanfanStudio
- 摘要、引言和结论的六项区别
热门文章
- [软件工具][windows]OCR指定区域图片自动识别内容重命名软件使用教程
- 阿斯利康疫苗研发教授和全球疫苗免疫联盟获得2022年鲜鹤平和赏
- 计算机毕业设计JAVA多特蒙德周边商城系统mybatis+源码+调试部署+系统+数据库+lw
- 如何入门单片机电子技术、学习方法、建议
- Andrej Karpathy的炼丹技巧
- 中国三氟乙醇行业研究与投资预测报告(2022版)
- 我的世界给钻石最多的服务器,我的世界:MC快速挖取钻石的方法,老玩家告诉你第几层的钻石最多...
- 打破区块链不可能三角!2 华人专家论文将登 NSDI 2019 计算机顶会
- 嵌入式linux学习笔记-- linux 开机总时间记录的一种方案
- 索爱X9挂脖式蓝牙耳机,佩戴轻盈音质纯正真正享受音乐世界