资讯专栏INFORMATION COLUMN

redux 源码分析,实现一个迷你的redux

Ashin / 1683人阅读

摘要:实现一个先不考虑中间件,实现一个简洁的实现是最主要的一个了,通过可以创建一个用来存放应用中所有的,一个应用只能有一个。方法是用来把每一个用方法包裹一下,因为可能只是返回一个具有属性的对象,只有用执行才有意义。正好可以利用的特性实现这个效果。

实现一个redux 先不考虑中间件,实现一个简洁的redux 实现createStore

createStore是redux最主要的一个API了,通过createStore可以创建一个store用来存放应用中所有的state,一个应用只能有一个store。

// 先创建一个mini-redux.js文件:
export function createStore(reducer) {
    // 初始化store
    let currentStore = {};
    // 初始化事件列表
    let currentListeners = [];
    
    // 获取state
    function getState() {
        return currentStore;
    }
    // 订阅事件
    function subscribe(listener) {
        currentListeners.push(listener);
    }
    // 定义dispatch方法
    function dispatch(action) {
        currentStore = reducer(currentStore, action);
        currentListeners.forEach(v => v());
        // return dispatch;
    }
    // 默认执行reducer type类型不要命中reducer中自定义的case
    dispatch({type: "@ZQT-REDUX"});
    return {getState, subscribe, dispatch}
}

上面创建了一个redux.js文件,并暴露了一个createStore方法,接受reducer作为参数

// 创建mini-react-redux.js
import React from "react";
import PropTypes from "prop-types";

export const connect = (mapStateToProps = state => state, mapDispatchToProps = {}) => (WrapComponent) => {
    return class connentComponent extends React.Component{
        static contextTypes = {
            store: PropTypes.object
        }
        constructor(props, context) {
            super(props, context);
            this.state = {
                props: {}
            }
        }
        componentDidMount() {
            const {store} = this.context;
            // 为什么非要订阅  因为没一个connect实际上就是一个订阅  每当dispatch执行的时候  就要重新执行以下update方法
            store.subscribe(() => this.update());
            this.update();
        }
        update = () => {
            const {store} = this.context;
            const stateProps = mapStateToProps(store.getState());

            // 每一个action需要用dispatch包裹一下
            const stateDispatch = bindActionCreators(mapDispatchToProps, store.dispatch);
            this.setState({
                props: {
                    ...this.props,
                    ...stateProps,
                    ...stateDispatch
                }
            })
        }
        render() {
            return 
        }
    }
}

export class Provider extends React.Component{
    static childContextTypes = {
        store: PropTypes.object
    }
    getChildContext() {
        return {
            store: this.store
        }
    }
    constructor(props, context) {
        super(props, context);
        this.store = props.store;
    }
    render() {
        return this.props.children
    }
}

function bindActionCreators(creators, dispatch) {
    const bound = {};
    Object.keys(creators).forEach(v => {
        bound[v] = bindActionCreator(creators[v], dispatch);
    })
    return bound;
}
function bindActionCreator(creator, dispatch) {
    return (...args) => dispatch(creator(...args))
}

上面创建了mini-react-redux.js文件,主要暴露了connect方法和Provider组件。

先看Provider组件。Provider利用的react的context属性,把store注入到Provider组件,并返回this.props.children(也就是Provider组件里面嵌入的组件,一般是页面的跟组件App组件),这样所有的组件都可以共享store。

然后再看connect方法。connect方法是一个双重嵌套的方法(专业名词叫函数柯里化)里面的方法接受一个组件并且返回一个组件,正式高阶组件的用法,外面的函数接受mapStateToProps和mapDispatchToProps两个参数,mapStateToProps是用来把store里面的数据映射到组件的props属性中,mapDispatchToProps是把用户自己定义的action映射到组件的props属性中。

在componentDidMount方法里面执行了store.subscribe(() => this.update())这句代码,是因为每次使用dispatch触发一个action的时候都要执行一下update方法,即重新获取store数据并映射到组件中去,这样才能保证store数据发生变化,组件props能同时跟着变化。

bindActionCreators方法是用来把每一个action用dispatch方法包裹一下,因为action可能只是返回一个具有type属性的对象,只有用dispatch执行action才有意义。

到此为止,一个没有中间件的不支持异步dispatch的简洁版的redux已经实现了,创建一个demo,就可以看到效果了
// 创建index.js 作为项目入口文件,大家可以自己添加action和reducer,就可以查看效果
import React from "react";
import ReactDOM from "react-dom";
import { createStore, applyMiddleware } from "./mini-redux";
import { counter } from "./index.redux"
import { Provider } from "./mini-react-redux";
import App from "./App"

const store = createStore(counter);
ReactDOM.render(
  (
    
      
    
  ),
  document.getElementById("root"))
支持中间件和异步action的redux实现

上面实现了简洁版的redux,再此基础上添加支持中间件的代码

// 修改mini-redux.js为
export function createStore(reducer, enhancer) {
    if(enhancer) {
        return enhancer(createStore)(reducer)
    }
    let currentStore = {};
    let currentListeners = [];

    function getState() {
        return currentStore;
    }

    function subscribe(listener) {
        currentListeners.push(listener);
    }

    function dispatch(action) {
        currentStore = reducer(currentStore, action);
        currentListeners.forEach(v => v());
        // return dispatch;
    }

    dispatch({type: "@ZQT-REDUX"});
    return {getState, subscribe, dispatch}
}

