本篇是学习redux
源码的一些记录,学习的redux
版本是^4.0.1
。redux
在页面开发时,须要管理不少状态(state),好比服务器响应,缓存数据,UI状态等等···当页面的庞大时,状态就会变的混乱。redux就派上用场了,它最大的特色就是使状态变化变的可预测。数组
redux提供一个管理state的仓库(store
),而且规定了store
只能经过reducer
(函数)来更新,而reducer
必须经过dispatch(action)
来触发,action
就是普通的JavaScript对象,它约定了执行的类型而且传递数据。使得state的变化是能够预测的,一样的步骤会获得一样的state。缓存
从第一步建立仓库开始看起 createStore(reducer, preloadedState, enhancer)
。服务器
开始已经提到redux是管理一个store,那么第一步就是建立store,通常最简单的就是如下形式:闭包
let store = createStore(reducer,preloadedState,enhancer);
看一下createStore 源码中的关键部分:app
// 记录reducer函数、初始状态、监听函数 let currentReducer = reducer let currentState = preloadedState let currentListeners = [] // 建立store时,触发一个空的action,这样若是没有初始状态,就会返回reducer中的默认状态 dispatch({ type: ActionTypes.INIT }) // 返回一个提供了多种方法的对象 return { dispatch, //触发action的方法 subscribe, //增长监听方法 getState, //获取当前状态的方法 replaceReducer, //更换reducer方法 [$$observable]: observable }
这里是用了闭包,在createStore的做用域中建立了currentState
变量来记录状态,currentReducer
来记录reducer函数,currentListeners
来记录全部的监听函数。而后返回一个对象,对象中的方法能够获取currentState、触发reducer来更新currentState,添加监听函数,替换reducer等。函数
这个对象就是 store , 而state,reducer,listeners保存在createStore的做用域中,只有经过store中的方法能够访问到。学习
只有store.getState()
能获取到仓库的state --> currentState变量测试
function getState() { if (isDispatching) { //··· } return currentState }
只有store.dispatch(action)
能够触发更新state。注意在redux中action必须是一个纯对象,并且必须有type字段指定动做类型,dispatch中有对与这些的校验。spa
function dispatch(action) { /*-- action必须是对象 --*/ if (!isPlainObject(action)) { //··· } /*-- action必须有type字段--*/ if (typeof action.type === 'undefined') { //··· } if (isDispatching) { //··· } /*-- 触发reducer,更新state --*/ try { isDispatching = true currentState = currentReducer(currentState, action) } finally { isDispatching = false } /*-- 执行监听函数 --*/ const listeners = (currentListeners = nextListeners) for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } return action }
从这一段currentState = currentReducer(currentState, action)
看出currentState是经过执行reducer函数更新的。
并且也知道了reducer函数的参数状况:
若是想在state变化时作点什么,就须要用到subscribe
方法添加监听函数
只看关键代码,其实就是维护了一个保存监听函数的数组。从上面dispatch的代码listener()
能够看出,这些函数会在dispatch(action)
的时候触发。
并且每次新增listener的时候都会返回一个取消监听的方法unsubscribe
,能够在适当的时候取消监听。
function subscribe(listener) { /*-- 增长监听 --*/ nextListeners.push(listener) /*-- 返回一个取消监听的函数 --*/ return function unsubscribe() { const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) } }
能够更改reducer函数,很简单,从新赋值。
function replaceReducer(nextReducer) { if (typeof nextReducer !== 'function') { //··· } // 更新reducer函数 currentReducer = nextReducer dispatch({ type: ActionTypes.REPLACE }) }
上面对createStore的实现有了大体的了解,再看看栗子
action中须要type
字段来标记要执行的字段,会定义为字符串常量;通常会使用单独的模块来管理。
/*-- 动做类型 actionTypes.js --*/ export const ADD_TODO = 'ADD_LIST'; export const COMPLETE_TODO = 'COMPLETE_TODO';
action 必须是一个纯JavaScript对象。能够经过建立action的函数返回,这样就方便传递数据。
/*-- action对象 action.js --*/ import { ADD_TODO, COMPLETE_TODO} from "./actionTypes"; export function addList (text) { // 返回action对象 return { type: ADD_TODO, text } } export function completeList (id,bl = true) { return { type: COMPLETE_TODO, id, bl } }
reducer 是一个纯函数,接受旧的state和action,返回新的state。只要传入参数相同,返回计算获得的下一个 state 就必定相同。没有特殊状况、没有反作用,没有 API 请求、没有变量修改,单纯执行计算。
/*-- reducer函数 todolist.js --*/ import {ADD_TODO, COMPLETE_TODO} from '../actionTypes'; let id = 0; export default function todoList(state = [],action) { // 根据type区分如何更新state switch (action.type) { case ADD_TODO : // 不要直接修改state,要返回一个新的state return [...state,{ id: id++, text: action.text, complete:false }] case COMPLETE_TODO : return state.map( item => { if (item.id === action.id){ return Object.assign({},item,{complete:action.bl}) } return item; }) default: return state; } }
使用 createStore 建立 store
/*-- 建立store --*/ import {createStore} from 'redux'; import reducer from './reducer/todolist'; let store = createStore(reducer);
更新 state
import {addList,completeList} from './store/actions'; store.dispatch(addList('测试数据'));
addList('测试数据')
返回的是 action {type: ADD_TODO,text:'测试数据'}
。 在todoList函数中,执行完后返回新的state [{id:0,text:'测试数据',complete:false}]
。
获取 state
console.log(store.getState()); // [{id:0,text:'测试数据',complete:false}]
添加/删除 监听函数
// 添加 unsubscribe = store.subscribe(function(){ // 每次更新state都会执行此函数 console.log(store.getState()); }); // 取消 unsubscribe()