Learn the overall architecture of Redux source code, and deeply understand the principles of Redux and its middleware

Time:2020-9-16

1. Preface

Hello, I’m Ruochuan. This isLearn source code overall architecture seriesChapter eight. The word “overall architecture” seems to be a bit big. Even if it is the overall structure of the source code, the main thing is to learn the overall structure of the code, and not go into the implementation of other specific functions that are not the main line. This article studies the code of the actual warehouse.

If someone talks about how to read the source code, you can recommend my source code series articles, that’s great

Learn source code overall architecture seriesThe article is as follows:

1. Learn the overall framework of jQuery source code and create its own JS class library
2. Learn the overall framework of the source code of underscore and create its own functional programming class library
3. Learn the overall framework of lodash source code and create its own functional programming class library
4. Learn the overall framework of sentry source code and create its own front-end exception monitoring SDK
5. Learn the overall architecture of vuex source code, and create its own state management library
6. Learn the overall framework of Axios source code and create its own request library
7. Learn the overall framework of KOA source code, analyze the principle of KOA onion model and co principle
8. Learn the overall framework of Redux source code, and deeply understand the principles of Redux and its middleware
Interested readers can click to read.

Other source plans include:expressvue-rotuerreact-reduxWait for source code, I do not know when I can finish (crying), welcome to continue to pay attention to me (Ruochuan).

Source class articles, general reading is not high. If you have the ability to understand, you can read it yourself. Do not want to see, do not dare to see the source code.

So my article, try to write so that want to read the source code and do not know how to read the reader can understand.

Read this article to learn:

  1. git subtreeManage sub warehouse
  2. How to learnreduxSource code
  3. reduxMiddleware principle
  4. reduxeachAPIImplementation of
  5. vuexandreduxComparison of
  6. wait

1.1 the best way to read this article

Take minereduxSource code repositorygit clone https://github.com/lxchuan12/redux-analysis.gitClone it, by the waystarLet’s talk about my Redux source code learning repository^_ ^。Follow the rhythm of the article debugging and sample code debuggingchromeMore impressed by hands-on debugging。 This article does not need to look at the long code, you can debug it. Read this kind of source code article a hundred times, it may be better to debug several times. We also welcome wechat exchangesruochuan12

2. Git subtree manages the sub warehouse

Wrote a lot of source code articles,vuexaxioskoaAnd so on are using the new repository clone a source code in their own repository.
Although the computer can pull up the latest code and see the GIT information of the original author. But upload togithubAfter. Readers can’t see the author of the original warehousegitInformation. So I found itgit submodulesThe plan, but not very suitable. And then I found outgit subtree

In a nutshellnpm packageandgit subtreeThe difference.
npm packageIt’s one-way.git subtreeIt’s two-way.

For details, please refer to this article @ delay (original Zan boss): use git subtree to synchronize subprojects between multiple git projects, with a concise user manual

Got itgit subtreeAfter that, I built a new oneredux-analysisAfter the project, putreduxSource code4.x(as of June 13, 2020,4.xThe latest version of the branch is4.0.5masterThe branch istsI don’t want to be unfamiliar with some of themtsThe branch was cloned into a sub project of my project and preservedgitInformation.

The corresponding command is:

git subtree add --prefix=redux https://github.com/reduxjs/redux.git 4.x

3. Debugging Redux source code preparation work

Before, I answered a question in Zhihu: what if the front-end within a year can’t understand the source code of the front-end framework?
Recommended some materials, reading volume is good, you can have a look if you are interested. There are four main points

1. With the help of debugging

2. Search for related highly praised articles

3. Record what you don’t understand and refer to relevant documents

4. Summary

It’s very important to see the source code debugging, so every source article of mine describes in detail how to debug the source code.

Breakpoint debugging Essentials:

The assignment statement can be pressed one step at a timeF10Skip and look at the return value. See the details later.

Function execution requires the breakpoint to pressF11As you follow, you can also use comments and context to deduce what the function does.

Some do not need to look closely, directly pressF8Go to the next breakpoint

Refresh and debug by pressingF5

Before debugging the source code, take a simple lookreduxI have a general impression of the workflow.

Learn the overall architecture of Redux source code, and deeply understand the principles of Redux and its middleware

3.1 rollup generates sourcemap for easy debugging

modifyrollup.config.jsDocuments,outputConfiguration generation of outputsourcemap

// redux/ rollup.config.js  Some omissions
const sourcemap = {
  sourcemap: true,
};

output: {
    // ...
    ...sourcemap,
}

Installation dependency

git clone http://github.com/lxchuan12/redux-analysis.git
cd redux-analysi/redux
npm i
npm run build
#After the compilation, the source map. Map format file will be generated to dist, ES, lib directory.

Take a closer lookredux/examplesContents andredux/README

At this point, I create a new folder under the root pathexamplesThe originaljsWrite counterredux/examples/counter-vanilla/index.html, copy toexamples/index.html。 At the same time, the package containssourcemapOfredux/distDirectory, copying toexamples/distcatalog.

modifyindex.htmlOfscriptOfredux.jsThe document isPath in dist

In order to distinguish and debug the follow-uphtmlFile, I putindex.htmlRename toindex.1.redux.getState.dispatch.html

#Redux analysis root
#Install NPM package for startup service
npm i -g http-server
cd examples
hs -p 5000

Can be happy debugging. You can clone my project directlygit clone http://github.com/lxchuan12/redux-analysis.git。 Local debugging, hands-on practice, easy to digest and absorb.

4. Learn Redux source code by debugging counter examples

Then let’s take a lookexamples/index.1.redux.getState.dispatch.htmlDocuments. Look firsthtmlpart. Just a fewbutton, which is relatively simple.

<div>
    <p>
    Clicked: <span id="value">0</span> times
    <button id="increment">+</button>
    <button id="decrement">-</button>
    <button id="incrementIfOdd">Increment if odd</button>
    <button id="incrementAsync">Increment async</button>
    </p>
</div>

JS partIt is also relatively simple. A statement was madecounterFunction, passed toRedux.createStore(counter)And get the resultstore, andstoreIt’s an object.renderMethod to render the number to the page. usestore.subscribe(render)Subscribedrendermethod. alsostore.dispatch({type: 'INCREMENT' })Method, callingstore.dispatchWill triggerrendermethod. This implements a counter.

function counter(state, action) {
    if (typeof state === 'undefined') {
        return 0
    }

    switch (action.type) {
        case 'INCREMENT':
        return state + 1
        case 'DECREMENT':
        return state - 1
        default:
        return state
    }
}

var store = Redux.createStore(counter)
var valueEl = document.getElementById('value')

function render() {
    valueEl.innerHTML = store.getState().toString()
}
render()
store.subscribe(render)

document.getElementById('increment')
.addEventListener('click', function () {
    store.dispatch({ type: 'INCREMENT' })
})

//Omitting some temporary invalid code

Thinking: after reading this code, where would you break in to debug it.

//You can see the breakpoints everywhere
// 1.
var store = Redux.createStore(counter)
// 2.
function render() {
valueEl.innerHTML = store.getState().toString()
}
render()
// 3.
store.subscribe(render)
// 4.
store.dispatch({ type: 'INCREMENT' })

Learn the overall architecture of Redux source code, and deeply understand the principles of Redux and its middleware

The right side of the pictureScopeSometimes, when you need to pay attention to it, it will display variables such as closure, global environment, current environment, etc., as well as the specific code location such as function, which can help you understand the code.

Breakpoint debugging, pressF5After refreshing the page, pressF8, put the mouse on theReduxandstoreGo ahead.

You can seeReduxThere are several ways. namely:

  • __DO_NOT_USE__ActionTypes: {INIT: “@@redux/INITu.v.d.u.6.r”, REPLACE: “@@redux/REPLACEg.u.u.7.c”, PROBE_UNKNOWN_ACTION: ƒ}
  • Applymiddleware: the ƒ applymiddleware() function is an enhancer that combines multiple middleware and finally enhances thestore.dispatchFunction,dispatchAll middleware can be concatenated.
  • Bindactioncreators: ƒ bindactioncreators (action creators, dispatch) generates actions, which are mainly used in other libraries, such asreact-redux
  • Combine reducers: combine multiplereducers, return a totalreducerFunction.
  • Compose: ƒ compose() combines multiple functions from right to left, for example: compose (F, G, H) and finally get this result (… Args) = > F (g (H (… Args)))
  • Create store: ƒ create store (reducer, preloaded state, enhancer) generationstoreobject

Look againstoreThere are also several ways. namely:

  • Dispatch: dispatch (action) to dispatch an action, that is to saysubscribeThe collected functions are executed in turn
  • Subscribe: ƒ subscribe (listener) the subscription collection function exists in the array and is waiting to be triggereddispatchIn turn. Returns a unsubscribe function to unsubscribe from listening.
  • Getstate: ƒ getstate() get existencecreateStoreThe object of the inner closure of a function.
  • Replacereducer: ƒ replacereducer (nextreducer) is mainly used forreduxDeveloper tools to compare the similarities and differences between the current and the last operation. It’s sort of like time travel.
  • Symbol(observable): ƒ observable()

Official documents redux.org.js On theAPI

I’m not going to go into each one for the time beingAPIImplementation of. Press againF5Refresh page, breakpoint tovar store = Redux.createStore(counter)。 Keep pressingF11Go through the main process first.

4.1 Redux.createSotre

createStoreThe function structure is like this, does it look very simple, and finally returns the objectstore, containingdispatchsubscribegetStatereplaceReducerAnd so on.

//Several codes are omitted
export default function createStore(reducer, preloadedState, enhancer) {
    //Omit parameter check and replace
    //Current reducer function
    let currentReducer = reducer
    //Current state
    let currentState = preloadedState
    //Current listening array function
    let currentListeners = []
    //Next listen array function
    let nextListeners = currentListeners
    //Is it in dispatch
    let isDispatching = false
    function ensureCanMutateNextListeners() {
        if (nextListeners === currentListeners) {
        nextListeners = currentListeners.slice()
        }
    }
    function getState() {
        return currentState
    }
    function subscribe(listener) {}
    function dispatch(action) {}
    function replaceReducer(nextReducer) {}
    function observable() {}
    // ActionTypes.INIT @@redux/INITu.v.d.u.6.r
    dispatch({ type: ActionTypes.INIT })
    return {
        dispatch,
        subscribe,
        getState,
        replaceReducer,
        [$$observable]: observable
    }
}

4.2 store.dispatch(action)

function dispatch(action) {
    //Judge whether the action is an object. If not, an error is reported
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }
    //Judgment action.type  Whether it exists or not; if not, an error will be reported
    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
          'Have you misspelled a constant?'
      )
    }
    //If it is not, it will report an error
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
        //Set to false after calling
      isDispatching = false
    }
    //Take out the collected functions and call them in turn
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }
    //Finally return to action
    return action
  }
