redux源码实现
自己实现一个redux库,通过redux的使用,我们知道redux内部有个最核心的 api createStore
,createStores
有三个参数:
第一个参数就是 reducer函数. 作用就是更具action的类型对store中的状态进行更改,
第二个参数是 preloadedState. 作用是预存储的store状态
第三个参数是 enhancer. 作用是对store功能进行增强
在调用完createStores
方法之后,这个方法会给我们返回一个对象,这个对象中要有三个方法:
第一个方法是 getState 作用是获取状态
第二个方法是 dispatch 作用是触发action
第三个方法是 subscribe 作用是
实现最核心功能
function createStore (reducer, perloadedState) {
// store 对象中存储的状态, 因为store是持久存储的所以我们需要使用闭包来持久保存状态
var currentState = perloadedState
var currentListeners =[]
// 获取状态
function getState () {
return currentState
}
// 触发action
function dispatch (action){
currentState = reducer(currentState, action)
// 循环数组调用订阅者
for (var i=0; i<currentListeners.length; i++) {
// 获取订阅者
var listener = currentListeners[i]
// 调用订阅者
listener()
}
}
// 订阅状态
function subscribe (listener){
currentListeners.push(listener)
}
return {
getState,
dispatch,
subscribe
}
}
- 使用计数器来使用一下我们实现的这个redux
<body>
<button id="increment">+</button>
<span id="box">0</span>
<button id="decrement">-</button>
<script src="./myRedux.js"></script>
<script>
const initialState = 0
function reducer(state, action) {
switch (action.type) {
case 'increment':
return state + 1
case 'decrement':
return state - 1
default:
return state
}
}
// 创建store
const store = createStore(reducer, initialState)
document.querySelector('#increment').addEventListener('click', () => {
store.dispatch({ type: 'increment' })
})
document.querySelector('#decrement').addEventListener('click', () => {
store.dispatch({ type: 'decrement' })
})
store.subscribe(() => {
document.querySelector('#box').innerHTML = store.getState()
})
</script>
</body>
参数类型约束
- 因为reducer是一个函数,所以我们要约束reducer的类型,如果不是函数的话我们直接抛出错误。
// 约束reducer类型
if (typeof reducer !== 'function') throw new Error('reducer 必须是函数')
- action必须是一个对象必须要有type属性所以如果不是对象没有type属性我们则抛出错误
// 判断action是否为一个对象
if(!isPlainObject(action)) throw new Error('aciton 必须是对象')
if(typeof action.type === 'undefined') throw new Error('aciton必须要有type属性')
// 判断是否是对象 redux源码也是这么判断的
function isPlainObject (obj) {
// 排除基本数据类型和null
if (typeof obj !== 'object' || obj === null) return false
// 区分数组和对象
var proto = obj
while (Object.getPrototypeOf(proto) != null) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(obj) === proto
}
实现第三个参数Enhancer
实现增强功能,这个参数必须是一个函数,这个函数由createStore这个方法的调用者传递进来,我们在createStore这个方法内部去调用Enhancer,在调用的时候我们传递,createStore、reducer、preloadedState,传递进去,这样Enhancer就可以去创建store,在创建完store之后对这个store进行增强
// 判断enhancer传递没有
if (typeof enhancer !== 'undefined') {
// 约束enhancer类型
if (typeof enhancer !== 'function') {
throw new Error('enhancer必须是一个函数')
}
return enhancer(createStore)(reducer, perloadedState)
}
function enhancer(createStore) {
return function (reducer, perloadedState) {
var store = createStore(reducer, perloadedState)
var dispatch = store.dispatch
function _dispatch (action){
if (typeof action === 'function') {
return action(dispatch)
}
dispatch(action)
}
return {
...store,
dispatch: _dispatch
}
}
}
// 创建store
const store = createStore(reducer, initialState, enhancer)
document.querySelector('#increment').addEventListener('click', () => {
// store.dispatch({ type: 'increment' })
store.dispatch(function (dispatch){
setTimeout(() => {
dispatch({ type: 'increment' })
}, 2000)
})
})
applyMiddleware 实现
function thunk (store){
return function (next) {
return function (action) {
next(action)
}
}
}
先看中间件结构是这样的一个函数返回一个函数在返回一个函数,前两个函数其实都是给最里层函数传参用的,最里层函数才是真正要执行中间件逻辑的函数,在applyMiddleware中我们先要调用中间件的第一层函数去传递store,通过compose函数给第二层函数传递参数,在调用第二层函数的时候我们是倒着调用的,只有我们倒着调用的时候我们才能去传递next参数,实际上传递出去的就是第一个中间件函数,直到调用到了最后一个中间件的时候才是执行的真正的dispatch方法
function applyMiddleware (...middlewares) {
return function (createStore){
return function (reducer, perloadedState){
// 创建store
var store = createStore(reducer, perloadedState)
// 阉割版的store
var middlewareAPI = {
getState: store.getState,
dispatch: store.dispatch
}
// 调用中间件的第一层函数 传递阉割版的store对象
var chain = middlewares.map(middleware => middleware(middlewareAPI))
// 通过compose 方法给第二层函数传递参数
var dispatch = compose(...chain)(store.dispatch)
return {
...store,
// 这个时候的dispatch其实是一层中间件的最里层函数
dispatch
}
}
}
}
function compose () {
var funcs = [...arguments]
return function (dispatch){
for(var i = funcs.length-1; i >= 0; i--) {
dispatch = funcs[i](dispatch)
}
return dispatch
}
}
bindActionCreators 实现
bindActionCreators 的作用就是将actioncreator函数转换成能够触发action的函数,当我们调用 bindActionCreators 方法的时候要求我们传递的第一个参数是一个对象这个对象中存储的就是actioncreator函数,第二个参数就是dispatch方法
这个方法实际上就是返回一个这样的函数
function name () {
dispath({ type: 'actionsName'})
}
下面是实现代码
function bindActionCreators (actionCreators, dispatch){
var boundActionCreators = {}
for (var key in actionCreators) {
(function (key){
boundActionCreators[key] = function() {
dispatch(actionCreators[key]())
}
})(key)
}
return boundActionCreators
}
#### combineReducers 实现
combineReducers 允许我们把大的reducer拆分成一个小的reducer,让我们用combineReducers api 把一个一个小的reducer再组合成一个大的reducer
combineReducers的第一个参数是一个对象这个对象当的属性就是我们向store当中存储的那个状态,这个属性对应的值就是处理状态对应的reducer函数
这个函数就是返回一个函数返回的函数再返回合并后的状态
function combineReducers (reducers) {
// 1. 检查reducer类型,他必须是函数
var reducerKeys = Object.keys(reducers)
for (var i=0; i<reducerKeys.length; i++) {
var key = reducerKeys[i]
if (typeof reducers[key] !== 'function') throw new Error('reducer 必须是函数')
}
// 2. 调用一个一个晓得reducer 将每一个小的reducer中返回的状态储存在一个新的大的对象中
return function (state, action) {
var nextState = {}
for (var i=0; i<reducerKeys.length; i++) {
var key = reducerKeys[i]
var reducer = reducers[key]
var previousStateForKey = state[key]
nextState[key] = reducer(previousStateForKey, action)
}
console.log(nextState)
return nextState
}
}