日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

学习React的一知半解

發(fā)布時間:2023/12/10 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 学习React的一知半解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

① 初探

HMTL的渲染過程

? 這個結(jié)構(gòu)化文本就是 HTML 文本, HTML 中的每個元素都對應(yīng) DOM中某個節(jié)點,這樣,因為 HTML 元素的逐級包含關(guān)系, DOM 節(jié)點自然就構(gòu)成了一個樹形結(jié)構(gòu),稱為 DOM 樹 。 ? 瀏覽器為了渲染 HTML 格式的網(wǎng)頁,會先將 HTML 文本解析以構(gòu)建 DOM 樹,然后根據(jù) DOM 樹渲染出用戶看到的界面,當(dāng)要改變界面內(nèi)容的時候,就去改變 DOM 樹上的節(jié)點 。

純函數(shù)

React 的理念 ,歸結(jié)為一個公式,就像下面這樣 : ? UI=render(data) ? 讓我們來看看這個公式表達的含義,用戶看到的界面( UI),應(yīng)該是一個函數(shù)(在這里叫 render)的執(zhí)行結(jié)果,只接受數(shù)據(jù)( data)作為參數(shù) 。

? 這個函數(shù)是一個純函數(shù),所謂純函數(shù),指的是沒有任何副作用,輸出完全依賴于輸入的函數(shù),兩次函數(shù)調(diào)用如果輸人 ? 相同,得到的結(jié)果也絕對相同 。 如此一來,最終的用戶界面,在 render 函數(shù)確定的情況下完全取決于輸入數(shù)據(jù) 。

React初解

? react的功能其實很單一,主要負責(zé)渲染的功能,現(xiàn)有的框架,比如angular是一個大而全的框架,用了angular幾乎就不需要用其他工具輔助配合.

PS: react感覺類似VScode或sublime需要裝各種插件來寫代碼,而angular就像webstorm一樣集成了很多功能

React 是什么

? 用腳本進行DOM操作的代價很昂貴。把DOM和JavaScript各自想象為一個島嶼,它們之間用收費橋梁連接,js每次訪問DOM,都要途徑這座橋,并交納“過橋費”,訪問DOM的次數(shù)越多,費用也就越高。

? 因此,推薦的做法是盡量減少過橋的次數(shù),努力待在ECMAScript島上。因為這個原因react的虛擬dom就顯得難能可貴了,它創(chuàng)造了虛擬dom并且將它們儲存起來,每當(dāng)狀態(tài)發(fā)生變化的時候就會創(chuàng)造新的虛擬節(jié)點和以前的進行對比,讓變化的部分進行渲染。

整個過程沒有對dom進行獲取和操作,只有一個渲染的過程,所以react說是一個ui框架。

組件的生命周期

組件在初始化時會觸發(fā)5個鉤子函數(shù):

1、getDefaultProps()

? 設(shè)置默認的props,也可以用defaultProps設(shè)置組件的默認屬性。

這個函數(shù)只在 React.createClass 方法創(chuàng)造的組件類才會用到 。

2、getInitialState()

? 在使用es6的class語法時是沒有這個鉤子函數(shù)的,可以直接在constructor中定義this.state。此時可以訪問this.props。

? 這個函數(shù)只在 React.createClass 方法創(chuàng)造的組件類才會用到 。

3、componentWillMount()

? 組件初始化時只調(diào)用,以后組件更新不調(diào)用,整個生命周期只調(diào)用一次,此時可以修改state。

4、 render()

? react最重要的步驟,創(chuàng)建虛擬dom,進行diff算法,更新dom樹都在此進行。此時就不能更改state了。

? 通常一個組件要發(fā)揮作用,總是要渲染一些東西, render 函數(shù)并不做實際的誼染動作,它只是返回一個 JSX 描述的結(jié)構(gòu),最終由 React 來操作渲染過程。 ? 當(dāng)然,某些特殊組件的作用不是渲染界面,或者,組件在某些情況下選擇沒有東西可畫,那就讓 render 函數(shù)返回一個 null 或者 false ,等于告訴 React,這個組件這次不需要渲染任何 DOM 元素 。 ? 需要注意, render 函數(shù)應(yīng)該是一個純函數(shù),完全根據(jù) this.state 和 this.props 來決定返回的結(jié)果,而且不要產(chǎn)生任何副作用。在 render 函數(shù)中去調(diào)用 this.setState 毫無疑問是錯誤的,因為一個純函數(shù)不應(yīng)該引起狀態(tài)的改變。

5、componentDidMount()

? Render 函數(shù)返回的東西已 經(jīng)引發(fā)了渲染,組件已經(jīng)被“裝載”到了 DOM 樹上 。 組件渲染之后調(diào)用,可以通過this.getDOMNode()獲取和操作dom節(jié)點,只調(diào)用一次。


在更新時也會觸發(fā)5個鉤子函數(shù):

6、componentWillReceivePorps(nextProps)

組件初始化時不調(diào)用,組件接受新的props時調(diào)用。

7、shouldComponentUpdate(nextProps, nextState)

? React性能優(yōu)化非常重要的一環(huán)。組件接受新的state或者props時調(diào)用,我們可以設(shè)置在此對比前后兩個props和state是否相同,如果相同則返回false阻止更新,因為相同的屬性狀態(tài)一定會生成相同的dom樹,這樣就不需要創(chuàng)造新的dom樹和舊的dom樹進行diff算法對比,節(jié)省大量性能,尤其是在dom結(jié)構(gòu)復(fù)雜的時候。不過調(diào)用this.forceUpdate會跳過此步驟。

8、componentWillUpdate(nextProps, nextState)

組件初始化時不調(diào)用,只有在組件將要更新時才調(diào)用,此時可以修改state

9、render()

當(dāng)組件的state或者props發(fā)生改變的時候,render函數(shù)就會重新執(zhí)行

10、componentDidUpdate()

組件初始化時不調(diào)用,組件更新完成后調(diào)用,此時可以獲取dom節(jié)點。

還有一個卸載鉤子函數(shù)

11、componentWillUnmount()

組件將要卸載時調(diào)用,一些事件監(jiān)聽和定時器需要在此時清除。

? 以上可以看出來react總共有10個周期函數(shù)(render重復(fù)一次),這個10個函數(shù)可以滿足我們所有對組件操作的需求,利用的好可以提高開發(fā)效率和組件性能。

? render 和 shouldComponentUpdate函數(shù),也是 React 生命周期函數(shù)中唯二兩個要求有返回結(jié)果的函數(shù)。 render 函數(shù)的返回結(jié)果將用于構(gòu)造 DOM 對象,而 shouldComponentUpdate函數(shù)返回一個布爾值,告訴 React 庫這個組件在這次更新過程中是否要繼續(xù) 。

V16 生命周期函數(shù)用法建議

class ExampleComponent extends React.Component {// 用于初始化 stateconstructor() {}// 用于替換 `componentWillReceiveProps` ,該函數(shù)會在初始化和 `update` 時被調(diào)用// 因為該函數(shù)是靜態(tài)函數(shù),所以取不到 `this`// 如果需要對比 `prevProps` 需要單獨在 `state` 中維護static getDerivedStateFromProps(nextProps, prevState) {}// 判斷是否需要更新組件,多用于組件性能優(yōu)化shouldComponentUpdate(nextProps, nextState) {}// 組件掛載后調(diào)用// 可以在該函數(shù)中進行請求或者訂閱componentDidMount() {}// 用于獲得最新的 DOM 數(shù)據(jù)getSnapshotBeforeUpdate() {}// 組件即將銷毀// 可以在此處移除訂閱,定時器等等componentWillUnmount() {}// 組件銷毀后調(diào)用componentDidUnMount() {}// 組件更新后調(diào)用componentDidUpdate() {}// 渲染組件函數(shù)render() {}// 以下函數(shù)不建議使用UNSAFE_componentWillMount() {}UNSAFE_componentWillUpdate(nextProps, nextState) {}UNSAFE_componentWillReceiveProps(nextProps) {} } 復(fù)制代碼

父子組件的渲染過程

? 因為 render 函數(shù)本身并不往 DOM 樹上渲染或者裝載內(nèi)容,它只是返回一個 JSX 表示的對象,然后由 React 庫來根據(jù)返回對象決定如何渲染 。而 React 庫肯定是要把所有組件返回的結(jié)果綜合起來,才能知道該如何產(chǎn)生對應(yīng)的 DOM修改 。 所以,只有 React 庫調(diào)用三個 Counter 組件的 render 函數(shù)之后,才有可能完成裝載,這時候才會依次調(diào)用各個組件的 componentDidMount 函數(shù)作為裝載過程的收尾 。

React的組件化

? react的一個組件很明顯的由dom視圖和state數(shù)據(jù)組成,兩個部分涇渭分明。

? state是數(shù)據(jù)中心,它的狀態(tài)決定著視圖的狀態(tài)。這時候發(fā)現(xiàn)似乎和我們一直推崇的MVC開發(fā)模式有點區(qū)別,沒了Controller控制器,那用戶交互怎么處理,數(shù)據(jù)變化誰來管理?

? 然而這并不是react所要關(guān)心的事情,它只負責(zé)ui的渲染。與其他框架監(jiān)聽數(shù)據(jù)動態(tài)改變dom不同,react采用setState來控制視圖的更新。

? setState會自動調(diào)用render函數(shù),觸發(fā)視圖的重新渲染,如果僅僅只是state數(shù)據(jù)的變化而沒有調(diào)用setState,并不會觸發(fā)更新。

? 組件就是擁有獨立功能的視圖模塊,許多小的組件組成一個大的組件,整個頁面就是由一個個組件組合而成。它的好處是利于重復(fù)利用和維護。

UI = render(data)

? React 組件扮 演的是 render 函數(shù)的角色,應(yīng)該是一個沒有副作用的純函數(shù)。修改 props 的值, 是一個副作用,組件應(yīng)該避免。

組件類別

概念: 所謂組件,簡單說,指的是能完成某個特定功能的獨立的 、 可重用的代碼 。

  • 容器組件 只關(guān)心邏輯,不負責(zé)頁面渲染
  • UI組件 不關(guān)心邏輯,只負責(zé)頁面渲染
  • 無狀態(tài)組件 沒有render()函數(shù),只是一個函數(shù),沒有聲明周期函數(shù),效率更高

React的 Diff算法

? 當(dāng)組件更新的時候,react會創(chuàng)建一個新的虛擬dom樹并且會和之前儲存的dom樹進行比較,這個比較多過程就用到了diff算法,所以組件初始化的時候是用不到的

? react提出了一種假設(shè),相同的節(jié)點具有類似的結(jié)構(gòu),而不同的節(jié)點具有不同的結(jié)構(gòu)。在這種假設(shè)之上進行逐層的比較,如果發(fā)現(xiàn)對應(yīng)的節(jié)點是不同的,那就直接刪除舊的節(jié)點以及它所包含的所有子節(jié)點然后替換成新的節(jié)點。如果是相同的節(jié)點,則只進行屬性的更改。

? 對于列表的diff算法稍有不同,因為列表通常具有相同的結(jié)構(gòu),在對列表節(jié)點進行刪除,插入,排序的時候,單個節(jié)點的整體操作遠比一個個對比一個個替換要好得多,所以在創(chuàng)建列表的時候需要設(shè)置key值,這樣react才能分清誰是誰。當(dāng)然不寫key值也可以,但這樣通常會報出警告,通知我們加上key值以提高react的性能。

演變過程: JSX > createElement > 虛擬dom (JS對象) > 真實dom

虛擬Dom的對比算法

不同類型的元素

? 每當(dāng)根元素有不同類型,React將卸載舊樹并重新構(gòu)建新樹。從<a>到<img>或從<Article>到<Comment>,或從<Button> 到 <div>,任何的調(diào)整都會導(dǎo)致全部重建。

? 當(dāng)樹被卸載,舊的DOM節(jié)點將被銷毀。組件實例會調(diào)用componentWillUnmount()。當(dāng)構(gòu)建一棵新樹,新的DOM節(jié)點被插入到DOM中。組件實例將依次調(diào)用componentWillMount()和componentDidMount()。任何與舊樹有關(guān)的狀態(tài)都將丟棄。

? 這個根節(jié)點下所有的組件都將會被卸載,同時他們的狀態(tài)將被銷毀。

相同類型的DOM元素

? 當(dāng)比較兩個相同類型的React DOM元素時,React則會觀察二者的屬性,保持相同的底層DOM節(jié)點,并僅更新變化的屬性。

相同類型的組件元素

? 當(dāng)組件更新時,實例仍保持一致,以讓狀態(tài)能夠在渲染之間保留。React通過更新底層組件實例的props來產(chǎn)生新元素,并在底層實例上依次調(diào)用componentWillReceiveProps() 和 componentWillUpdate() 方法。

? 接下來,render()方法被調(diào)用,同時對比算法會遞歸處理之前的結(jié)果和新的結(jié)果。

React diff算法流程圖

key的作用

? React DOM 首先會比較元素內(nèi)容先后的不同,而在渲染過程中只會更新改變了的部分。

? key的重要性: 提高對比的效率

? Keys可以在DOM中的某些元素被增加或刪除的時候幫助React識別哪些元素發(fā)生了變化。因此你應(yīng)當(dāng)給數(shù)組中的每一個元素賦予一個確定的標(biāo)識。

? 用數(shù)組下標(biāo)作為 key,看起來 key 值是唯一的,但是卻不是穩(wěn)定不變的,隨著 todos數(shù)組值的不同,同樣一個 Todoltem 實例在不同的更新過程中在數(shù)組中的下標(biāo)完全可能不同,把下標(biāo)當(dāng)做 key 就讓 React 徹底亂套了 。 ? 需要注意,雖然 key 是一個 prop ,但是接受 key 的組件并不能讀取到 key 的值,因為 key 和 ref 是 React 保留的兩個特殊 prop ,并沒有預(yù)期讓組件直接訪問 。

為什么使用setState修改數(shù)據(jù)?

? 直接修改this.state的值,雖然事實上改變了組件的內(nèi)部狀態(tài),但只是野蠻地修改了state ,卻沒有驅(qū)動組件進行重新渲染,既然組件沒有重新渲染,當(dāng)然不會反應(yīng) this.state值的變化;

? 而 this.setState()函數(shù)所做的事情,首先是改變 this.state 的值,然后驅(qū)動組件經(jīng)歷更新過程,這樣才有機會讓 this.state 里新的值出現(xiàn)在界面上 。

setState 是異步函數(shù)?

? setState() 排隊更改組件的 state ,并通過更新 state來告訴 React,該組件及其子組件需要重新渲染。這是用于 響應(yīng)事件處理程序 和 服務(wù)器響應(yīng) 更新用戶界面的主要方法。

? 記住 setState() 作為一個請求,而不是立即命令來更新組件。為了更好的感知性能,React 可能會延遲它,然后合并多個setState()更新多個組件。React不保證state 更新就立即應(yīng)用(重新渲染)。

? React 可以將多個setState() 調(diào)用合并成一個調(diào)用來提高性能。

? 因為 this.props 和 this.state 可能是異步更新的,你不應(yīng)該依靠它們的值來計算下一個狀態(tài)。setState() 并不總是立即更新組件。它可能會 批量延遲到后面更新。這使得在調(diào)用 setState() 之后立即讀取 this.state 存在一個潛在的陷阱。 而使用 componentDidUpdate 或 setState 回調(diào)(setState(updater, callback)),在應(yīng)用更新后,都將被保證觸發(fā)。

舉個例子:

? 例如,此代碼可能無法更新計數(shù)器:

// Wrong this.setState({counter: this.state.counter + this.props.increment, }); 復(fù)制代碼

? 要修復(fù)它,請使用第二種形式的 setState() 來接受一個函數(shù)而不是一個對象。 該函數(shù)將接收先前的狀態(tài)作為第一個參數(shù),將此次更新被應(yīng)用時的props做為第二個參數(shù):

// Correct this.setState((prevState, props) => ({counter: prevState.counter + props.increment })); 復(fù)制代碼

? setState()總是會導(dǎo)致重新渲染,除非 shouldComponentUpdate()返回 false 。如果可變對象被使用,并且條件渲染邏輯不能在shouldComponentUpdate() 中實現(xiàn),只有當(dāng)新state與先前 state 不同時調(diào)用 setState()才能避免不必要的重新渲染。

不能在render()里面寫this.setState()會導(dǎo)致循環(huán)修改

React組件寫法

ES6的class類可以看作是構(gòu)造函數(shù)的一個語法糖,可以把它當(dāng)成構(gòu)造函數(shù)來看,extends實現(xiàn)了類之間的繼承 —— 定義一個類Main 繼承React.Component所有的屬性和方法,組件的生命周期函數(shù)就是從這來的。

constructor是構(gòu)造器,在實例化對象時調(diào)用,super調(diào)用了父類的constructor創(chuàng)造了父類的實例對象this,然后用子類的構(gòu)造函數(shù)進行修改。

super(props)

? 如果在構(gòu)造函數(shù)中沒有調(diào)用super(props),那么組件實例被構(gòu)造之后,類實例的所有成員函數(shù)就無法通過 this.props 訪問到父組件傳遞過來的 props 值。很明顯,給 this.props 賦值是 React.Component 構(gòu)造函數(shù)的工作之一 。

shouldCompnentUpdate生命周期

? 在通用的 shouldCompnentUpdate 函數(shù)中做“淺層比較”,是一個被普遍接受的做法;如果需要做“深層比較”,那就是某個特定組件的行為,需要開發(fā)者自己根據(jù)組件情況去編寫 。

PureComponent

? React15.3 中新加了一個類PureComponent,前身是 PureRenderMixin ,和 Component 基本一樣,只不過會在 render之前幫組件自動執(zhí)行一次shallowEqual(淺比較),來決定是否更新組件,淺比較類似于淺復(fù)制,只會比較第一層。使用 PureComponent 相當(dāng)于省去了寫 shouldComponentUpdate 函數(shù),當(dāng)組件更新時,如果組件的 props 和 state:

  • 引用和第一層數(shù)據(jù)都沒發(fā)生改變, render 方法就不會觸發(fā),這是我們需要達到的效果。
  • 雖然第一層數(shù)據(jù)沒變,但引用變了,就會造成虛擬 DOM 計算的浪費。
  • 第一層數(shù)據(jù)改變,但引用沒變,會造成不渲染,所以需要很小心的操作數(shù)據(jù)。
  • so. 為了性能,React只做了淺對比,于是就有了immutable.js

    immutable.js

    高階組件

    ? 高階組件就是一個函數(shù),且該函數(shù)接受一個組件作為參數(shù),并返回一個新的組件

    const EnhancedComponent = higherOrderComponent(WrappedComponent); 復(fù)制代碼

    ? 對比組件將props屬性轉(zhuǎn)變成UI,高階組件則是將一個組件轉(zhuǎn)換成另一個新組件。

    ? 高階組件在React第三方庫中很常見,比如Redux的connect方法和Relay的createContainer.

    Refs屬性

    創(chuàng)建 Refs

    ? 使用 React.createRef() 創(chuàng)建 refs,通過 ref 屬性來獲得 React 元素。當(dāng)構(gòu)造組件時,refs 通常被賦值給實例的一個屬性,這樣你可以在組件中任意一處使用它們.

    class MyComponent extends React.Component {constructor(props) {super(props);this.myRef = React.createRef();}render() {return <div ref={this.myRef} />;} } 復(fù)制代碼

    ref的值取決于節(jié)點的類型:

    • 當(dāng) ref 屬性被用于一個普通的 HTML 元素時,React.createRef() 將接收底層 DOM 元素作為它的 current 屬性以創(chuàng)建 ref 。
    • 當(dāng) ref 屬性被用于一個自定義類組件時,ref 對象將接收該組件已掛載的實例作為它的 current 。
    • 你不能在函數(shù)式組件上使用 ref 屬性,因為它們沒有實例。

    React-Router路由

    ? Router就是React的一個組件,它并不會被渲染,只是一個創(chuàng)建內(nèi)部路由規(guī)則的配置對象,根據(jù)匹配的路由地址展現(xiàn)相應(yīng)的組件。

    ? Route則對路由地址和組件進行綁定,Route具有嵌套功能,表示路由地址的包涵關(guān)系,這和組件之間的嵌套并沒有直接聯(lián)系。Route可以向綁定的組件傳遞7個屬性:children,history,location,params,route,routeParams,routes,每個屬性都包涵路由的相關(guān)的信息。

    ? 比較常用的有children(以路由的包涵關(guān)系為區(qū)分的組件),location(包括地址,參數(shù),地址切換方式,key值,hash值)。

    ? react-router提供Link標(biāo)簽,這只是對a標(biāo)簽的封裝,值得注意的是,點擊鏈接進行的跳轉(zhuǎn)并不是默認的方式,react-router阻止了a標(biāo)簽的默認行為并用pushState進行hash值的轉(zhuǎn)變。

    ? 切換頁面的過程是在點擊Link標(biāo)簽或者后退前進按鈕時,會先發(fā)生url地址的轉(zhuǎn)變,Router監(jiān)聽到地址的改變根據(jù)Route的path屬性匹配到對應(yīng)的組件,將state值改成對應(yīng)的組件并調(diào)用setState觸發(fā)render函數(shù)重新渲染dom。

    路由(按需加載)

    ? 當(dāng)頁面比較多時,項目就會變得越來越大,尤其對于單頁面應(yīng)用來說,初次渲染的速度就會很慢,這時候就需要按需加載,只有切換到頁面的時候才去加載對應(yīng)的js文件。react配合webpack進行按需加載的方法很簡單,Route的component改為getComponent,組件用require.ensure的方式獲取,并在webpack中配置chunkFilename。

    const chooseProducts = (location, cb) => {require.ensure([], require => {cb(null, require('../Component/chooseProducts').default)},'chooseProducts') }const helpCenter = (location, cb) => {require.ensure([], require => {cb(null, require('../Component/helpCenter').default)},'helpCenter') }const saleRecord = (location, cb) => {require.ensure([], require => {cb(null, require('../Component/saleRecord').default)},'saleRecord') }const RouteConfig = (<Router history={history}><Route path="/" component={Roots}><IndexRoute component={index} />//首頁<Route path="index" component={index} /><Route path="helpCenter" getComponent={helpCenter} />//幫助中心<Route path="saleRecord" getComponent={saleRecord} />//銷售記錄<Redirect from='*' to='/' /></Route></Router> ); 復(fù)制代碼

    組件之間的通信

    ? react推崇的是單向數(shù)據(jù)流,通常被稱為自頂向下或單向數(shù)據(jù)流。 任何狀態(tài)始終由某些特定組件所有,并且從該狀態(tài)導(dǎo)出的任何數(shù)據(jù)或 UI 只能影響樹中下方的組件。

    解決通信問題的方法很多:

  • 如果只是父子級關(guān)系,父級可以將一個回調(diào)函數(shù)當(dāng)作屬性傳遞給子級,子級可以直接調(diào)用函數(shù)從而和父級通信。
  • 組件層級嵌套到比較深,可以使用上下文getChildContext來傳遞信息,這樣在不需要將函數(shù)一層層往下傳,任何一層的子級都可以通過this.context直接訪問。
  • 兄弟關(guān)系的組件之間無法直接通信,它們只能利用同一層的上級作為中轉(zhuǎn)站。而如果兄弟組件都是最高層的組件,為了能夠讓它們進行通信,必須在它們外層再套一層組件,這個外層的組件起著保存數(shù)據(jù),傳遞信息的作用,這其實就是redux所做的事情。
  • 組件之間的信息還可以通過全局事件來傳遞。不同頁面可以通過參數(shù)傳遞數(shù)據(jù),下個頁面可以用location.param來獲取。
  • React的事件委托

    ? 我們在 JSX 中看到一個組件使用了 onClick,但并沒有產(chǎn)生直接使用 onclick (注意是 onclick 不是 onClick)的HTML ,而是使用了事件委托(event delegation)的方式處理點擊事件,無論有多少個 onClick 出現(xiàn),其實最后都只在 DOM 樹上添加了一個事件處理函數(shù),掛在最頂層的 DOM 節(jié)點上。

    ? 所有的點擊事件都被這個事件處理函數(shù)捕獲,然后根據(jù)具體組件分配給特定函數(shù),使用事件委托的性能當(dāng)然要比為每個 onClick 都掛載一個事件處理函數(shù)要高 。 ? 因為 React 控制了組件的生命周期,在 unmount 的時候自然能夠清除相關(guān)的所有事 件處理函數(shù),內(nèi)存泄露也不再是一個問題。

    ② 進階

    Redux

    基本原則

    Flux 的基本原則是“單向數(shù)據(jù)流”, Redux 在此基礎(chǔ)上強調(diào)三個基本原則:

    • 唯一數(shù)據(jù)源( Single Source of Truth);

      ? 在 Flux 中,應(yīng)用可以擁有多個 Store ,往往根據(jù)功能把應(yīng)用的狀態(tài) 數(shù)據(jù)劃分給若干個 Store 分別存儲管理 。

      ? Redux 對這個問題的解決方法就是,整個應(yīng)用只保持一個 Store ,所有組件的數(shù)據(jù)源 就是這個 Store 上的狀態(tài) 。

    • 保持狀態(tài)只讀( State is read-only);

      ? 保持狀態(tài)只讀,就是說不能去直接修改狀態(tài),要修改 Store 的狀態(tài),必須要通過派發(fā) 一個 action 對象完成,這一點 ,和 Flux 的要求并沒有什么區(qū)別 。

      ? 當(dāng)然,要驅(qū)動用戶界面渲染,就要改變應(yīng)用的狀態(tài),但是改變狀態(tài)的方法不是去修 改狀態(tài)上值,而是創(chuàng)建一個新的狀態(tài)對象返回給 Redux ,由 Redux 完成新的狀態(tài)的組裝 。

    • 數(shù)據(jù)改變只能通過純函數(shù)完成( Changes are made with pure functions ) 。

      ? 在 Redux 中, 每個 reducer 的函數(shù)簽名如下所示 : ? reducer(state , action ) ? 第一個參數(shù) state 是當(dāng)前的狀態(tài),第二個參數(shù) action 是接收到的 action 對象,而 reducer函數(shù)要做的事情,就是根據(jù) state 和 action 的值產(chǎn)生一個新的對象返回,注意 reducer 必須是純函數(shù),也就是說函數(shù)的返回結(jié)果必須完全由參數(shù) state 和 action 決定,而且不產(chǎn)生任何副作用,也不能修改參數(shù) state 和 action 對象。

    Redux核心API

    Redux主要由三部分組成:store,reducer,action。

    store

    ? Redux的核心是store,它由Redux提供的 createStore(reducer, defaultState)這個方法生成,生成三個方法,getState(),dispatch(),subscrible()。

    • getState():存儲的數(shù)據(jù),狀態(tài)樹;
    • dispatch(action):分發(fā)action,并返回一個action,這是唯一能改變store中數(shù)據(jù)的方式;
    • subscrible(listener):注冊一個監(jiān)聽者,store發(fā)生變化的時候被調(diào)用。

    reducer

    reducer是一個純函數(shù),它根據(jù)previousState和action計算出新的state。 reducer(previousState,action)

    action

    action本質(zhì)上是一個JavaScript對象,其中必須包含一個type字段來表示將要執(zhí)行的動作,其他的字段都可以根據(jù)需求來自定義。

    const ADD_TODO = 'ADD_TODO' 復(fù)制代碼{type: ADD_TODO,text: 'Build my first Redux app' } 復(fù)制代碼

    整合

    他們?nèi)咧g的交互,可以由下圖概括:

    概念分析:

    redux主要由三部分組成:store,reducer,action。

    store是一個對象,它有四個主要的方法:

    1、dispatch:

    ? 用于action的分發(fā)——在createStore中可以用middleware中間件對dispatch進行改造,比如當(dāng)action傳入dispatch會立即觸發(fā)reducer,有些時候我們不希望它立即觸發(fā),而是等待異步操作完成之后再觸發(fā),這時候用redux-thunk對dispatch進行改造,以前只能傳入一個對象,改造完成后可以傳入一個函數(shù),在這個函數(shù)里我們手動dispatch一個action對象,這個過程是可控的,就實現(xiàn)了異步。

    2、subscribe:

    ? 監(jiān)聽state的變化——這個函數(shù)在store調(diào)用dispatch時會注冊一個listener監(jiān)聽state變化,當(dāng)我們需要知道state是否變化時可以調(diào)用,它返回一個函數(shù),調(diào)用這個返回的函數(shù)可以注銷監(jiān)聽。

    let unsubscribe = store.subscribe(() => {console.log('state發(fā)生了變化')})

    3、getState:

    ? 獲取store中的state——當(dāng)我們用action觸發(fā)reducer改變了state時,需要再拿到新的state里的數(shù)據(jù),畢竟數(shù)據(jù)才是我們想要的。

    ? getState主要在兩個地方需要用到,一是在dispatch拿到action后store需要用它來獲取state里的數(shù)據(jù),并把這個數(shù)據(jù)傳給reducer,這個過程是自動執(zhí)行的,二是在我們利用subscribe監(jiān)聽到state發(fā)生變化后調(diào)用它來獲取新的state數(shù)據(jù),如果做到這一步,說明我們已經(jīng)成功了。

    4、replaceReducer:

    替換reducer,改變state修改的邏輯。

    ? store可以通過createStore()方法創(chuàng)建,接受三個參數(shù),經(jīng)過combineReducers合并的reducer和state的初始狀態(tài)以及改變dispatch的中間件,后兩個參數(shù)并不是必須的。store的主要作用是將action和reducer聯(lián)系起來并改變state。

    action:

    ? action是一個對象,其中type屬性是必須的,同時可以傳入一些數(shù)據(jù)。action可以用actionCreactor進行創(chuàng)造。dispatch就是把action對象發(fā)送出去。

    reducer:

    ? reducer是一個函數(shù),它接受一個state和一個action,根據(jù)action的type返回一個新的state。根據(jù)業(yè)務(wù)邏輯可以分為很多個reducer,然后通過combineReducers將它們合并,state樹中有很多對象,每個state對象對應(yīng)一個reducer,state對象的名字可以在合并時定義。

    const reducer = combineReducers({a: doSomethingWithA,b: processB,c: c }) 復(fù)制代碼

    combineReducers:

    ? 其實它也是一個reducer,它接受整個state和一個action,然后將整個state拆分發(fā)送給對應(yīng)的reducer進行處理,所有的reducer會收到相同的action,不過它們會根據(jù)action的type進行判斷,有這個type就進行處理然后返回新的state,沒有就返回默認值,然后這些分散的state又會整合在一起返回一個新的state樹。

    流程分析:

  • 首先調(diào)用store.dispatch將action作為參數(shù)傳入,同時用getState獲取當(dāng)前的狀態(tài)樹state并注冊subscribe的listener監(jiān)聽state變化,再調(diào)用combineReducers并將獲取的state和action傳入。
  • combineReducers會將傳入的state和action傳給所有reducer,并根據(jù)action的type返回新的state,觸發(fā)state樹的更新,我們調(diào)用subscribe監(jiān)聽到state發(fā)生變化后用getState獲取新的state數(shù)據(jù)。
  • redux的state和react的state兩者完全沒有關(guān)系,除了名字一樣。


    React-Redux

    React-redux是怎么配合的

    react-redux 的兩個最主要功能:

    • connect :連接容器組件和視圖組件;
    • Provider :提供包含 store 的 context。
  • react-redux提供了connect和Provider兩個好基友,它們一個將組件與redux關(guān)聯(lián)起來,一個將store傳給組件。
  • 組件通過dispatch發(fā)出action,store根據(jù)action的type屬性調(diào)用對應(yīng)的reducer并傳入state和這個action,reducer對state進行處理并返回一個新的state放入store,connect監(jiān)聽到store發(fā)生變化,調(diào)用setState更新組件,此時組件的props也就跟著變化。
  • 值得注意的是connect,Provider,mapStateToProps,mapDispatchToProps是react-redux提供的,redux本身和react沒有半毛錢關(guān)系,它只是數(shù)據(jù)處理中心,沒有和react產(chǎn)生任何耦合,是react-redux讓它們聯(lián)系在一起。
  • Redux 本身和React沒有關(guān)系,只是數(shù)據(jù)處理中心,是React-Redux讓他們聯(lián)系在一起。

    React-Redux的兩個方法

    connect

    掘金資料

    connect連接React組件和Redux store。connect實際上是一個高階函數(shù),返回一個新的已與 Redux store 連接的組件類。

    const VisibleTodoList = connect(mapStateToProps,mapDispatchToProps )(TodoList) 復(fù)制代碼

    TodoList是 UI 組件,VisibleTodoList就是由 react-redux 通過connect方法自動生成的容器組件。

  • mapStateToProps:從Redux狀態(tài)樹中提取需要的部分作為props傳遞給當(dāng)前的組件。
  • mapDispatchToProps:將需要綁定的響應(yīng)事件(action)作為props傳遞到組件上。
  • **書籍資料 **

    export default connect(mapStateToProps, mapDispatchToProps) ( Counter); 復(fù)制代碼

    這個 connect 函數(shù)具體做了什么工作呢? 作為容器組件,要做的工作無外乎兩件事:

    • 把 Store 上的狀態(tài)轉(zhuǎn)化為內(nèi)層傻瓜組件的 prop;
    • 把內(nèi)層傻瓜組件中的用戶動作轉(zhuǎn)化為派送給 Store 的動作 。

    Provider

    Provider實現(xiàn)store的全局訪問,將store傳給每個組件。

    原理:使用React的context,context可以實現(xiàn)跨組件之間的傳遞。

    如果只使用redux,那么流程是這樣的:

    component --> dispatch(action) --> reducer --> subscribe --> getState --> component

    用了react-redux之后流程是這樣的:

    component --> actionCreator(data) --> reducer --> component

    store的三大功能:dispatch,subscribe,getState都不需要手動來寫了。

    react-redux幫我們做了這些,同時它提供了兩個好基友Provider和connect。

    Provider是一個組件,它接受store作為props,然后通過context往下傳,這樣react中任何組件都可以通過context獲取store。

    ? 也就意味著我們可以在任何一個組件里利用dispatch(action)來觸發(fā)reducer改變state,并用subscribe監(jiān)聽state的變化,然后用getState獲取變化后的值。但是并不推薦這樣做,它會讓數(shù)據(jù)流變的混亂,過度的耦合也會影響組件的復(fù)用,維護起來也更麻煩。

    connect --connect(mapStateToProps, mapDispatchToProps, mergeProps, options) 是一個函數(shù),它接受四個參數(shù)并且再返回一個函數(shù)--wrapWithConnect,wrapWithConnect接受一個組件作為參數(shù)wrapWithConnect(component),它內(nèi)部定義一個新組件Connect(容器組件)并將傳入的組件(ui組件)作為Connect的子組件然后return出去。

    所以它的完整寫法是這樣的:`connect(mapStateToProps, mapDispatchToProps, mergeProps, options)(component)

    mapStateToProps(state, [ownProps]):

    mapStateToProps 接受兩個參數(shù),store的state和自定義的props,并返回一個新的對象,這個對象會作為props的一部分傳入ui組件。我們可以根據(jù)組件所需要的數(shù)據(jù)自定義返回一個對象。ownProps的變化也會觸發(fā)mapStateToProps

    function mapStateToProps(state) {return { todos: state.todos }; } 復(fù)制代碼

    mapDispatchToProps(dispatch, [ownProps]):

    mapDispatchToProps如果是對象,那么會和store綁定作為props的一部分傳入ui組件。

    如果是個函數(shù),它接受兩個參數(shù),bindActionCreators會將action和dispatch綁定并返回一個對象,這個對象會和ownProps一起作為props的一部分傳入ui組件。

    所以不論mapDispatchToProps是對象還是函數(shù),它最終都會返回一個對象,如果是函數(shù),這個對象的key值是可以自定義的

    function mapDispatchToProps(dispatch) {return {todoActions: bindActionCreators(todoActionCreators, dispatch),counterActions: bindActionCreators(counterActionCreators, dispatch)}; } 復(fù)制代碼

    mapDispatchToProps返回的對象其屬性其實就是一個個actionCreator,因為已經(jīng)和dispatch綁定,所以當(dāng)調(diào)用actionCreator時會立即發(fā)送action,而不用手動dispatch。ownProps的變化也會觸發(fā)mapDispatchToProps。

    mergeProps(stateProps, dispatchProps, ownProps):

    將mapStateToProps() 與 mapDispatchToProps()返回的對象和組件自身的props合并成新的props并傳入組件。默認返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的結(jié)果。

    options:

    pure = true 表示Connect容器組件將在shouldComponentUpdate中對store的state和ownProps進行淺對比,判斷是否發(fā)生變化,優(yōu)化性能。為false則不對比。

    其實connect函數(shù)并沒有做什么,大部分的邏輯都是在它返回的wrapWithConnect函數(shù)內(nèi)實現(xiàn)的,確切的說是在wrapWithConnect內(nèi)定義的Connect組件里實現(xiàn)的。


    在項目中我使用的大store目錄結(jié)構(gòu)是:

    // index.js import {createStore, compose, applyMiddleware} from 'redux'; import thunk from 'redux-thunk'; import reducers from './reducers';const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; const store = createStore(reducers, composeEnhancers(applyMiddleware(thunk) ));export default store; 復(fù)制代碼// reducers.js // 合并小的reducer import {combineReducers} from 'redux-immutable'; // 提供的是immutable數(shù)據(jù) import {reducer as userReducer} from './user' import {reducer as chatUserReducer} from './chat_user' import {reducer as chatReducer} from './chat'const reducer = combineReducers({user: userReducer,chatUser: chatUserReducer,chat: chatReducer });export default reducer; 復(fù)制代碼

    在項目中我使用的小store(舉例)目錄結(jié)構(gòu)是:

    核心代碼:

    // _reducer.js import * as constants from './constants' import {getRedirectPath} from '../../common/js/util'const initState = {isAuth: false,msg: '',user: '',pwd: '',type: '' }const defaultState = (localStorage.getItem('jobUser') && JSON.parse(localStorage.getItem('jobUser'))) || initStateexport default (state = defaultState, action) => {switch (action.type) {case constants.AUTH_SUCCESS:localStorage.setItem('jobUser', JSON.stringify({...state,msg: '',redirectTo: getRedirectPath(action.payload), ...action.payload}))return {...state, msg: '', redirectTo: getRedirectPath(action.payload), ...action.payload}case constants.LOAD_DATA:return {...state, ...action.payload}case constants.ERROR_MSG:return {...state, isAuth: false, msg: action.msg}case constants.LOGIN_OUT:return {redirectTo: '/login', ...initState}default:return state} } 復(fù)制代碼// actionCreators.js import * as constants from './constants' import axios from 'axios' const authSuccess = (obj) => {const {pwd, ...data} = objreturn {type: constants.AUTH_SUCCESS, payload: data} } const errorMsg = (msg) => {return {msg, type: constants.ERROR_MSG} }// 注冊 export function register({user, pwd, repeatpwd, type}) {if (!user || !pwd || !type) {return errorMsg('用戶名密碼必須輸入')}if (pwd !== repeatpwd) {return errorMsg('密碼和確認密碼不同')}return dispatch => {axios.post('/user/register', {user, pwd, type}).then(res => {if (res.status === 200 && res.data.code === 0) {dispatch(authSuccess(res.data.data))} else {dispatch(errorMsg(res.data.msg))}})} }// 登錄 export function login({user, pwd}) {if (!user || !pwd) {return errorMsg('用戶名密碼必須輸入')}return dispatch => {axios.post('/user/login', {user, pwd}).then(res => {if (res.status === 200 && res.data.code === 0) {dispatch(authSuccess(res.data.data))} else {dispatch(errorMsg(res.data.msg))}})} }// 登出 export function logoutSubmit() {return {type: constants.LOGIN_OUT} }// 修改 export function update(data) {return dispatch => {axios.post('/user/update', data).then(res => {if (res.status === 200 && res.data.code === 0) {dispatch(authSuccess(res.data.data[0]))} else {dispatch(errorMsg(res.data.msg))}})} } 復(fù)制代碼// constants.js export const AUTH_SUCCESS = 'AUTH_SUCCESS' export const LOGIN_OUT = 'LOGIN_OUT' export const ERROR_MSG = 'ERROR_MSG' export const LOAD_DATA = 'LOAD_DATA' 復(fù)制代碼// index.js import reducer from './_reducer' import * as actionCreators from './actionCreators' import * as constants from './constants'export {reducer, actionCreators, constants} 復(fù)制代碼

    完整的 react --> redux --> react 流程

    一、Provider組件接受redux的store作為props,然后通過context往下傳。

    二、

  • connect函數(shù)在初始化的時候會將mapDispatchToProps對象綁定到store,

  • 如果mapDispatchToProps是函數(shù)則在Connect組件獲得store后,根據(jù)傳入的store.dispatch和action通過bindActionCreators進行綁定,再將返回的對象綁定到store,connect函數(shù)會返回一個wrapWithConnect函數(shù),同時wrapWithConnect會被調(diào)用且傳入一個ui組件,wrapWithConnect內(nèi)部使用class Connect extends Component定義了一個Connect組件,傳入的ui組件就是Connect的子組件,

  • 然后Connect組件會通過context獲得store,并通過store.getState獲得完整的state對象,將state傳入mapStateToProps返回stateProps對象、mapDispatchToProps對象或mapDispatchToProps函數(shù)會返回一個dispatchProps對象,stateProps、dispatchProps以及Connect組件的props三者通過Object.assign(),或者mergeProps合并為props傳入ui組件。然后在ComponentDidMount中調(diào)用store.subscribe,注冊了一個回調(diào)函數(shù)handleChange監(jiān)聽state的變化。

  • 三、

  • 此時ui組件就可以在props中找到actionCreator,當(dāng)我們調(diào)用actionCreator時會自動調(diào)用dispatch,在dispatch中會調(diào)用getState獲取整個state,同時注冊一個listener監(jiān)聽state的變化,store將獲得的state和action傳給combineReducers,
  • combineReducers會將state依據(jù)state的key值分別傳給子reducer,并將action傳給全部子reducer,reducer會被依次執(zhí)行進行action.type的判斷,如果有則返回一個新的state,如果沒有則返回默認。
  • combineReducers再次將子reducer返回的單個state進行合并成一個新的完整的state。此時state發(fā)生了變化。
  • dispatch在state返回新的值之后會調(diào)用所有注冊的listener函數(shù)其中包括handleChange函數(shù),handleChange函數(shù)內(nèi)部首先調(diào)用getState獲取新的state值并對新舊兩個state進行淺對比,如果相同直接return,如果不同則調(diào)用mapStateToProps獲取stateProps并將新舊兩個stateProps進行淺對比,如果相同,直接return結(jié)束,不進行后續(xù)操作。
  • 如果不相同則調(diào)用this.setState()觸發(fā)Connect組件的更新,傳入ui組件,觸發(fā)ui組件的更新,此時ui組件獲得新的props,react --> redux --> react 的一次流程結(jié)束。
  • 上面的有點復(fù)雜,簡化版的流程是:

    一、Provider組件接受redux的store作為props,然后通過context往下傳。

    二、connect函數(shù)收到Provider傳出的store,然后接受三個參數(shù)mapStateToProps,mapDispatchToProps和組件,并將state和actionCreator以props傳入組件,這時組件就可以調(diào)用actionCreator函數(shù)來觸發(fā)reducer函數(shù)返回新的state,connect監(jiān)聽到state變化調(diào)用setState更新組件并將新的state傳入組件。

    connect可以寫的非常簡潔,mapStateToProps,mapDispatchToProps只不過是傳入的回調(diào)函數(shù),connect函數(shù)在必要的時候會調(diào)用它們,名字不是固定的,甚至可以不寫名字。

    簡化版本:

    connect(state => state, action)(Component); 復(fù)制代碼

    redux以及react-redux到底是怎么實現(xiàn)的?

    總結(jié)

    下圖闡述了它們?nèi)咧g的工作流程:


    redux-thunk 中間件

    代碼示例:

    function createThunkMiddleware(extraArgument) {return ({ dispatch , getState }) => next => action=> {if (typeof action === ’ function ’){return action(dispatch , getState , extraArgument);}return next(action);} } const thunk= createThunkMiddleware(); export default thunk; 復(fù)制代碼

    ? 我們看 redux-thunk 這一串函數(shù)中最里層的函數(shù),也就是實際處理每個 action 對象的函數(shù)。 首先檢查參數(shù) action 的類型,如果是函數(shù)類型的話,就執(zhí)行這個 action 函數(shù),把dispatch 和 getState 作為參數(shù)傳遞進去,否則就調(diào)用 next 讓下一個中間件繼續(xù)處理 action,這個處理過程和 redux-thunk 文檔中描述的功能一致。

    ? Redux的單向數(shù)據(jù)流是同步操作,驅(qū)動 Redux 流程的 是 action 對象, 每一個 action對象被派發(fā)到 Store 上之后,同步地被分配給所有的 reducer 函數(shù),每個 reducer 都是純函數(shù),純函數(shù)不產(chǎn)生任何副作用,自然是完成數(shù)據(jù)操作之后立刻同步返回, reducer 返回的結(jié)果又被同步地拿去更新 Store 上的狀態(tài)數(shù)據(jù),更新狀態(tài)數(shù)據(jù)的操作會立刻被同步給監(jiān)聽Store 狀態(tài)改變的函數(shù),從而引發(fā)作為視圖的 React 組件更新過程。

    ? 當(dāng)我們想要讓 Redux 幫忙處理一個異步操作的時候,代碼一樣也要派發(fā)一個 action對象,畢竟 Redux 單向數(shù)據(jù)流就是由 action 對象驅(qū)動的 。 但是這個引發(fā)異步操作的action 對象比較特殊,我們叫它們“異步 action 對象” 。 ? 前面例子中的 action 構(gòu)造函數(shù)返回的都是一個普通的對象,這個對象包含若干字段,其中必不可少的字段是 type ,但是“異步 action 對象”不是一個普通 JavaScript 對象,而是一個函數(shù) 。 ? 如果沒有 redux-thunk 中間件的存在 這樣一個函數(shù)類型的 action 對象被派發(fā)出來會一路發(fā)送到各個 reducer 函數(shù), reducer 函數(shù)從這些實際上是函數(shù)的 action 對象上是無法獲得 type 字段的,所以也做不了什么實質(zhì)的處理。

    ? 不過,有了redux-thunk中間件之后,這些 action 對象根本沒有機會觸及到 reducer函數(shù),在中間件一層就被 redux-thunk 截獲 。

    ? redux-thunk 的工作是檢查 action 對象是不是函數(shù),如果不是函數(shù)就放行,完成普通action 對象的生命周期,而如果發(fā)現(xiàn) action 對象是函數(shù),那就執(zhí)行這個函數(shù),并把 Store的 dispatch 函數(shù)和 getState 函數(shù)作為參數(shù)傳遞到函數(shù)中去,處理過程到此為止,不會讓這個異步 action 對象繼續(xù)往前派發(fā)到 reducer 函數(shù) 。

    React中間件機制

    ? 在 Redux框架中,中間件處理的是 action 對象,而派發(fā) action 對象的就是 Store 上的dispatch 函數(shù),之前介紹過通過 dispatch 派發(fā)的 action 對象會進入 reducer 。 在 action 對象進入 reducer 之前,會經(jīng)歷中間件的管道 。

    ? 在這個中間件管道中,每個中間件都會接收到 action 對象,在處理完畢之后,就會把 action 對象交給下一個中間件來處理,只有所有的中間件都處理完 action 對象之后,在這個中間件管道中,每個中間件都會接收到 action 對象,在處理完畢之后,就會把 action 對象交給下一個中間件來處理,只有所有的中間件都處理完 action 對象之后,才輪到 reducer 來處理 action 對象,然而,如果某個中間件覺得沒有必要繼續(xù)處理這個action 對象了,就不會把 action 對象交給下一個中間件,對這個 action 對象的處理就此中止,也就輪不到 reducer 上場了 。

    ? 每個中間件必須要定義成一個函數(shù),返回一個接受 next 參數(shù)的函數(shù),而這個接受next 參數(shù)的函數(shù)又返回一個接受 action 參數(shù)的函數(shù) 。 next 參數(shù)本身也是一個函數(shù),中間件調(diào)用這個 next 函數(shù)通知 Redux 自己的處理工作已經(jīng)結(jié)束 。

    代碼舉例:

    // 一個實際上什么事都不做的中間件代碼如下: function doNothingMiddleware{{dispatch, getState)) {return function {next) {return function {action) {return next{action)}} } 復(fù)制代碼

    ? 以 action 為參數(shù)的函數(shù)對傳人的 action 對象進行處理,因為 JavaScript 支持閉包 ( Clousure ),在這個函數(shù)里可以訪問上面兩層函數(shù)的參數(shù),所以可以根據(jù)需要做很多事 情,包括以下功能:

    • 調(diào)用 dispatch 派發(fā)出一個新 action 對象;
    • 調(diào)用 getState 獲得當(dāng)前 Redux Store 上的狀態(tài);
    • 調(diào)用 next 告訴 Redux 當(dāng)前中間件工作完畢,讓 Redux 調(diào)用下一個中間件;
    • 訪問 action 對象 action 上的所有數(shù)據(jù)。 具有上面這些功能,一個中間件足夠獲取 Store 上的所有信息,也具有足夠能力控制數(shù)據(jù)的流轉(zhuǎn) 。

    中間件用于擴展 dispatch 函數(shù)的功能,多個中間件實際構(gòu)成了一個處理 action 對象的管道, action 對象被這個管道中所有中間件依次處理過之后,才有機會被 reducer 處理。

    ③ 起步

    上面說了react,react-router和redux的知識點。但是怎么樣將它們整合起來,搭建一個完整的項目。

    1、先引用 react.js,redux,react-router 等基本文件,建議用npm安裝,直接在文件中引用。

    2、從 react.js,redux,react-router 中引入所需要的對象和方法。

    import React, {Component, PropTypes} from 'react'; import ReactDOM, {render} from 'react-dom'; import {Provider, connect} from 'react-redux'; import {createStore, combineReducers, applyMiddleware} from 'redux'; import { Router, Route, Redirect, IndexRoute, browserHistory, hashHistory } from 'react-router'; 復(fù)制代碼

    3、根據(jù)需求創(chuàng)建頂層ui組件,每個頂層ui組件對應(yīng)一個頁面。

    4、創(chuàng)建actionCreators和reducers,并用combineReducers將所有的reducer合并成一個大的reduer。利用createStore創(chuàng)建store并引入combineReducers和applyMiddleware。

    5、利用connect將actionCreator,reuder和頂層的ui組件進行關(guān)聯(lián)并返回一個新的組件。

    6、利用connect返回的新的組件配合react-router進行路由的部署,返回一個路由組件Router。

    7、將Router放入最頂層組件Provider,引入store作為Provider的屬性。

    8、調(diào)用render渲染Provider組件且放入頁面的標(biāo)簽中。

    可以看到頂層的ui組件其實被套了四層組件,Provider,Router,Route,Connect,這四個組件并不會在視圖上改變react,它們只是功能性的。


    Github地址: wq93

    創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

    總結(jié)

    以上是生活随笔為你收集整理的学习React的一知半解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。