Redux架构模式
1.抽离 store 和监控数据变化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Redux架构模式</title>
<style type="text/css">
</style>
</head>
<body>
<div id='title'></div>
<div id='content'></div>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
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 }
}
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
}
let appState = {
title: {
text: 'React.js 小书',
color: 'red',
},
content: {
text: 'React.js 小书内容',
color: 'blue'
}
}
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
}
}
const store = createStore(appState, stateChanger)
store.subscribe(() => renderApp(store.getState())) // 监听数据变化
renderApp(store.getState()) // 首次渲染页面
store.dispatch({ type: 'UPDATE_TITLE_TEXT', text: '《React.js 小书》' }) // 修改标题文本
store.dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'blue' }) // 修改标题颜色
})
</script>
</body>
</html>
2.共享结构的对象提高性能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Redux架构模式</title>
<style type="text/css">
</style>
</head>
<body>
<div id='title'></div>
<div id='content'></div>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
function createStore(state, stateChanger) {
const listeners = []
const subscribe = (listener) => listeners.push(listener)
const getState = () => state
const dispatch = (action) => {
state = stateChanger(state, action) // 覆盖原对象
listeners.forEach((listener) => listener())
}
return { getState, dispatch, subscribe }
}
function renderApp(newAppState, oldAppState = {}) { // 防止 oldAppState 没有传入,所以加了默认参数 oldAppState = {}
if (newAppState === oldAppState) return // 数据没有变化就不渲染了
console.log('render app...')
renderTitle(newAppState.title, oldAppState.title)
renderContent(newAppState.content, oldAppState.content)
}
function renderTitle(newTitle, oldTitle = {}) {
if (newTitle === oldTitle) return // 数据没有变化就不渲染了
console.log('render title...')
const titleDOM = document.getElementById('title')
titleDOM.innerHTML = newTitle.text
titleDOM.style.color = newTitle.color
}
function renderContent(newContent, oldContent = {}) {
if (newContent === oldContent) return // 数据没有变化就不渲染了
console.log('render content...')
const contentDOM = document.getElementById('content')
contentDOM.innerHTML = newContent.text
contentDOM.style.color = newContent.color
}
let appState = {
title: {
text: 'React.js 小书',
color: 'red',
},
content: {
text: 'React.js 小书内容',
color: 'blue'
}
}
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 // 没有修改,返回原来的对象
}
}
const store = createStore(appState, stateChanger)
let oldState = store.getState() // 缓存旧的 state
store.subscribe(() => {
const newState = store.getState() // 数据可能变化,获取新的 state
renderApp(newState, oldState) // 把新旧的 state 传进去渲染
oldState = newState // 渲染完以后,新的 newState 变成了旧的 oldState,等待下一次数据变化重新渲染
})
renderApp(store.getState()) // 首次渲染页面
store.dispatch({ type: 'UPDATE_TITLE_TEXT', text: '《React.js 小书》' }) // 修改标题文本
store.dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'blue' }) // 修改标题颜色
})
</script>
</body>
</html>
3.reducer
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Redux架构模式</title>
<style type="text/css">
</style>
</head>
<body>
<div id='title'></div>
<div id='content'></div>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
$(function() {
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({}) // 初始化 state
return { getState, dispatch, subscribe }
}
function renderApp(newAppState, oldAppState = {}) { // 防止 oldAppState 没有传入,所以加了默认参数 oldAppState = {}
if (newAppState === oldAppState) return // 数据没有变化就不渲染了
console.log('render app...')
renderTitle(newAppState.title, oldAppState.title)
renderContent(newAppState.content, oldAppState.content)
}
function renderTitle(newTitle, oldTitle = {}) {
if (newTitle === oldTitle) return // 数据没有变化就不渲染了
console.log('render title...')
const titleDOM = document.getElementById('title')
titleDOM.innerHTML = newTitle.text
titleDOM.style.color = newTitle.color
}
function renderContent(newContent, oldContent = {}) {
if (newContent === oldContent) return // 数据没有变化就不渲染了
console.log('render content...')
const contentDOM = document.getElementById('content')
contentDOM.innerHTML = newContent.text
contentDOM.style.color = newContent.color
}
function reducer (state, action) {
if (!state) {
return {
title: {
text: 'React.js 小书',
color: 'red',
},
content: {
text: 'React.js 小书内容',
color: 'blue'
}
}
}
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
}
}
const store = createStore(reducer)
let oldState = store.getState() // 缓存旧的 state
store.subscribe(() => {
const newState = store.getState() // 数据可能变化,获取新的 state
renderApp(newState, oldState) // 把新旧的 state 传进去渲染
oldState = newState // 渲染完以后,新的 newState 变成了旧的 oldState,等待下一次数据变化重新渲染
})
renderApp(store.getState()) // 首次渲染页面
store.dispatch({ type: 'UPDATE_TITLE_TEXT', text: '《React.js 小书》' }) // 修改标题文本
store.dispatch({ type: 'UPDATE_TITLE_COLOR', color: 'blue' }) // 修改标题颜色
})
</script>
</body>
</html>
不知不觉地,到这里大家不仅仅已经掌握了 Redux,而且还自己动手写了一个 Redux。我们从一个非常原始的代码开始,不停地在发现问题、解决问题、优化代码的过程中进行推演,最后把 Redux 模式自己总结出来了。这就是所谓的 Redux 模式,我们再来回顾一下这几节我们到底干了什么事情。
我们从一个简单的例子的代码中发现了共享的状态如果可以被任意修改的话,那么程序的行为将非常不可预料,所以我们提高了修改数据的门槛:你必须通过 dispatch
执行某些允许的修改操作,而且必须大张旗鼓的在 action
里面声明。
这种模式挺好用的,我们就把它抽象出来一个 createStore
,它可以产生 store
,里面包含 getState
和 dispatch
函数,方便我们使用。
后来发现每次修改数据都需要手动重新渲染非常麻烦,我们希望自动重新渲染视图。所以后来加入了订阅者模式,可以通过 store.subscribe
订阅数据修改事件,每次数据更新的时候自动重新渲染视图。
接下来我们发现了原来的“重新渲染视图”有比较严重的性能问题,我们引入了“共享结构的对象”来帮我们解决问题,这样就可以在每个渲染函数的开头进行简单的判断避免没有被修改过的数据重新渲染。
我们优化了 stateChanger
为 reducer,定义了 reducer 只能是纯函数,功能就是负责初始 state
,和根据 state
和 action
计算具有共享结构的新的 state
。
createStore
现在可以直接拿来用了,套路就是:
// 定一个 reducer
function reducer (state, action) {
/* 初始化 state 和 switch case */
}
// 生成 store
const store = createStore(reducer)
// 监听数据变化重新渲染页面
store.subscribe(() => renderApp(store.getState()))
// 首次渲染页面
renderApp(store.getState())
// 后面可以随意 dispatch 了,页面自动更新
store.dispatch(...)
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步