var store = Redux.createStore(counter)

This sentence has been debugged above.

Press againF11Debugging.

function render() {
    valueEl.innerHTML = store.getState().toString()
}
render()

4.3 store.getState()

getStateFunction implementation is relatively simple.

function getState() {
    //An error is reported if it is in the process of dispatching
    if (isDispatching) {
        throw new Error(
        'You may not call store.getState() while the reducer is executing. ' +
            'The reducer has already received the state as an argument. ' +
            'Pass it down from the top reducer instead of reading it from the store.'
        )
    }
    //Returns the current state
    return currentState
}

4.4 store.subscribe(listener)

The subscription listener function is stored in the array,store.dispatch(action)Is executed.

function subscribe(listener) {
    //Subscription parameter verification is not a function error
    if (typeof listener !== 'function') {
      throw new Error('Expected the listener to be a function.')
    }
    //Dispatching, error reported
    if (isDispatching) {
      throw new Error(
        'You may not call store.subscribe() while the reducer is executing. ' +
          'If you would like to be notified after the store has been updated, subscribe from a ' +
          'component and invoke store.getState() in the callback to access the latest state. ' +
          'See https://redux.js.org/api-reference/store#subscribelistener for more details.'
      )
    }
    //Subscription is true
    let isSubscribed = true

    ensureCanMutateNextListeners()
    nextListeners.push(listener)

    //Returns a unsubscribe function
    return function unsubscribe() {
      if (!isSubscribed) {
        return
      }
      //In the process of dispatching, an error is reported
      if (isDispatching) {
        throw new Error(
          'You may not unsubscribe from a store listener while the reducer is executing. ' +
            'See https://redux.js.org/api-reference/store#subscribelistener for more details.'
        )
      }
      //Subscription is false
      isSubscribed = false

      ensureCanMutateNextListeners()
    //Find the current listening function
      const index = nextListeners.indexOf(listener)
    //Delete from array
      nextListeners.splice(index, 1)
      currentListeners = null
    }
  }

