本文作者:IMWeb 黄qiong 原文出处:IMWeb社区 未经同意,禁止转载
Redux 其实是用来帮我们管理状态的一个框架,它暴露给我们四个接口,分别是:
- createStore
- combineReducers
- bindActionCreators
- applyMiddleware
- compose
源码系列里会分别对这五个接口进行解析。
Redux 的源码解析系列开篇之前,先来了解一下它的实现思想。
1、 为什么要有dispatch
假设一种场景下,app里每个组件都需要拿到appState的一部分进行渲染。
但是这里存在一个风险就是,谁都可以修改appState的值,换句话说,有一天当appState变了你都不知道是谁改的,所以我们需要有一个管理员来帮我们管理我们的状态,这时候引入了dispatch函数,来专门负责修改数据。
代码语言:javascript复制function dispatch (action) {
switch (action.type) {
case 'UPDATE_TITLE_TEXT':
appState.title.text = action.text
break
case 'UPDATE_TITLE_COLOR':
appState.title.color = action.color
break
default:
break
}
}
解决问题: 既可以解决组件共享问题,同时不会有数据可以被任意修改的问题。
2、为什么要有createStore
现在我们有了状态,又有了dispatch,这时候我们需要一个高层管理者store,帮我们管理好他们,
所以我们就有了createStore这个函数帮我们生成store, 这样再用的时候就可以直接store.getState
, store.dispatch
的方式获取和更改组件。
function createStore(state, stateChanger) {
const getState = () => state;
const dispatch = (action) => stateChanger(state, action)
return {getState, dispatch}
}
createStore 接受两个参数,一个是表示app的 state。另外一个是 stateChanger,它来描述应用程序状态会根据 action 发生什么变化,其实就是相当于本节开头的 dispatch 代码里面的内容,我们后来会将它命名为reducer。
到了这一步,每当我想状态发生改变的时候,我就dispatch一个action来改变组件当前的状态。
但是这里还有一个问题,就是store里的数据发生改变之后,react是感知不到的。
如图:
所以我们需要手动在重新render一次APP,这时候就需要观察者模式,订阅数据的改变,然后自动调用renderApp,所以我们的createStore功能又强大啦~
代码语言:javascript复制function createStore(state, reducer) {
const getState = () => state;
const listeners = [];
const subscribe = (listener) => {
listeners.push(listener)
}
const dispatch = (action) => {
reducer(state, action);
// 数据已发生改变就把所有的listener跑一遍
listeners.forEach((listener) => {
listener()
})
}
return {getState, dispatch, subscribe}
}
我们就可以这样使用
代码语言:javascript复制store.subscribe(() => renderApp(store.getState()))
这样,我们的renderApp 就会监听状态变化,每当dispatch一次action,它都会被自动执行。
流程图如下:
到这一步,一个APP就已经可以无压力的跑起来啦,最后一步,当然是关注性能,我们这个app 还是有严重性能问题的,因为每一次的dispatch 一次action,不管数据有没有变化,组件都会被重新渲染,这当然是不必要的。
3、为什么reducer是纯函数
所以就需要对reducer产生的前后appState进行一个对比,这就要求reducer必须是一个纯函数,返回的是一个新的object,不能直接更改reducer的参数,这样才能够对比可以通过对比前后的state是否相等,来决定是否re-render
代码语言:javascript复制// reducer用来管理状态变化
function reducer (state, action) {
if(!state) {
return appState;
}
switch (action.type) {
case 'CHANGE_TITLE':
return {
...state,
title: {
...state.title,
text: action.text
}
}
case 'CHANGE_CONTENT':
return {
...state,
content: {
...state.content,
color: action.color
}
}
}
}
代码语言:javascript复制function createStore(state, reducer) {
let appState = state;
const getState = () => appState;
const listeners = [];
const subscribe = (listener) => {
listeners.push(listener)
}
const dispatch = (action) => {
// 覆盖原先的appState
appState = reducer(state, action);
listeners.forEach((listener) => {
listener()
})
}
return {getState, dispatch, subscribe}
}
OK,到这一步,我们的redux就基本完成啦~ 接着改装下我们的reducer,让它有一个初始值,这样我们的createStore就只需要传入一个reducer即可
代码语言:javascript复制// reducer用来管理状态变化
function reducer (state, action) {
//设置初始值
if(!state) {
return appState;
}
switch (action.type) {
case 'CHANGE_TITLE':
return {
...state,
title: {
...state.title,
text: action.text
}
}
case 'CHANGE_CONTENT':
return {
...state,
content: {
...state.content,
color: action.color
}
}
}
}
代码语言:javascript复制function createStore (reducer) {
let state = null
const listeners = []
const subscribe = (listener) => listeners.push(listener)
const getState = () => state
const dispatch = (action) => {
// 可以看到 由于reducer返回的是一个新的object,那在外层,我们就可以对比nextProps跟t his.props 来决定是否渲染
state = reducer(state, action)
listeners.forEach((listener) => listener())
}
dispatch({}) // 初始化 state
return { getState, dispatch, subscribe }
}
总结以下:store对象里的方法主要做三件事
- getState : 获取组件状态
- dispatch :改变组件状态
- subscribe : 订阅组件变化
4、如何使用redux
需要五个步骤
代码语言:javascript复制// 1、定一个 reducer, 负责管理数据变化还有初始化appState的数据
function reducer (state, action) {
/* 初始化 state 和 switch case */
}
// 2、生成 store
const store = createStore(reducer)
// 监听数据变化重新渲染页面
store.subscribe(() => renderApp(store.getState()))
// 首次渲染页面
renderApp(store.getState())
// 后面可以随意 dispatch 了,页面自动更新
store.dispatch(...)
我们整个过程就是不断地发现问题,解决问题
1、共享状态 -> dispatch
2、store统一管理 dispatch getState
3、性能优化 --> reducer是一个纯函数
4、最终初始化整个reducer
以上就是redux的大致思想。
参考文档:
http://huziketang.com/books/react/lesson30