这一次总结主要是针对 React 的亮点 Redux 。html
在什么样的状况下适合使用 React-redux:redux
Redux 是一种架构模式,有三大特性 Store、Actions、Reducers , Store 是保存数据的地方,整个应用只能有一个。Actions 是
view
发出的通知,监听state
的变化。 Reducers 是一个纯函数,返回新的state
。数组
优雅地修改共享状态。bash
const appState = {
title: {
text: '这里是标题',
color: 'red',
},
content: {
text: '内容内容内容',
color: 'blue'
}
}
// 渲染函数
function renderApp(appState) {
renderTitle(appState.title)
renderContent(appState.content)
}
// 渲染标题
function renderTitle(title) {
const titleDOM = document.getElementById('title')
titleDOM.innerHTML = title.text
titleDOM.style.color = title.color
}
// 渲染内容
function renderContent(content) {
const contentDOM = document.getElementById('content')
contentDOM.innerHTML = content.text
contentDOM.style.color = content.color
}
renderApp(appState)
复制代码
上面的
appState
是一个共享状态,随时会被修改,这是不可预料的,有时候一些模块须要这个共享状态而且还须要修改它,这时能够声明一个函数来监听哪一处修改数据。架构
// 监听应用状态修改
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
}
}
dispatch({ type: 'UPDATE_TITLE_TEXT', text: '我已经修改了标题和颜色' }) // 修改标题文本
dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'green' }) // 修改标题颜色
renderApp(appState) // 渲染
复制代码
这样就不怕
renderApp()
执行以前的其余函数会对它们作什么修改,若是某个函数修改了title.text
可是我并不想要它这么干,我须要debug
出来是哪一个函数修改了,我只须要在dispatch
的switch
的第一个case
内部打个断点就能够调试出来了。app
抽离Store。函数
接下来会把
appState
和dispatch
抽出来放在一个地方,把这个地方叫做store
,而且构建一个函数叫createStore
用来专门生产这种state
和dispatch
的集合。post
<!-- 接受一个 应用状态 和 描述应用程序状态函数( 至关上面的dispatch ) -->
function createStore (state, stateChanger) {
<!-- 直接返回 state -->
const getState = () => state
<!-- 接受一个 state 还有一个 根据 action 来修改 state 的函数( 至关上面的dispatch ) -->
const dispatch = (action) => stateChanger(state, action)
<!-- 返回 state 和 dispatch -->
return { getState, dispatch }
}
复制代码
以后咱们来使用它:性能
<!-- 监听应用状态修改 (至关于上面的 dispatch 函数) -->
<!-- 接受 应用状态 和 修改应用状态的信息 -->
function stateChanger(state, action) {
switch (action.type) {
case 'UPDATE_TITLE_TEXT':
state.title.text = action.text
break
case 'UPDATE_TITLE_COLOR':
state.title.color = action.color
break
default:
break
}
}
// store
function createStore(state, stateChanger) {
const getState = () => state;
const dispatch = (action) => stateChanger(state, action);
return {getState, dispatch}
}
<!-- 传入 应用状态 和 修改应用状态的函数 -->
const store = createStore(appState, stateChanger);
renderApp(store.getState()) // 第一次渲染
store.dispatch({type: 'UPDATE_TITLE_TEXT', text: '老子修改了标题'}); // 发送 action
store.dispatch({type: 'UPDATE_TITLE_COLOR', color: 'green'}); // 发送 action
renderApp(store.getState()) // 从新渲染
复制代码
上面每次经过
dispatch
修改数据时候,若是不手动调用renderApp
页面是不会发生变化的,咱们能够利用观察者模式,往dispatch
加入renderApp
就好了。ui
function createStore(state, stateChanger) {
<!-- 声明空数组 -->
const listeners = [];
<!-- 每次调用接受渲染函数做为参数而且添加到空数组 -->
const subscribe = (listener) => listeners.push(listener);
const getState = () => state;
const dispatch = (action) => {
stateChanger(state, action);
<!-- 把全部渲染函数遍历而且调用,这样其余的渲染也会从新渲染 -->
listeners.forEach((listener) => listener());
}
return {getState, dispatch, subscribe}
}
const store = createStore(appState, stateChanger)
<!-- 下面能够传递多个渲染函数,而后调用 dispatch 所有会从新渲染 -->
store.subscribe(() => renderApp(store.getState()))
store.subscribe(() => renderApp2(store.getState()))
store.subscribe(() => renderApp3(store.getState()))
复制代码
上面
subscribe
是订阅者,这样的订阅模式,不管你利用dispatch
进行修改数据,他都会从新渲染,还有一个好处就是拿同一块数据来渲染别的页面时,dispatch
致使的变化也会让每一个页面都从新渲染。
总结来讲就是构建一个
createStore
,它能够产生一种咱们新定义的数据类型store
,经过store.getState
咱们获取共享状态,并且咱们约定只能经过store.dispatch
修改共享状态。store
也容许咱们经过store.subscribe
监听数据数据状态被修改了,而且进行后续的例如从新渲染页面的操做。
共享结构的对象提升性能。
// 渲染函数
function renderApp(appState) {
console.log('渲染所有')
renderTitle(appState.title)
renderContent(appState.content)
}
// 渲染标题
function renderTitle(title) {
console.log('渲染标题')
const titleDOM = document.getElementById('title')
titleDOM.innerHTML = title.text
titleDOM.style.color = title.color
}
// 渲染内容
function renderContent(content) {
console.log('渲染内容')
const contentDOM = document.getElementById('content')
contentDOM.innerHTML = content.text
contentDOM.style.color = content.color
}
renderApp(store.getState()) // 第一次渲染
store.dispatch({type: 'UPDATE_TITLE_TEXT', text: '老子修改了标题'});
store.dispatch({type: 'UPDATE_TITLE_COLOR', color: 'green'});
复制代码
在上面调用时会从新渲染三次,这会有一个很严重的性能问题,我并无修改
content
的东西,也会从新渲染content
。
这里提出的解决方案是,在每一个渲染函数执行渲染操做以前先作个判断,判断传入的新数据和旧的数据是否是相同,相同的话就不渲染了。
// 渲染函数
function renderApp(newAppState, oldAppState = {}) {
if (newAppState === oldAppState) return // 数据没有变化就不渲染了
console.log('渲染所有')
renderTitle(newAppState.title, oldAppState.title)
renderContent(newAppState.content, oldAppState.content)
}
// 渲染标题
function renderTitle(newTitle, oldTitle = {}) {
if (newTitle === oldTitle) return // 数据没有变化就不渲染了
console.log('渲染标题')
const titleDOM = document.getElementById('title')
titleDOM.innerHTML = newTitle.text
titleDOM.style.color = newTitle.color
}
// 渲染内容
function renderContent(newContent, oldContent = {}) {
if (newContent === oldContent) return // 数据没有变化就不渲染了
console.log('渲染内容')
const contentDOM = document.getElementById('content')
contentDOM.innerHTML = newContent.text
contentDOM.style.color = newContent.color
}
// 监听应用状态修改
function stateChanger(state, action) {
switch (action.type) {
case 'UPDATE_TITLE_TEXT':
return {
...state,
title: {
...state.title,
text: action.text
}
}
case 'UPDATE_TITLE_COLOR':
return {
...state,
title: {
...state.title,
color: action.color
}
}
default:
return state
}
}
// store
function createStore(state, stateChanger) {
const listeners = [];
const subscribe = (listener) => listeners.push(listener);
const getState = () => state;
const dispatch = (action) => {
state = stateChanger(state, action); // 从新覆盖 state
listeners.forEach((listener) => listener());
}
return { getState, dispatch, subscribe }
}
const store = createStore(appState, stateChanger);
let oldState = store.getState(); // 保存旧状态
store.subscribe(() => {
const newState = store.getState();
renderApp(newState, oldState);
oldState = newState; // 每次更改让旧的状态变成新的状态
})
renderApp(store.getState()) // 第一次渲染
store.dispatch({ type: 'UPDATE_TITLE_TEXT', text: '老子修改了标题' });
store.dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'green' });
复制代码
上面那三个是第一次渲染触发的,下面那两次修改从新渲染时并无从新渲染
content
。
因为上面须要保存一个旧的状态,而后根据旧的状态和新的状态进行判断,上面的
stateChanger
修改为返回一个新的状态,这个新的状态与旧的状态是相对独立的,还有就是store
里面的dispatch
,每次进行修改就用一个state
接受一个新的状态而且覆盖原来的state
,最后再将新的状态和旧的状态传达给renderApp
。
reducer
reducer
是一个纯函数,它接受两个参数,一个是state
,一个是action
,它的做用是计算state
而且返回一个新的state
,若是没有传入值或没法识别action
时,会直接返回state
。
function themeReducer (state, action) {
if (!state) return {
themeName: 'Red Theme',
themeColor: 'red'
}
switch (action.type) {
case 'UPATE_THEME_NAME':
return { ...state, themeName: action.themeName }
case 'UPATE_THEME_COLOR':
return { ...state, themeColor: action.themeColor }
default:
return state
}
}
function createStore(reducer) {
let state = null; // 初始化状态
const listeners = [];
const subscribe = (listener) => listeners.push(listener);
const getState = () => state;
const dispatch = (action) => {
state = reducer(state, action);
listeners.forEach((listener) => listener());
}
dispatch({}) // 初始化
return { getState, dispatch, subscribe }
}
复制代码
Store
收到Action
之后,必须给出一个新的State
,这样View
才会发生变化。这种State
的计算过程就叫作Reducer
。Reducer
是一个纯函数,它接受Action
和当前State
做为参数,返回一个新的State
。
redux
的使用有如下步骤:
// 定一个 reducer
function reducer (state, action) {
/* 初始化 state 和 switch case */
}
// 生成 store
const store = createStore(reducer)
// 监听数据变化从新渲染页面
store.subscribe(() => renderApp(store.getState()))
// 首次渲染页面
renderApp(store.getState())
// 后面能够随意 dispatch 了,页面自动更新
store.dispatch(...)
复制代码