Redux源碼解析

最近研究了Redux源碼,作為React技術棧中的一份子,保持了其特有的小巧專一。代碼總共不過百來行,其中包含了狀態機,發佈訂閱模式以及高階函數的應用,也確實值得我們學習借鑑。(以下源碼略作修改和註釋)

1、三大原則


Redux源碼解析


單一數據源

整個應用的state被儲存在一顆object tree中,並且這個object tree只存在於唯一

State是隻讀的

唯一改變state的方法就是觸發action, action是一個已發生事件的普通對象

使用純函數來執行修改

為了描述action如何改變state tree,就需要reducer

源碼目錄


Redux源碼解析


util/isPlainObject

<code>// 判斷是不是純對象,而非構造函數
export default function isPlainObject(obj) {
if(typeof obj != 'object' || obj === null) {
return false;
}
let proto = obj;
// 取到obj最終_proto_指向
while(Object.getPrototypeOf(proto)) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(obj) === proto;
}
複製代碼/<code>

此純對象的判斷,判斷對象最初的_proto_指向和_proto_最終指向的判斷是否為同一個來判斷


Redux源碼解析


util/actionTypes.js

<code>const ActionTypes = {
INIT: '@@redux/INIT'
}
export default ActionTypes;
複製代碼/<code>

createStore.js

作為redux中的C位,其中的方法包括getState, dispatch, subscribe方法

<code>export default function createStore(reducer, preloadedState) {
if(typeof reducer != 'function') {
throw new Error('reducer不是函數')
}
let currentReducer = reducer; // 狀態處理器
let currentState = preloadedState; // 初始狀態
let currentListeners = []; // 監聽函數隊列
function getState() {
return currentState;
}

function dispatch(action) {
if(!isPlainObject(action)) {
throw new Error('action不是純對象')
}
if(typeof action.type == 'undefined') {
throw new Error('action的type未定義')
}
// 獲取當前state
currentState = createReducer(currentState, action);
// 發佈訂閱模式
for(let i=0; i<currentlisteners.length> const listener = currentListeners[i];
listener();
}
return action;
}

// 訂閱

function subscribe(listener) {
let subscribed = true;
currentListeners.push(listener);
// 取消訂閱
return function unsubscribe() {
// 重複訂閱優化
if(!subscribed) {
return ;
}
const index = currentListeners.indexOf(listener);
currentListeners.splice(index, 1);
subscirbed = false;
}
}

// 創建store初始化值
dispatch({type:ActionTypes.INIT});

return {
getState,// 返回狀態
dispatch,// 派發動作
subscirbe
}
}
複製代碼/<currentlisteners.length>/<code>

combineReducers.js

將多個reducer合併到一個對象中,方便維護

<code>export default function(reducers) {
// 返回reducers名稱的數組
const reducerKeys = Object.keys(reducers);
return function (state = {}, action) {
const nextState = {};
for(let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i];
const reducer = reducers[key];
const previousStateForKey = state[key]; // 老狀態
const nextStateForKey = reducer(previousStateForKey, action); // 新狀態
nextState[key] = nextStateForKey;
}
return nextState;
}
}

複製代碼/<code>

bindActionCreators.js

將dispatch整合到action中

<code>function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}
/**
*
* @param {Function|Object} actionCreators
* @param {Function} dispatch 對redux Store生效的dispatch方法
* @returns {Function|Object} 如果返回對象的話類似與原始對象,但是每個對象元素添加了dispatch方法
*/
export default function bindActionCreators(actionCreators, dispatch) {
if(typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
if(typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error(
`bindActionCreators應該是一個對象或者函數類型, 而不是接收到${
actionCreators === null ? 'null' : typeof actionCreators
}. ` +
`是不是這樣引用 "import ActionCreators from" 而不是 "import * as ActionCreators from"?`
)
}

// actionCreators為對象的時候
const boundActionCreators = {}
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
複製代碼/<code>

compose.js

這一塊靈活應用了reduce方法和高階函數,實現參數函數從右向左執行

<code>/**
*
* @param {...any} funcs
* @returns 包含從右向左執行的函數參數。比如,compose(f, g, h) 等同於 (...args) => f(g(h(...args)))
*/
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)))
}
複製代碼/<code>

applyMiddleware.js

<code>import compose from './compose'

export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error('構造中間鍵的時候不能運行dispatch,否則其他中間鍵將接收不到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
}

}
}
複製代碼/<code>

index.js

<code>import createStore from './createStore';
import combineReducers from './combineReducers';
import bindActionCreators from './bindActionCreators';
import applyMiddleware from './applyMiddleware';
export {
createStore,//創建倉庫
combineReducers,//合併reducers
bindActionCreators,//把actionCreator 和 dispatch方法綁定在一起
applyMiddleware
}
複製代碼/<code>

中間鍵實踐

redux-thunk中間鍵實現

<code>function createThunkMiddleware(extraArgument) {
return ({dispatch, getState}) => next => action => {
if(typeof action === 'function') {
return action(dispatch, getState, extraArgument)
} else {
next(action)
}
}
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
複製代碼/<code>

redux-promise

<code>// Promise判斷:有then方法的函數對象
function isPromise(obj) {
return !obj&&(typeof obj == 'object' || typeof obj == 'function') && (typeof obj.then == 'function')
}

export default function({dispatch, getState}) {
return next => action => {
return isPromise(action.payload)? action.payload.then(result => {
dispatch({...action, payload: result})

}).catch((error) => {
dispatch({...action, payload:error, error: true});
return Promise.reject(error)
}) : next(action)
}
}
複製代碼/<code>

React-Redux實現

connect.js

<code>import React, {Component} from 'react';
import { bindActionCreators } from '../redux';
import ReduxContext from './context';

export default function(mapStateToProps, mapDispatchToProps) {
return function(WrappedComponent) {
return class extends Component {
static contextType = ReduxContext;
constructor(props, context) {
super(props);
this.state = mapStateToProps(context.store.getState());
}
componentDidMount() {
this.unsubscribe = this.context.store.subscribe(() => {
this.setState(mapStateToProps(this.context.store.getState()))
})
}
componentWillMount() {
this.unsubscribe()
}
render() {
let actions = {}
if(typeof mapDispatchToProps == 'function') {
actions = mapDispatchToProps(this.context.store.dispatch)
} else {
actions = bindActionCreators(mapDispatchToProps, this.context.store.dispatch);
}

return <wrappedcomponent>
}
}
}
}
複製代碼/<code>

Provider.js

<code>import React, { Component } from 'react';
import ReduxContext from './context';
export default class Provider extends Component {
render() {
return (
<reduxcontext.provider>
{this.props.children}
/<reduxcontext.provider>
)
}
}
複製代碼/<code>

context.js

<code>import React from 'react';
const ReduxContext = React.createContext(null);
export default ReduxContext;
複製代碼/<code>

index.js

<code>import Provider from './Provider';
import connect from './connect';
export {
Provider,
connect
}/<code>


作者:金金金呀
鏈接:https://juejin.im/post/5e9042ad6fb9a03c3f1ea7c0
來源:掘金
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。


分享到:


相關文章: