摘要:更新類型及具體內(nèi)容負(fù)責(zé)更新數(shù)據(jù)的具體邏輯。即根據(jù)當(dāng)前及攜帶的信息合成新的數(shù)據(jù)。下面帶著這些問題深入了解本質(zhì)上也是高階組件的一種實(shí)現(xiàn)。核心實(shí)現(xiàn)聲明以被子組件獲取。通過的實(shí)現(xiàn),我們可以得到的重要性淺比較的實(shí)現(xiàn)由此可以看到的重要性。
前言Redux作為通用的狀態(tài)管理器,可以搭配任意界面框架。所以并搭配react使用的話就要借助redux官方提供的React綁定庫react-redux,以高效靈活的在react中使用redux。下面我們一起看看是react-redux如何靈活高效的
redux 概述在開始之間還是大概提一下redux的內(nèi)容,以免脫節(jié)。比較早的時(shí)候也解讀了下redux的源碼實(shí)現(xiàn),可以參考一下
Redux 是 JavaScript 狀態(tài)容器,旨在提供可預(yù)測化的狀態(tài)管理。 其包括action、store、reducer等三部分:
在理解各部分作用之前,我們可以通過一個(gè)更新數(shù)據(jù)的例子來捋下思路:
要更新數(shù)據(jù),肯定有個(gè)數(shù)據(jù)庫來存儲和維護(hù)數(shù)據(jù)。即數(shù)據(jù)層。
具體如何更新,需要有負(fù)責(zé)執(zhí)行的部分,即邏輯處理層。
具體何時(shí)更新哪個(gè)字段、何時(shí)更新,同樣需要分發(fā)層來控制。
根據(jù)上面的例子我們再對比下redux的流程圖(圖片來自阮一峰大佬): 可以對照著來理解不同部分的作用。
action就如上面所說負(fù)責(zé)更新字段和更新時(shí)機(jī)。 用戶接觸到的是view(即用戶界面),對應(yīng)的更新信息通過acton傳遞給reducer。
function addTodo(text) {
return {
// 更新類型及具體內(nèi)容
type: ADD_TODO,
text
}
}
reducer
負(fù)責(zé)更新數(shù)據(jù)的具體邏輯。
即根據(jù)當(dāng)前state及action攜帶的信息合成新的數(shù)據(jù)。
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
})
// 不同更新類型的處理邏輯不同
case ADD_TODO:
return Object.assign({}, state, {
todos: [
...state.todos,
{
text: action.text,
completed: false
}
]
})
default:
return state
}
}
store
store就是負(fù)責(zé)維護(hù)和管理數(shù)據(jù)。 此外還有dispatch,subscrible等api來完成更新事件的分發(fā)。 例如:
import { createStore } from "redux"
import todoApp from "./reducers"
let store = createStore(todoApp)
import {
addTodo} from "./actions"
// 注冊監(jiān)聽事件
const unsubscribe = store.subscribe(() => console.log(store.getState()))
// 發(fā)起一系列 action
store.dispatch(addTodo("Learn about actions"))
// 停止監(jiān)聽
unsubscribe()
到這里,我們應(yīng)該就大概明白redux如何更新管理數(shù)據(jù)的了。
通過store.subscribe來監(jiān)聽狀態(tài)更新,這是響應(yīng)變化的重要一步。
然后通過stroe.getState()獲取相應(yīng)數(shù)據(jù)
具體更新通過action和reducer來實(shí)現(xiàn)了。
那么對照react-redux的實(shí)例官方demo,來結(jié)合React的時(shí)候,會發(fā)現(xiàn)redux使用有些不同之處。
不同之處大概可以有下面這三點(diǎn):
組件沒有顯示調(diào)用store.subscrible()
state也不是通過Store.getState()來獲取。
多了Provider和connect方法
可以猜測,上述差異是React-redux幫我們封裝了綁定監(jiān)聽等過程,避免需要每個(gè)應(yīng)用都重復(fù)相同的操作。使得React組件的數(shù)據(jù)源只關(guān)注props和state。
下面帶著這些問題深入了解React-redux.
react-redux本質(zhì)上 react-redux也是react高階組件HOC的一種實(shí)現(xiàn)。其基于 容器組件和展示組件相分離 的開發(fā)思想來實(shí)現(xiàn)的。 其核心是通過兩部分來實(shí)現(xiàn): 1、Provider 2、container通過connect來解除手動調(diào)用store.subscrible
provider 的實(shí)現(xiàn)provider用法如下,綁定之后,再經(jīng)過connect處理,就可以在組件中通過props訪問對應(yīng)信息了。
import React from "react"
import { render } from "react-dom"
import { Provider } from "react-redux"
import { createStore } from "redux"
import todoApp from "./reducers"
import App from "./components/App"
let store = createStore(todoApp)
render(
// 綁定store
<App />
Provider>,
document.getElementById("root")
)
在看源碼之前,我們先自行猜測一下。
前面也提到了Provider是React組件。
那么為了讓子組件都能方便的訪問到store,store這個(gè)屬性會如何傳遞呢。props?context?
import { Component, Children } from "react"
export default class Provider extends Component {
// 聲明context 以被子組件獲取。
getChildContext() {
return { store: this.store }
}
constructor(props, context) {
super(props, context)
// 掛載store到Provider
this.store = props.store
}
render() {
// 判斷是否只有一個(gè)child,是則返回該child節(jié)點(diǎn),否則拋錯(cuò)
return Children.only(this.props.children)
}
}
Provider將store傳遞給子組件,具體如何和組件綁定就是conect做的事情了。
connectconnect連接組件和store,該操作并不修改原組件而是返回一個(gè)新的增強(qiáng)了關(guān)聯(lián)store的組件。
根據(jù)這個(gè)描述,這顯然就是個(gè)React高階組件(HOC)嗎。先看一下使用:
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
接收四個(gè)參數(shù),具體每個(gè)參數(shù)的作用詳細(xì)可以參考cn.redux.js.org/docs/react-…
[mapStateToProps(state, [ownProps]): stateProps] (Function): 如果定義該參數(shù),組件將會監(jiān)聽 Redux store 的變化。任何時(shí)候,只要 Redux store 發(fā)生改變,mapStateToProps 函數(shù)就會被調(diào)用。該回調(diào)函數(shù)必須返回一個(gè)純對象,這個(gè)對象會與組件的 props 合并。
[mapDispatchToProps(dispatch, [ownProps]): dispatchProps](Object or Function): 如果傳遞的是一個(gè)對象,那么每個(gè)定義在該對象的函數(shù)都將被當(dāng)作 Redux action creator,對象所定義的方法名將作為屬性名;每個(gè)方法將返回一個(gè)新的函數(shù),函數(shù)中dispatch方法會將 action creator 的返回值作為參數(shù)執(zhí)行。這些屬性會被合并到組件的 props 中。
[mergeProps(stateProps, dispatchProps, ownProps): props] (Function): 如果指定了這個(gè)參數(shù),mapStateToProps() 與 mapDispatchToProps() 的執(zhí)行結(jié)果和組件自身的 props 將傳入到這個(gè)回調(diào)函數(shù)中。該回調(diào)函數(shù)返回的對象將作為 props 傳遞到被包裝的組件中。
[options] (Object) 如果指定這個(gè)參數(shù),可以定制 connector 的行為
[pure = true] (Boolean): 如果為 true,connector 將執(zhí)行 shouldComponentUpdate 并且淺對比 mergeProps 的結(jié)果,避免不必要的更新,默認(rèn)true
[withRef = false] (Boolean): 如果為 true,connector 會保存一個(gè)對被被包含的組件實(shí)例的引用,該引用通過 getWrappedInstance() 方法獲得。默認(rèn)false
import { connect } from "react-redux"
import { toggleTodo } from "../actions"
import TodoList from "../components/TodoList"
import { VisibilityFilters } from "../actions"
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case VisibilityFilters.SHOW_ALL:
return todos
case VisibilityFilters.SHOW_COMPLETED:
return todos.filter(t => t.completed)
case VisibilityFilters.SHOW_ACTIVE:
return todos.filter(t => !t.completed)
default:
throw new Error("Unknown filter: " + filter)
}
}
// 將store中的state作為props傳遞給被包裹組件
// mapStateToProps對應(yīng)當(dāng)前組件所需要的props,不過這個(gè)props顯然是要從store中抽取的,不是所有store都需要,所以只會取state.todos
const mapStateToProps = state => ({
todos: getVisibleTodos(state.todos, state.visibilityFilter)
})
// 將action 與被包裹組件相綁定。
// 其實(shí)就是將action中的方法賦值到Props上,以便在組件中調(diào)用toggleTodo方法
const mapDispatchToProps = dispatch => ({
toggleTodo: id => dispatch(toggleTodo(id))
})
// 被包裹組件就對應(yīng)TodoList
export default connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
具體實(shí)現(xiàn)
connect實(shí)現(xiàn)比較復(fù)雜一點(diǎn),返回的是個(gè)高階函數(shù)我們可以先看該函數(shù)實(shí)現(xiàn)了什么。
首先該方法接受相關(guān)參數(shù),進(jìn)行參數(shù)的判斷和兼容處理(不指定使用默認(rèn))。 并返回一個(gè) wrapWithConnect 方法來裝飾傳入的容器組件。
// 每個(gè)參數(shù)的默認(rèn)實(shí)現(xiàn)
const defaultMapStateToProps = state => ({}) // eslint-disable-line no-unused-vars
const defaultMapDispatchToProps = dispatch => ({ dispatch })
const defaultMergeProps = (stateProps, dispatchProps, parentProps) => ({
...parentProps,
...stateProps,
...dispatchProps
})
export default function connect(mapStateToProps, mapDispatchToProps, mergeProps, options = {}) {
// 需要store中的state才會去監(jiān)聽
const shouldSubscribe = Boolean(mapStateToProps)
// 更新state 方法的兼容,無mapStateToProps則使用默認(rèn)
const mapState = mapStateToProps || defaultMapStateToProps
let mapDispatch
// action creater是否為 函數(shù)
if (typeof mapDispatchToProps === "function") {
// 函數(shù)直接賦值
mapDispatch = mapDispatchToProps
} else if (!mapDispatchToProps) {
// 不存在,則使用默認(rèn)方法
mapDispatch = defaultMapDispatchToProps
} else {
// 否則 將action Creater 包裝起來
mapDispatch = wrapActionCreators(mapDispatchToProps)
}
const finalMergeProps = mergeProps || defaultMergeProps
const { pure = true, withRef = false } = options
const checkMergedEquals = pure && finalMergeProps !== defaultMergeProps
function wrapWithConnect(WrappedComponent) {
const connectDisplayName = `Connect(${getDisplayName(WrappedComponent)})`
class Connect extends Component {/****/}
// ****
return hoistStatics(Connect, WrappedComponent)
}
wrapWithConnect 函數(shù)接受一個(gè)組件(connect這就是個(gè)HOC。返回一個(gè)connect組件
// ****省略*****
// hoistStatics的作用:常用語高階組件中,將被包裹元素的靜態(tài)方法,“同步”到容器元素中。
// 也就是 connect中那些WrappedComponent屬性的mix
return hoistStatics(Connect, WrappedComponent)
這里,就是HOC常見的增加功能的實(shí)現(xiàn)了。 也就是增強(qiáng)與redux的關(guān)聯(lián),讓使用者只需要關(guān)注props,而非每次都要自己手動綁定。
既然connect存在生命周期,那就順著生命周期看看
this.store 即Provider中掛載的Store
// 構(gòu)造函數(shù),獲取store中的state
constructor(props, context) {
super(props, context)
this.version = version
// props或者context中,這是provider中掛載的store
this.store = props.store || context.store
// 獲取state
const storeState = this.store.getState()
// 初始化state
this.state = { storeState }
this.clearCache()
}
shouldComponentUpdate這里會根據(jù)options里面的參數(shù)來看是否 pure 選擇不同的更新策略
shouldComponentUpdate() {
return !pure || this.haveOwnPropsChanged || this.hasStoreStateChanged
}
componentDidMount 根據(jù)前面的shouldSubscribe標(biāo)識(mapStateToProps是否為true)決定是否增加監(jiān)聽事件
componentDidMount() {
this.trySubscribe()
}
trySubscribe() {
// 存在監(jiān)聽必要 并且沒有注冊過監(jiān)聽事件
if (shouldSubscribe && !this.unsubscribe) {
// 業(yè)務(wù)組件中沒有使用的subscribe 在這里實(shí)現(xiàn),這也是HOC的用法之一,公共方法的抽離
// 注冊完成之后,this.unsubscribe為對一個(gè)unsubscribe回調(diào)
this.unsubscribe = this.store.subscribe(this.handleChange.bind(this))
this.handleChange()
}
}
componentWillReceiveProps 判斷是否更新 ,對于pure 組件 這里就涉及到了shallowEqual。 通過shallowEqual的實(shí)現(xiàn),我們可以得到Immtable的重要性
componentWillReceiveProps(nextProps) {
if (!pure || !shallowEqual(nextProps, this.props)) {
this.haveOwnPropsChanged = true
}
}
由此可以看到Immutable的重要性。對于引用類型的數(shù)據(jù),只是比較了引用地址是否相同。
對于嵌套引用數(shù)據(jù)類型,只比較key的長度和value引用地址,并沒有進(jìn)一步深入比較。導(dǎo)致嵌套結(jié)構(gòu)并不適用。
export default function shallowEqual(objA, objB) {
// 引用地址是否相同
if (objA === objB) {
return true
}
const keysA = Object.keys(objA)
const keysB = Object.keys(objB)
// key長度是否相同
if (keysA.length !== keysB.length) {
return false
}
// 循環(huán)比較,vakue思否相同,對于嵌套引用類型,這種比較是不能滿足預(yù)期的。
const hasOwn = Object.prototype.hasOwnProperty
for (let i = 0; i < keysA.length; i++) {
if (!hasOwn.call(objB, keysA[i]) ||
objA[keysA[i]] !== objB[keysA[i]]) {
return false
}
}
return true
}
再下面是render,對于是否更新進(jìn)行判斷,即是否更新傳遞給子組件的props render的關(guān)注點(diǎn)在于 傳遞給WrappedComponent的props如何獲得。
// this.mergedProps 的計(jì)算
if (withRef) {
this.renderedElement = createElement(WrappedComponent, {
...this.mergedProps,
ref: "wrappedInstance"
})
} else {
this.renderedElement = createElement(WrappedComponent,
this.mergedProps
)
}
計(jì)算this.mergedProps 最終傳遞下去的props是經(jīng)過mapStateToProps,mapDispatchToProps計(jì)算之后,最后再由mergeProps計(jì)算之后的state。
// 簡化代碼
this.mergedProps = nextMergedProps = computeMergedProps(this.stateProps, this.dispatchProps, this.props)
/**
* 獲得最終props 即經(jīng)過參數(shù)中的
* @param {*} stateProps 經(jīng)過mapStateToProps之后的結(jié)果
* @param {*} dispatchProps mapDispatchToProps之后的結(jié)果
* @param {*} parentProps 此處即為connect組件的props this.props
* @returns
*/
function computeMergedProps(stateProps, dispatchProps, parentProps) {
// finalMergeProps 即為參數(shù)中的mergeProps 或者 defaultMergeProps。 將前兩參數(shù)計(jì)算結(jié)果再進(jìn)行處理。
const mergedProps = finalMergeProps(stateProps, dispatchProps, parentProps)
if (process.env.NODE_ENV !== "production") {
checkStateShape(mergedProps, "mergeProps")
}
return mergedProps
}
到這里connect的作用也體現(xiàn)出來了:
根據(jù)參數(shù)決定監(jiān)數(shù)據(jù)的變化
將store和action作為warpered的props傳入,一共組件使用store中的state和action
對于部分操作進(jìn)行緩存優(yōu)化,提升執(zhí)行效率
此時(shí)再回過頭去看上面的例子應(yīng)該更清晰了。
結(jié)束語 參考文章cn.redux.js.org/docs/react-…
到這里就結(jié)束了react-redux的源碼解析,更多是自己的學(xué)習(xí)筆記吧。
使用一定程度之后再回頭看,可能對自己的理解更有幫助。
另外閱讀源碼不是要盲目去讀,而是在應(yīng)用之后帶著問題去讀。
這樣會更清晰如何去優(yōu)化如何去提升。因?yàn)樗接邢蘅隙ㄓ绣e(cuò)漏指出,歡迎指出。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/6802.html
摘要:帶有內(nèi)部狀態(tài),內(nèi)部可以使用。代表監(jiān)聽的全局,也可以說是全局的,表示該組件自身攜帶的方法?;卮饐栴}是通過應(yīng)用的,將通過頂層組件傳遞到上下文環(huán)境中。所有頁面集合緩存不用每次都重新加載通過綁定用法完參考文章 react-redux把組件分為UI組件和容器組件。先看圖下圖: showImg(https://segmentfault.com/img/bVbkVBz?w=1378&h=1194);...
摘要:函數(shù)組合,科里化的串聯(lián)結(jié)合示例源碼,實(shí)現(xiàn)也很優(yōu)雅,對于返回的,將等參數(shù)傳遞進(jìn)去,然后執(zhí)行,等待回調(diào)異步完成再。對于正常對象則進(jìn)行下一步。前言 作為前端狀態(tài)管理器,這個(gè)比較跨時(shí)代的工具庫redux有很多實(shí)現(xiàn)和思想值得我們思考。在深入源碼之前,我們可以相關(guān)注下一些常見問題,這樣帶著問題去看實(shí)現(xiàn),也能更加清晰的了解。 常見問題 大概看了下主要有這么幾個(gè): redux三大原則 這個(gè)可以直接參考...
摘要:簡介創(chuàng)建的函數(shù),返回一個(gè)對象,包含等方法合并多個(gè)中間件處理,在實(shí)際的前調(diào)用一系列中間件,類似于綁定和函數(shù)式編程中常見的方法,介紹官方提供的綁定庫。 前言 在學(xué)習(xí)了React之后, 緊跟著而來的就是Redux了~ 在系統(tǒng)性的學(xué)習(xí)一個(gè)東西的時(shí)候, 了解其背景、設(shè)計(jì)以及解決了什么問題都是非常必要的。接下來記錄的是, 我個(gè)人在學(xué)習(xí)Redux時(shí)的一些雜七雜八~ Redux是什么 通俗理解 h...
摘要:的核心思想就是維護(hù)一個(gè)單向數(shù)據(jù)流,數(shù)據(jù)的流向永遠(yuǎn)是單向的,所以每個(gè)步驟便是可預(yù)測的,程序的健壯性得到了保證。另外,還有一點(diǎn)比較重要的是,因?yàn)闆]有了一個(gè)一直保存更新的狀態(tài)對象,所以在中的也就沒有意義了,通過可以完全實(shí)現(xiàn)一個(gè)順暢的數(shù)據(jù)流。 1 Redux Redux is a predictable state container for JavaScript apps 簡單來說,Redu...
摘要:本文并不逐行地對源碼進(jìn)行細(xì)致分析,不如說是基于以下幾個(gè)問題,對源碼進(jìn)行大致的掃覽。我們已經(jīng)知道,中,允許用戶注冊監(jiān)聽器,這些監(jiān)聽器會在每次執(zhí)行結(jié)束后遍歷觸發(fā)。省略一些無關(guān)代碼其中,是為了在嵌套的中嵌套執(zhí)行。 react-redux 源碼解讀 [TOC] 前置知識 閱讀本篇文章前,請先確認(rèn)你是否了解以下知識: react redux 高階組件 react diff 機(jī)制 其中高階組件...
閱讀 1274·2021-09-30 09:47
閱讀 3839·2021-09-06 15:02
閱讀 1852·2021-09-01 10:46
閱讀 2431·2019-08-30 15:52
閱讀 699·2019-08-29 15:28
閱讀 1930·2019-08-29 15:08
閱讀 1224·2019-08-29 13:28
閱讀 2628·2019-08-29 12:19