手写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 = []
    // 获取状态并闭包缓存currentState
    function getState () {
        return currentState;
    }

    // 触发action
    function 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+1
                case 'decrement':
                    return state-1
                default:
                return state
            }
        }
        // 创建store
        var store = createStore(reducer,0)

        document.getElementById('increment').onclick = function(){
            // 触发action 让状态+1
            store.dispatch({type:'increment'})
        }

        document.getElementById('decrement').onclick = function(){
            // 触发action 让状态-1
            store.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不是一个对象')
怎么判断一个数据是否是对象
  1. 排除基本数据类型和null
  2. 区分数组和对象 采用原型对象对比的方式
  3. 如果当前传入的对象的原型链全等于proto的最顶层原型链 那么就肯定是对象
function isPlainObject (obj) {
    // 排除基本数据类型和null
    if (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 让状态+1
  store.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)
            // 阉割版的 store
            var middlewareAPI = {
                getState : store.getState,
                dispatch : store.dispatch
            }
            // 调用中间件的第一层函数 传递阉割版的store对象 把第二层函数缓存到chain中
            var chain = middlewares.map(middleware => middleware(middlewareAPI))
            // 为什么要穿dispatch呢? 因为中间件最后一个next就是dispatch
            var dispatch = compose(...chain)(store.dispatch)
            // 最后返回:所以在第一个调用dispatch时 其实执行的是logger最里层返回的函数->然后在函数内部去调用thunk->最后调用原始的dispath进入redux
            return {
                ...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就是reduce
    return 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){
            // 沙盒模式 缓存key
            boundActionCreators[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))
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redux 是一种 JavaScript 状态管理库,它可以帮助我们管理应用程序中的状态,并且可以在整个应用程序中共享状态。Redux 的实现原理主要有以下几个方面: 1. Store:Redux 应用程序中的所有状态都存储在一个单一的、不可变的存储区域中,称为 "store"。store 是一个纯 JavaScript 对象,它包含了整个应用程序的状态。store 还包含了一些方法,用于获取状态、监听状态变化、更新状态等操作。 2. Action:Redux 应用程序中的状态只能通过 "action" 来修改。action 是一个描述状态变化的普通 JavaScript 对象,它必须包含一个 "type" 属性,用于描述变化的类型。action 还可以包含一些其他的数据,用于描述变化的具体内容。 3. Reducer:Redux 应用程序中的状态修改是通过 "reducer" 来实现的。reducer 是一个纯函数,它接受当前的状态和一个 action,然后返回一个新的状态。reducer 不能修改原来的状态,它只能返回一个全新的状态。Redux 应用程序中可能会有多个 reducer,每个 reducer 负责管理一个部分状态。 4. Dispatch:Redux 应用程序中的状态修改是通过 "dispatch" 来触发的。dispatch 是一个方法,它接受一个 action,然后将 action 发送给所有的 reducer。每个 reducer 都会接收到 action,然后根据 action 的类型来修改自己管理的状态。 5. Subscribe:Redux 应用程序中的状态变化是通过 "subscribe" 来监听的。subscribe 是一个方法,它接受一个回调函数,每当状态发生变化时就会调用这个回调函数。我们可以在回调函数中获取最新的状态,然后根据状态来更新应用程序的界面。 综上所述,Redux 的实现原理就是通过 store、action、reducer、dispatch 和 subscribe 这些机制来实现状态管理的。通过这些机制,我们可以将应用程序中的状态集中管理,从而带来更好的可维护性、可扩展性和可重用性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值