So far, we have finished debugging and learningRedux.createSotrestore.dispatchstore.getStatestore.subscribeSource code.

Next, we write a middleware example to debug the relevant source code of middleware.

5. Redux middleware related source code

Middleware is the focus, and interviewers often ask such questions.

5.1 Redux.applyMiddleware(…middlewares)

5.1.1 prepare for logger example debugging

For debuggingRedux.applyMiddleware(...middlewares)I’m hereexamples/js/middlewares.logger.example.jsWrite a simpleloggerexample. There are three in eachlogger1logger2logger3Function. Because they are similar, I only show them herelogger1Function.

// examples/js/middlewares.logger.example.js
function logger1({ getState }) {
  return next => action => {
      console.log('will dispatch--1--next, action:', next, action)

      // Call the next dispatch method in the middleware chain.
      const returnValue = next(action)

      console.log('state after dispatch--1', getState())

      // This will likely be the action itself, unless
      // a middleware further in chain changed it.
      return returnValue
  }
}
//Omit logger2 and logger3

loggerMiddleware functions do relatively simple things. They return two-level functions,nextIt is the next middleware function that returns the result. In order to make readers understand, I putlogger1With the arrow functionlogger2We use ordinary functions.

After writing the exampleLet’s go on to see how to debugRedux.applyMiddleware(...middlewares))Source code.

cd redux-analysis && hs -p 5000
#As mentioned above, NPMI - G HTTP server

openhttp://localhost:5000/examples/index.2.redux.applyMiddleware.compose.html, pressF12Open the console,

First click the plus sign operation + 1 to display the results.

Learn the overall architecture of Redux source code, and deeply understand the principles of Redux and its middleware

As can be seen from the figure,nextIs the next function. First 1-2-3, then 3-2-1.

This is what we often call middleware, aspect oriented programming (AOP).

Learn the overall architecture of Redux source code, and deeply understand the principles of Redux and its middleware

Next debug, break the following statements and places that you think are important.

// examples/index.2.redux.applyMiddleware.compose.html
var store = Redux.createStore(counter, Redux.applyMiddleware(logger1, logger2,  logger3))

5.1.2 Redux.applyMiddleware (… Middleware) source code

// redux/src/applyMiddleware.js
/**
 * ...
 * @param {...Function} middlewares The middleware chain to be applied.
 * @returns {Function} A store enhancer applying the middleware.
 */