export function applyMiddleware(...middlewares) {
    return createStore=>(...args)=> {
        // 这里args 就是上面createStore 传过来的reducers
        const store = createStore(...args)
        let dispatch = store.dispatch
        // 暴漏 getState 和 dispatch 给 第三方中间价使用
        const midApi = {
            getState: store.getState,
            dispatch: (...args) => dispatch(...args)
        }
        // 创造第三方中间件使用 middlewareAPI 后返回的函数组成的数组
        const middlewareChain = middlewares.map(middleware => middleware(midApi))
        // 结合这一组函数 和 dispatch 组成的新的 dispatch,然后这个暴漏给用户使用,而原有的 store.dispatch 是不变的,但是不暴漏
        dispatch = compose(...middlewareChain)(store.dispatch);
        return{
            ...store,
            dispatch
        }
    }
}

export function compose(...funcs) {
    if(funcs.length === 0){
        return arg => arg
    }
    if(funcs.length === 1) {
        return funcs[0]
    }
    return funcs.reduce((ret, item) => (...args) => item(ret(...args)));
}

createStore方法修改了一下,多接受了一个enhancer方法,enhancer就是在index.js创建store的时候传过来的applyMiddleware方法。判断是否传了enhancer参数,如果有就return enhancer(createStore)(reducer)

applyMiddleware方法接受多个中间件作为参数,这个方法的最终目的就是创建一个新的dispatch属性,新的dispatch属性是经过中间件修饰过的,并且暴露这个新的dispatch属性,原来的dispatch属性不变。

compose方法是一个可以吧compose(fn1,fn2,fn3)(arg)转为fn3(fn2(fn1(arg)))的方法,也就是fn1的执行结果作为fn2的参数,fn2的执行结果作为fn1的参数,依次类推。正好可以利用reduce的特性实现这个效果。

const thunk = ({getState, dispatch}) => next => action => {
    // 如果是函数  就执行action
    if(typeof action === "function") {
        return action(dispatch, getState)
    }
    return next(action)
}
export default thunk

异步action在定义的时候返回的就是一个接受一个dispatch的方法,所以如果action是一个函数,就吧dispatch和getState方法传给该action,并且执行该action。如果不是一个函数,就直接返回action。

到此为止一个支持中间件的redux就实现了,该demo只是为了学习redux的思想,不能作为真正的redux来使用,有很多类型检查代码都省略了

从实现迷你版的redux可以体会到redux精巧的设计和函数式编程的魅力,有队函数式编程感兴趣的可以看一下这篇文章https://llh911001.gitbooks.io...

github源码地址:https://github.com/zhuqitao/z...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/109188.html

相关文章

  • 去哪儿网迷你React研发心得

    摘要:市面上竟然拥有多个虚拟库。虚拟库,就是出来后的一种新式库,以虚拟与算法为核心,屏蔽操作,操作数据即操作视图。及其他虚拟库已经将虚拟的生成交由与处理了,因此不同点是,虚拟的结构与算法。因此虚拟库是分为两大派系算法派与拟态派。 去哪儿网迷你React是年初立项的新作品,在这前,去哪儿网已经深耕多年,拥有QRN(react-native的公司制定版),HY(基于React的hybird方案)...

    pekonchan 评论0 收藏0
  • express分析和对比

    摘要:前言目前最新版本是所以本文分析也基于这个版本。源码分析直接切入主题由于目前是一个独立的路由和中间件框架。所以分析的方向也以这两个为主。源码去年的时候有分析过现在对比分析思考下。 前言 目前express最新版本是4.16.2,所以本文分析也基于这个版本。目前从npm仓库上来看express使用量挺高的,express月下载量约为koa的40倍。所以目前研究下express还是有一定意义...

    mmy123456 评论0 收藏0
  • 高性能迷你React框架anujs1.1.4发布

    摘要:本周在支持机票的项目中对做了大量改进,包括性能上与结构上的改进。但通过一些简化改改良,代码的可靠性大大提高了。此外,还有周边的优化在目录下提供一个,用于在旧式中替换。改善,里面内置了一个补丁,也是用于改善性能,或中的性能好差。 本周在支持机票的项目中对anujs做了大量改进,包括性能上与结构上的改进。与1.1.3一样,还是差一个组件就完全兼容阿里的antd UI库。 框架本身的改进有:...

    elva 评论0 收藏0
  • 动手实现一个最简单redux

    摘要:原文地址的主要集中在函数返回值中,以下这个迷你的只简单实现方法,如下测试代码运行结果 原文地址:https://github.com/huruji/blog/issues/1 redux的主要API集中在createStore函数返回值中,以下这个迷你的redux只简单实现createStore、dispatch、subscribe、getState方法,如下: const creat...

    xiguadada 评论0 收藏0
  • redux源码解读--applyMiddleware源码解析

    摘要:的中间件主要是通过模块实现的。返回的也是一个对象这个其实就是,各个中间件的最底层第三层的哪个函数组成的圆环函数构成的这就是对源码的一个整体解读,水平有限,欢迎拍砖。后续的源码解读和测试例子可以关注源码解读仓库 applyMiddleware源码解析 中间件机制在redux中是强大且便捷的,利用redux的中间件我们能够实现日志记录,异步调用等多种十分实用的功能。redux的中间件主要是...

    Atom 评论0 收藏0

发表评论

0条评论

Ashin

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<