<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Test</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
</head>
<body>
<script type='text/javascript'>
// var f = function( ...arg ){
// arg 是个数组
// ...arg 是传递进来的原封不动的值
// } // const reducer = function(state, action){
// if(!state){
// state = initialState;
// }
// let nextState = _.deepClone(state); // switch(action.type){
// case 'ADD_TODO':
// nextState.todos.push( action.payload );
// break;
// case 'INCREASE':
// nextState.counter = nextState.counter++;
// break;
// default:
// break;
// } // return nextState;
// } /*** compose ***/
// compose(func3, func2, func1)(0);
// 相当于写了 func3( func2( func1(0) ) )
const compose = function( ...funcs ) {
// 若没有传递参数, 则直接返还1个函数
if ( funcs.length === 0 ) {
return arg => arg;
} // 若只有1个参数, 则直接返还这个函数
if ( funcs.length === 1 ) {
return funcs[0];
} // 取出最后1个函数 当作 reduceRight的第2个参数
const last = funcs[funcs.length - 1];
// 取除了最后一个函数以外的函数数组
const rest = funcs.slice(0, -1);
// 所传递进来的参数 都当作 最后1个函数的参数
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
} /*** combineReducers ***/
const combineReducers = function(obj){
return function(state, action){
// nextState 为返回的新 state
let nextState = {};
for(var item in obj){
// obj[item] 为item 对应的reducer
/*
state[item] 的意思是 state 对应与 item 上的值,
比如 state 为 { counter: 0}, state[item]的值就为 0
*/
nextState[item] = obj[item]( state[item], action );
}
return nextState;
}
} /*** bindActionCreators ***/
const bindActionCreators = (obj, dispatch) => {
let objNew = {};
for(let i in obj){
// item 为创建 action 的函数
let item = obj[i];
objNew[i] = (...args) => {
dispatch( item(...args) );
}
}
return objNew;
} /*** applyMiddleware ***/
// 传入所有的中间件
const applyMiddleware = (...middlewares) => {
// 可以传入 createStore 作为参数
return (createStore) => {
// 这一步已经生成了 增强版本的 createStore
return (reducer, initialState, enhancer) => { let store = createStore(reducer, initialState, enhancer); // 这一步原来的写法是
// let chain = middlewares.map( middleware => middleware(store) );
// 源代码里是 从新创建了一个store
let storeNew = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
let chain = middlewares.map( middleware => middleware(storeNew) ); // 只有最后1个函数的传入了 store.dispatch 当作 dispatch,
// 其它函数都是最后1个函数的返回值当作 dispatch
const dispatch = compose(...chain)(store.dispatch); /*
最早本来想下面那样写,
这个结果是所有中间件的 dispatch 都为 store.dispach let chain = [...middlewares].map( (item) => {
return item(store)(store.dispatch);
} ); const dispatch = (action) => {
compose(...chain)(action);
}; 打印的结果为: 3 dispatch before
3 dispatch after
2 dispatch before
2 dispatch after
1 dispatch before
1 dispatch after */ return {
getState: store.getState,
subscribe: store.subscribe,
replaceReducer: store.replaceReducer,
dispatch: dispatch
}
}
}
} /*** createStore ***/
const createStore = function(reducer, initialState, enhancer){
if( typeof enhancer === "function"){
return enhancer(createStore)(reducer, initialState);
} let state = initialState;
// 操作的 listeners
let listeners = [];
let listenersCache = listeners; // 但凡 listeners 有变化, 比如push了 或者删除了, 都提前执行下 ensure
const ensure = function(){
// 若相等, 则拷贝一个新的 listeners
if( listeners == listenersCache ){
listeners = listenersCache.slice();
}
} const getState = function(){
return state;
} const dispatch = function(action){
state = reducer(state, action); // 为了让 ensure 执行成功
listenersCache = listeners;
let len = listenersCache.length;
for(let i = 0; i < len; i++){
listenersCache[i]();
} return action;
} const subscribe = listener => {
ensure();
listeners.push(listener); return function(){
ensure();
listeners = listeners.filter(function(item, i){
return item != listener;
});
} } dispatch({});
return {
dispatch: dispatch,
getState: getState,
subscribe: subscribe
}
} const initialState = {
counter: 0,
todos: []
} const counterReducer = function(counter = 0, action){
switch(action.type){
case 'INCREASE':
console.log('Reducer increase');
return counter = counter + 1;
default:
return counter;
}
} const todosReducer = function(todos = [], action){
switch(action.type){
case 'ADD_TODO':
return [...todos, action.payload];
default:
return todos;
}
} const rootReducer = combineReducers({
counter: counterReducer,
todos: todosReducer
}); const actionAdd = (n) => {
return {type: n}
} const store = createStore(rootReducer, initialState); const actionCreate = bindActionCreators(
{actionAdd: actionAdd},
store.dispatch
); store.subscribe(function(){
console.log( store.getState() );
}); store.dispatch( actionAdd('INCREASE') );
actionCreate.actionAdd('INCREASE');
store.dispatch({type: 'ADD_TODO', payload: 'hello world'}); console.log(' ---------- 分割线 ---------- '); // 中间件 1
// 根据源代码 middlewareAPI 是一个新的对象, 类似 store
// 只有store 里的dispatch 和 getState方法
const middleware1 = ( middlewareAPI ) => {
return (dispatch) => {
return (action) => {
console.log( '1 dispatch before' ); // 这个 dispatch 其实是 middleware2 里的函数 (action) => {...}
let actionReturn = dispatch(action); console.log( '1 dispatch after' );
return actionReturn;
}
}
}; const middleware2 = ( middlewareAPI ) => {
return (dispatch) => {
return (action) => {
console.log( '2 dispatch before' ); // 这个 dispatch 其实是 middleware3 里的函数 (action) => {...}
let actionReturn = dispatch(action); console.log( '2 dispatch after' );
return actionReturn;
}
}
}; const middleware3 = ( middlewareAPI ) => {
return (dispatch) => {
return (action) => {
console.log( '3 dispatch before' ); // 这个 dispatch 才是 store.dispatch, 最终只有这里调用 reducer 更新
let actionReturn = dispatch(action); console.log( '3 dispatch after' );
return actionReturn;
}
}
}; let enhancedCreateStore = applyMiddleware(middleware1, middleware2, middleware3)(createStore);
let storeEnhance = enhancedCreateStore(rootReducer, initialState); /*
最后的打印结果是: 1 dispatch before
2 dispatch before
3 dispatch before
Reducer increase
3 dispatch after
2 dispatch after
1 dispatch after
*/
storeEnhance.dispatch({type: 'INCREASE'}); </script>
</body>
</html>