export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args)
    let dispatch = () => {
      throw new Error(
        'Dispatching while constructing your middleware is not allowed. ' +
          'Other middleware would not be applied to this dispatch.'
      )
    }

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}
// redux/src/createStore.js
export default function createStore(reducer, preloadedState, enhancer) {
  //Omit parameter check
  //If the second parameter 'preloadedstate' is a function and the third parameter 'enhancer' is undefined, swap them.
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    //Enhancer is also called enhancer` Redux.applyMiddleware `Function returned
    //The args of the createstore are 'reducer, preloadedstate'`
    /**
     * createStore => (...args) => {
            const store = createStore(...args)
            return {
              ...store,
               dispatch,
            }
        }
     ** /
    //Finally, the enhanced store object is returned.
    return enhancer(createStore)(reducer, preloadedState)
  }
  //Omit subsequent code
}

Transfer the received middleware functionlogger1, logger2, logger3Put it inmiddlewaresArray.Redux.applyMiddlewareFinally, two-level functions are returned.
The middleware functions are mixed with parametersgetStateanddispatch

// examples/index.2.redux.applyMiddleware.compose.html
var store = Redux.createStore(counter, Redux.applyMiddleware(logger1, logger2,  logger3))

Finally, this sentence actually returns an enhancementdispatchOfstoreObject.

And enhanceddispatchFunction is usedRedux.compose(...functions)To perform in series.

5.2 Redux.compose(…functions)

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
// applyMiddleware.js
dispatch = compose(...chain)(store.dispatch)
// compose
funcs.reduce((a, b) => (...args) => a(b(...args)))

These two sentences may not be easy to understand. You can debug breakpoints several times. I converted the arrow function into a normal function.

funcs.reduce(function(a, b){
  return function(...args){
    return a(b(...args));
  };
});

actuallyreduxThe annotation in the source code is very clearcomposeThere is a bunch of comments above the function, including a sentence: combine multiple functions, from right to left, for example:compose(f, g, h)Finally, we get this result(...args) => f(g(h(...args))).

5.2.1 evolution of compose function

seeRedux.compose(...functions)Function source code, or do not understand, do not panic, eat eggs and soup. To take a closer look at how it evolved, let’s take a brief look at the following requirements.

Pass in a number, multiply the calculated value by 10, add 10, and subtract 2.

It’s easy to implement.

const calc = (num) => num * 10 + 10 - 2;
calc(10); // 108

But there is a problem in this way. It is not easy to expand. For example, I want to multiply by10Print out the results.
For the convenience of extension, we write three functions separately.

const multiply = (x) => {
   const result = x * 10;
   console.log(result);
   return result;
};
const add = (y) => y + 10;
const minus = (z) => z - 2;

//Calculation results
console.log(minus(add(multiply(10))));
// 100
// 108
//So we get the results of the three functions.

Then we implement a relatively general function and calculate the results of these three functions.

const compose = (f, g, h) => {
  return function(x){
    return f(g(h(x)));
  }
}
const calc = compose(minus, add, multiply);
console.log(calc(10));
// 100
// 108

This is still a problem. Only three functions are supported. I want to support multiple functions.
We’ve learned about arraysreduceMethod can achieve such a function.
Previous function

//We often use reduce to calculate the sum of numerical arrays
[1,2,3,4,5].reduce((pre, item, index, arr) => {
  console.log('(pre, item, index, arr)', pre, item, index, arr);
  // (pre, item, index, arr) 1 2 1 (5) [1, 2, 3, 4, 5]
  // (pre, item, index, arr) 3 3 2 (5) [1, 2, 3, 4, 5]
  // (pre, item, index, arr) 6 4 3 (5) [1, 2, 3, 4, 5]
  // (pre, item, index, arr) 10 5 4 (5) [1, 2, 3, 4, 5]
  return pre + item;
});
// 15

preIs the last return value, in this case a numeric value1,3,6,10。 In the next example, it’s an anonymous function.

function(x){
  return a(b(x));
}

itemyes2,3,4,5In the next example, it isminus、add、multiply

const compose = (...funcs) => {
  return funcs.reduce((a, b) => {
    return function(x){
      return a(b(x));
    }
  })
}
const calc = compose(minus, add, multiply);
console.log(calc(10));
// 100
// 108

andRedux.compose(...functions)In fact, it’s just that middleware returns double-layer functions.

So the return isNext functionThey are executed in series, forming the onion model of middleware.
People say that a picture is worth a thousand words. I drew a relatively simple onereduxSchematic diagram of middleware.

Learn the overall architecture of Redux source code, and deeply understand the principles of Redux and its middleware

If you don’t quite understand, it is recommended to follow my example and debug more.

cd redux-analysis && hs -p 5000
#As mentioned above, NPMI - G HTTP server

openhttp://localhost:5000/examples/index.3.html, pressF12Open the console for debugging.

5.2.2 implementation of compose function in front end framework

lodashSource codecomposeThe implementation of the function is also similar to the arrayreduce, it’s just implemented internallyarrayReduce

Self reference article: learn the overall framework of lodash source code

//Lodash source code
function baseWrapperValue(value, actions) {
    var result = value;
    //If it is an instance of lazywrapper, call LazyWrapper.prototype.value  Method, also known as lazyvalue method
    if (result instanceof LazyWrapper) {
        result = result.value();
    }
    //Similar to []. Reduce(), the return result of the previous function is passed to the next function as a parameter
    return arrayReduce(actions, function(result, action) {
        return action.func.apply(action.thisArg, arrayPush([result], action.args));
    }, result);
}

koa-composeThe source code is also availablecomposeFunction implementation. The implementation is cyclic additionpromise
Because the code is relatively long, I have omitted it. For details, see the link Ruochuan: learn the overall framework of KOA source code, and analyze the principle of KOA onion model and co principleKoa compose source code(onion model implementation)

6. Redux.combineReducers(reducers)

openhttp://localhost:5000/examples/index.4.html, pressF12Open the console and follow the example given to debug the followingRedux.combineReducers(reducers)andRedux.bindActionCreators(actionCreators, dispatch)Specific implementation. Because the article has been very long, these two functions are not explained in detail.

combineReducersFunction is simply a combination of multiplereducerIs a functioncombination

export default function combineReducers(reducers) {
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]

    //Omit some development environment judgment code

    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }

  //After some processing, we get the final reducerkeys
  const finalReducerKeys = Object.keys(finalReducers)

  //Omit some development environment judgment code

  return function combination(state = {}, action) {
    //... omit some judgments about the development environment

   //Whether the state has been modified before and after recording with haschanged variable
    let hasChanged = false
    //Declare the object to store the next state
    const nextState = {}
    //Traversing finalreducerkeys
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      const previousStateForKey = state[key]
      //Execute reducer
      const nextStateForKey = reducer(previousStateForKey, action)

      //Omit fault tolerant code

      nextState[key] = nextStateForKey
      //If the two key comparisons are not equal, they will change
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    //If the comparison of the last keys array is not equal, it will change
    hasChanged =
      hasChanged || finalReducerKeys.length !== Object.keys(state).length
    return hasChanged ? nextState : state
  }
}

7. Redux.bindActionCreators(actionCreators, dispatch)

If the first argument is a function, a function is returned directly. If it is an object, it traverses the assignment and finally generatesboundActionCreatorsObject.

function bindActionCreator(actionCreator, dispatch) {
  return function() {
    return dispatch(actionCreator.apply(this, arguments))
  }
}

export default function bindActionCreators(actionCreators, dispatch) {
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }

  //... omit some fault-tolerant judgments

  const boundActionCreators = {}
  for (const key in actionCreators) {
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}

reduxProvidedAPIexceptstore.replaceReducer(nextReducer)No analysis. Everything else is analyzed.

8. Simple comparison between vuex and Redux

8.1 source code implementation form

In terms of source code implementation,vuexThe source code mainly uses the constructor, andreduxIt is multi-functional programming, closure.

8.2 coupling degree

vuexAndvueStrong coupling, detachedvueCannot be used. andreduxFollowreactIt doesn’t matter, so it can be used with applets orjQueryEtc. If necessary andreactUse, also need to combinereact-reduxLibrary.

8.3 extension

//The logger plug-in is omitted
function logger (store) {
  console.log('store', store);
}
//Pass in as an array
new Vuex.Store({
  state,
  getters,
  actions,
  mutations,
  plugins: process.env.NODE_ENV !== 'production'
    ? [logger]
    : []
})
//Implementation part of vuex source code plug-in
class Store{
  constructor(){
    //Pass the vuex instance object store to the plug-in for use
    plugins.forEach(plugin => plugin(this))
  }
}

vuexThe extension is implemented in the form of plug-ins, andreduxIt’s in the form of middleware.reduxAOP (aspect oriented programming) is the middleware,reduxinRedux.applyMiddleware()In fact, it is also an enhancement function, so the user can implement the enhancerreduxThe ecology is relatively prosperous.

8.4 ease of use

Relatively speaking,vuexIt’s relatively simple to start with,reduxIt’s relatively difficult,reduxIt involves some concepts of functional programming, higher-order function, pure function, etc.

9. Summary

This article mainly through the way of debugging step by stepreduxThe specific implementation of the source code. It aims to teach readers to debug the source code, not afraid of the source code.

Interviewers often like to write onereduxMiddlewarereduxThe principle of middleware.

function logger1({ getState }) {
  return next => action => {
      const returnValue = next(action)
      return returnValue
  }
}
const compose = (...funcs) => {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  //Arrow function
  // return funcs.reduce((a, b) => (...args) => a(b(...args)))
  return funcs.reduce((a, b) => {
    return function(x){
      return a(b(x));
    }
  })
}
const enhancerStore = Redux.create(reducer, Redux.applyMiddleware(logger1, ...))
enhancerStore.dispatch(action)

User triggeredenhancerStore.dispatch(action)It is enhanced. In fact, it is the first middleware function in the middlenextIs the next middleware function, and finallynextThere is no enhancementstore.dispatch(action)

Finally, let’s look at ZhangreduxWork flow chart
Learn the overall architecture of Redux source code, and deeply understand the principles of Redux and its middleware

Is it more understandable.

If readers find that there is something wrong or can be improved, or where it is not clear, please comment on it. In addition, I think it’s good. It’s helpful for you. I can like it, comment on it, forward it and share it. It’s also a kind of support for me. Thank you very much.If someone talks about how to read the source code, you can recommend my source code series articles, that’s great

Recommended reading

@Hu Daha: hands on implementation of Redux (1): modify the sharing state gracefully, with a total of 6 sections, which is highly recommended. Although I finished reading the react book a long time ago, I can get harvest by reading it again

Meituan @ Yingying Redux from the design to the source code, meituan this article is after I basically finished writing the article, I feel that it is well written, very recommended

Redux Chinese document

Redux English document

Ruochuan’s learning Redux source code warehouse

Another series

Interviewer: JS inheritance

Interviewer asked: JS this point

Interviewer: can we simulate the call and apply methods of JS

Interviewer asked: can we simulate the bind method of JS

Interviewer asked: can we simulate the implementation of JS new operator

about

Author: Chang YiRuochuanIn the name of wandering in the lake. Ppt enthusiasts know little about the front-end road and are only good at learning.

If Chuan’s blog, usevuepressReconstructed, the reading experience may be better

Nuggets column, welcome to pay attention~

segmentfaultFront end view column, welcome to pay attention~

Zhihu front view column, welcome to pay attention~

Front view column of language bird, new column of language bird, welcome to pay attention~

GitHub blog, relevant source code and resources are put here, find astar^_^~

Welcome to WeChat exchange WeChat official account.

Perhaps the more interesting WeChat official account is long. Welcome to add me wechatruochuan12(indicate the source, basic visitors will not refuse), pull you into the front-end vision exchange group, long-term exchange and learning~

Learn the overall architecture of Redux source code, and deeply understand the principles of Redux and its middleware