写在前面:
新手学习,不喜轻喷。
1.个人初解
redux将整个应用的state存储树级机构的object中,树级结构的顶点为唯一Store,在应用的整个生命周期内,只会存在一个Store。
store中存放state,要修改store中的state必须通过action来触发,使用reducer里面的函数对state修改,个人觉得有点类似于函数式编程。
2.数据流向
2.1.redux是严格的单向数据流
即:view触发action,reducer接收action修改store中的state,view根据判断,重新渲染。
2.2.同一个action得到的新的state数据值一定是相等的
reducer是纯函数,只是用来计算下一个state,函数结果是可以预测的,相同的输入,相同的输出,不应该有副作用。
3.redux在运行过程中到底做了什么事情
3.1. redux/src/index.js redux的入口文件
先看一段redux入口源码
import createStore from './createStore'
import combineReducers from './combineReducers'
import bindActionCreators from './bindActionCreators'
import applyMiddleware from './applyMiddleware'
import compose from './compose'
import warning from './utils/warning'
import __DO_NOT_USE__ActionTypes from './utils/actionTypes' //redux自带的action类型
/*
* This is a dummy function to check if the function name has been altered by minification.
* If the function has been minified and NODE_ENV !== 'production', warn the user.
* 判断在非生产环境下,redux代码是否被压缩,使用function.name来进行判断,无实际意义和方法体
*/
function isCrushed() {}
if (
process.env.NODE_ENV !== 'production' && //当前环境不是生产环境
typeof isCrushed.name === 'string' && //方法名称是String类型,IE不支持Function.name
isCrushed.name !== 'isCrushed' //当代码被编译压缩之后,方法名称会改变,该判断标识当前没有被编译和压缩
) {
warning(
'您当前正在使用node_env==“production”之外的缩小代码。 ' +
'这意味着您正在运行一个较慢的redux开发版本。' +
'你可以使用loose envify(https://github.com/zertosh/loose-envify)来浏览 ' +
'或在webpack中设置生产模式(https://webpack.js.org/concepts/mode/)' +
'以确保生产版本的代码正确。'
)
}
export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose,
__DO_NOT_USE__ActionTypes //语义:不要使用ActionTypes
}
该文件主要导出变量和检查当前环境下是否有压缩代码,redux压缩之后,运行性能会有所降低。
关于代码压缩简单理解:
未压缩
function isCrushed() {}
if (
process.env.NODE_ENV !== 'production' &&
typeof isCrushed.name === 'string' &&
isCrushed.name !== 'isCrushed'
)
压缩之后,方法名称会改变,所以等式成立
function d(){}"string"==typeof d.name&&"isCrushed"!==d.name
3.2.redux/src/createStore
createStore(reducer:any,preloadedState?:any,enhancer?:middleware),最终返回一个实例。可以进行, 监听和 派发。
看一段代码:
/**
*创建保存状态树的redux存储。
*更改存储区中数据的唯一方法是对其调用“dispatch()”。
*
*你的应用中应该只有一个store。指定不同的
*部分状态树响应操作,可以组合多个还原器
*通过使用“combinereducers”将其转换为单个reducer函数。
*
*@param{function}reducer返回下一个状态树的函数,给定
*当前状态树和要处理的操作。
*
*@param{any}[preloadedstate]初始状态。您可以选择指定它
*在通用应用程序中对服务器的状态进行优化,或恢复
*以前序列化的用户会话。
*如果使用'combinereducers'生成根还原函数,则必须
*与“combinereducers”键形状相同的对象。
*
*@param{function}[enhancer]存储增强器。您可以选择指定它
*为了增强store的第三方功能,如中间件,
*时间旅行、坚持等。redux附带的唯一存储增强功能
*是“applymiddleware()”。
*
*@return{store}一个redux存储,用于读取状态、分派操作
*并订阅更改。
*/
export default function createStore(reducer, preloadedState, enhancer) {
if (
(typeof preloadedState === 'function' && typeof enhancer === 'function') ||
(typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
throw new Error(
'It looks like you are passing several store enhancers to ' +
'createStore(). This is not supported. Instead, compose them ' +
'together to a single function.'
)
}
//检查增强器
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.')
}
//返回增强后的store
return enhancer(createStore)(reducer, preloadedState)
}
//reducer也只能是函数
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
}
enhancer?:middle,增强器,若干个中间件可以通过applymiddleware产生一个增强器,增强器通过Compose函数合并成一个增强器。
applymiddleware代码如下:
export default function applyMiddleware(...middlewares) {
// 接受若干个中间件参数
// 返回一个enhancer增强器函数,enhancer的参数是一个createStore函数。等待被enhancer(createStore)
return createStore => (...args) => {
// 先创建store或者更新Store
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.'
)
}
//暂时存储更新前的Store
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
//遍历中间件,将更新前的Store传入,获取更新后的tore数组
const chain = middlewares.map(middleware => middleware(middlewareAPI))
//组合中间件,将dispatch传入,即每个中间件都有一个增强后的dispatch
dispatch = compose(...chain)(store.dispatch)
//返回一个store和dispatch,即:返回实现了中间件的store增强器
return {
...store,
dispatch
}
}
}
调用逻辑:
1.通过createStore方法创建出一个store
2.定一个dispatch,如果在中间件构造过程中调用,抛出错误提示
3.定义middlewareAPI,有两个方法,一个是getState,另一个是dispatch,将其作为中间件调用的store的桥接
4.middlewares调用Array.prototype.map进行改造,存放在chain
5.用compose整合chain数组,并赋值给dispatch
6.将新的dispatch替换原先的store.dispatch
compose代码如下:
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)))
}
compose接收>0个的增强器,如果没有增强器,就返回一个空函数,如果有一个函数,返回函数本身,如果是多个,才会产生合并的过程即compose的过程,最后一行,通过迭代器生成组合迭代函数。
看到这里,很懵逼有没有?有没有?
redux-thunk、redux-saga都是redux常用的中间件,一般配合applyMiddleware使用,applyMiddleware的作用就是将这些enhancer格式化成redux想要的enhancer,以redux-thunk举个栗子
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
根据源码来看,我们如果单独使用thunk,应该如下:
thunk = ({ dispatch, getState })=>{
return next => action => {
if (typeof action === 'function') {
return action(dispatch, getState);
}
return next(action);
};
}
applyMiddleware处理之后,应该是china数组如下:
const newDispatch;
const middlewareAPI={
getState:store.getState,
dispatch: (...args) => newDispatch(...args)
}
const { dispatch, getState } = middlewareAPI;
const fun1 = (next)=>{
return action => {
if (typeof action === 'function') {
return action(dispatch, getState);
}
return next(action);
}
}
const chain = [fun1]
模拟整个过程:
function makeASandwichWithSecretSauce(forPerson) {
return function (dispatch) {
return fetchSecretSauce().then(
sauce => dispatch(makeASandwich(forPerson, sauce)),
error => dispatch(apologize('The Sandwich Shop', forPerson, error))
);
};
}
// store.dispatch就等价于newDispatch
store.dispatch(makeASandwichWithSecretSauce('Me'))
====> 转换
const forPerson = 'Me';
const action = (dispatch)=>{
return fetchSecretSauce().then(
sauce => dispatch(makeASandwich(forPerson, sauce)),
error => dispatch(apologize('The Sandwich Shop', forPerson, error))
);
}
newDispatch()
===> typeof action === 'function' 成立时
((dispatch)=>{
return fetchSecretSauce().then(
sauce => dispatch(makeASandwich(forPerson, sauce)),
error => dispatch(apologize('The Sandwich Shop', forPerson, error))
);
})( (...args) => newDispatch(...args), getState)
====> 计算运行结果
const forPerson = 'Me';
const dispatch = (...args) => newDispatch(...args) ;
fetchSecretSauce().then(
sauce => dispatch(makeASandwich(forPerson, sauce)),
error => dispatch(apologize('The Sandwich Shop', forPerson, error))
);
// 其中:
function fetchSecretSauce() {
return fetch('https://www.google.com/search?q=secret+sauce');
}
function makeASandwich(forPerson, secretSauce) {
return {
type: 'MAKE_SANDWICH',
forPerson,
secretSauce
};
}
function apologize(fromPerson, toPerson, error) {
return {
type: 'APOLOGIZE',
fromPerson,
toPerson,
error
};
}
====> 我们这里只计算Promise.resolve的结果,并且假设fetchSecretSauce返回值为'666',即sauce='666'
const forPerson = 'Me';
const dispatch = (...args) => newDispatch(...args) ;
dispatch({
type: 'MAKE_SANDWICH',
'Me',
'666'
})
====> 为了方便对比,我们再次转换一下
const action = {
type: 'MAKE_SANDWICH',
'Me',
'666'
};
const next = store.dispatch
const newDispatch = action =>{
if (typeof action === 'function') {
return action(dispatch, getState);
}
return next(action);
}
newDispatch(action)
====> 最终结果
store.dispatch({
type: 'MAKE_SANDWICH',
'Me',
'666'
});