Redux 源码自己写了一遍的更多相关文章

  1. redux源码解读(二)

    之前,已经写过一篇redux源码解读(一),主要分析了 redux 的核心思想,并用100多行代码实现一个简单的 redux .但是,那个实现还不具备合并 reducer 和添加 middleware ...

  2. redux源码解读(一)

    redux 的源码虽然代码量并不多(除去注释大概300行吧).但是,因为函数式编程的思想在里面体现得淋漓尽致,理解起来并不太容易,所以准备使用三篇文章来分析. 第一篇,主要研究 redux 的核心思想 ...

  3. Redux 源码解读 —— 从源码开始学 Redux

    已经快一年没有碰过 React 全家桶了,最近换了个项目组要用到 React 技术栈,所以最近又复习了一下:捡起旧知识的同时又有了一些新的收获,在这里作文以记之. 在阅读文章之前,最好已经知道如何使用 ...

  4. 从Redux源码探索最佳实践

    前言 Redux 已经历了几个年头,很多 React 技术栈开发者选用它,我也是其中一员.期间看过数次源码,从最开始为了弄清楚某一部分运行方式来解决一些 Bug,到后来看源码解答我的一些假设性疑问,到 ...

  5. redux源码浅入浅出

    运用redux有一段时间了,包括redux-thunk和redux-saga处理异步action都有一定的涉及,现在技术栈转向阿里的dva+antd,好用得不要不要的,但是需要知己知彼要对react家 ...

  6. redux源码解读

    react在做大型项目的时候,前端的数据一般会越来越复杂,状态的变化难以跟踪.无法预测,而redux可以很好的结合react使用,保证数据的单向流动,可以很好的管理整个项目的状态,但是具体来说,下面是 ...

  7. Redux源码学习笔记

    https://github.com/reduxjs/redux 版本 4.0.0 先了解一下redux是怎么用的,此处摘抄自阮一峰老师的<Redux 入门教程> // Web 应用是一个 ...

  8. redux源码阅读之compose,applyMiddleware

    我的观点是,看别人的源码,不追求一定要能原样造轮子,单纯就是学习知识,对于程序员的提高就足够了.在阅读redux的compose源码之前,我们先学一些前置的知识. redux源码阅读之compose, ...

  9. 带着问题看redux源码

    前言 作为前端状态管理器,这个比较跨时代的工具库redux有很多实现和思想值得我们思考.在深入源码之前,我们可以相关注下一些常见问题,这样带着问题去看实现,也能更加清晰的了解. 常见问题 大概看了下主 ...

随机推荐

  1. Tomcat报错:Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/JFreeChartTest]]

    最好把项目移除,然后在tomcat的webapps发布路径下也把项目文件删掉,重新部署就好了,原因是可能在tomcat的remove覆盖中以前的文件有所保留导致冲突,亲测有效

  2. Centos服务器ssh免密登录以及搭建私有git服务器

    一.概述 服务器的免密登录和git服务器的搭建,关键都是要学会把自己用的机器的公钥添加到服务器上,让服务器“认识”你的电脑,从而不需要输入密码就可以远程登录服务器上的用户 免密登录当然是登录root用 ...

  3. python面向对象进阶(下)

    一.item系列:就是把字典模拟成一个字典去操作(操作字典就用item的方式) obj[‘属性’]的方式去操作属性时触发的方法 __getitem__:obj['属性'] 时触发 __setitem_ ...

  4. C#中执行批处理文件(.bat),执行数据库相关操作

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  5. 【bzoj4373】算术天才⑨与等差数列

    同之前那道由乃题,可以认为由乃题是这题的特殊情况…… 维护方法是同样的,维护区间和,区间平方和即可. 注意特判一个数(其实没有必要) #include<bits/stdc++.h> ; u ...

  6. JAVA常见的集合类

    关系的介绍: Set(集):集合中的元素不按特定方式排序,并且没有重复对象.他的有些实现类能对集合中的对象按特定方式排序. List(列表):集合中的元素按索引位置排序,可以有重复对象,允许按照对象在 ...

  7. [New learn]GCD的基本使用

    https://github.com/xufeng79x/GCDDemo 1.简介 介绍GCD的使用,介绍多种队列与同步异步多种情况下的组合运行情况. 2.基本使用步骤 如果使用GCD则一般也就两个步 ...

  8. JS如何获取Input的name或者ID?

    <input name="music" type="image" id="music" onclick="loadmusic ...

  9. CentOS安装按进程实时统计流量情况工具NetHogs笔记

    CentOS安装按进程实时统计流量情况工具NetHogs笔记 一.概述 NetHogs是一款开源.免费的,终端下的网络流量监控工具,它可监控Linux的进程或应用程序的网络流量.NetHogs只能实时 ...

  10. Linux设备驱动--内存管理

           MMU具有物理地址和虚拟地址转换,内存访问权限保护等功能.这使得Linux操作系统能单独为每个用户进程分配独立的内存空间并且保证用户空间不能访问内核空间的地址,为操作系统虚拟内存管理模块 ...