摘要:同時吸取了社區大量優秀思想,進行歸納比對。有興趣的讀者可以點擊下面的鏈接購買,再次感謝各位的支持與鼓勵懇請各位批評指正京東當當原文網址
在React中最小的邏輯單元是組件,組件之間如果有耦合關系就會進行通信,本文將會介紹React中的組件通信的不同方式
通過歸納范,可以將任意組件間的通信歸類為四種類型的組件間通信,分別是父子組件,爺孫組件,兄弟組件和任意組件,
需要注意的是前三個也可以算作任意組件的范疇,所以最后一個是萬能方法
父子組件間的通信分為父組件向子組件通信和子組件向父組件通信兩種情況,下面先來介紹父組件向子組件通信,
傳統做法分為兩種情況,分別是初始化時的參數傳遞和實例階段的方法調用,例子如下
class Child { constructor(name) { // 獲取dom引用 this.$div = document.querySelector("#wp"); // 初始化時傳入name this.updateName(name); } updateName(name) { // 對外提供更新的api this.name = name; // 更新dom this.$div.innerHTML = name; } } class Parent { constructor() { // 初始化階段 this.child = new Child("yan"); setTimeout(() => { // 實例化階段 this.child.updateName("hou"); }, 2000); } }
在React中將兩個情況統一處理,全部通過屬性來完成,之所以能夠這樣是因為React在屬性更新時會自動重新渲染子組件,
下面的例子中,2秒后子組件會自動重新渲染,并獲取新的屬性值
class Child extends Component { render() { return{this.props.name}} } class Parent extends Component { constructor() { // 初始化階段 this.state = {name: "yan"}; setTimeout(() => { // 實例化階段 this.setState({name: "hou"}) }, 2000); } render() { return} }
下面來看一下子組件如何向父組件通信,傳統做法有兩種,一種是回調函數,另一種是為子組件部署消息接口
先來看回調函數的例子,回調函數的優點是非常簡單,缺點就是必須在初始化的時候傳入,并且不可撤回,并且只能傳入一個函數
class Child { constructor(cb) { // 調用父組件傳入的回調函數,發送消息 setTimeout(() => { cb() }, 2000); } } class Parent { constructor() { // 初始化階段,傳入回調函數 this.child = new Child(function () { console.log("child update") }); } }
下面來看看消息接口方法,首先需要一個可以發布和訂閱消息的基類,比如下面實現了一個簡單的EventEimtter,實際生產中可以直接使用別人寫好的類庫,比如@jsmini/event,子組件繼承消息基類,就有了發布消息的能力,然后父組件訂閱子組件的消息,即可實現子組件向父組件通信的功能
消息接口的優點就是可以隨處訂閱,并且可以多次訂閱,還可以取消訂閱,缺點是略顯麻煩,需要引入消息基類
// 消息接口,訂閱發布模式,類似綁定事件,觸發事件 class EventEimtter { constructor() { this.eventMap = {}; } sub(name, cb) { const eventList = this.eventMap[name] = this.eventMap[name] || {}; eventList.push(cb); } pub(name, ...data) { (this.eventMap[name] || []).forEach(cb => cb(...data)); } } class Child extends EventEimtter { constructor() { super(); // 通過消息接口發布消息 setTimeout(() => { this.pub("update") }, 2000); } } class Parent { constructor() { // 初始化階段,傳入回調函數 this.child = new Child(); // 訂閱子組件的消息 this.child.sub("update", function () { console.log("child update") }); } }
Backbone.js就同時支持回調函數和消息接口方式,但React中選擇了比較簡單的回調函數模式,下面來看一下React的例子
class Child extends Component { constructor(props) { setTimeout(() => { this.props.cb() }, 2000); } render() { return } } class Parent extends Component { render() { return爺孫組件{console.log("update")}} /> } }
父子組件其實可以算是爺孫組件的一種特例,這里的爺孫組件不光指爺爺和孫子,而是泛指祖先與后代組件通信,可能隔著很多層級,我們已經解決了父子組件通信的問題,根據化歸法,很容易得出爺孫組件的答案,那就是層層傳遞屬性么,把爺孫組件通信分解為多個父子組件通信的問題
層層傳遞的優點是非常簡單,用已有知識就能解決,問題是會浪費很多代碼,非常繁瑣,中間作為橋梁的組件會引入很多不屬于自己的屬性
在React中,通過context可以讓祖先組件直接把屬性傳遞到后代組件,有點類似星際旅行中的蟲洞一樣,通過context這個特殊的橋梁,可以跨越任意層次向后代組件傳遞消息
怎么在需要通信的組件之間開啟這個蟲洞呢?需要雙向聲明,也就是在祖先組件聲明屬性,并在后代組件上再次聲明屬性,然后在祖先組件上放上屬性就可以了,就可以在后代組件讀取屬性了,下面看一個例子
import PropTypes from "prop-types"; class Child extends Component { // 后代組件聲明需要讀取context上的數據 static contextTypes = { text: PropTypes.string } render() { // 通過this.context 讀取context上的數據 return{this.context.text}} } class Ancestor extends Component { // 祖先組件聲明需要放入context上的數據 static childContextTypes = { text: PropTypes.string } // 祖先組件往context放入數據 getChildContext() { return {text: "yanhaijing"} } }
context的優點是可以省去層層傳遞的麻煩,并且通過雙向聲明控制了數據的可見性,對于層數很多時,不失為一種方案;但缺點也很明顯,就像全局變量一樣,如果不加節制很容易造成混亂,而且也容易出現重名覆蓋的問題
個人的建議是對一些所有組件共享的只讀信息可以采用context來傳遞,比如登錄的用戶信息等
小貼士:React Router路由就是通過context來傳遞路由屬性的
兄弟組件如果兩個組件是兄弟關系,可以通過父組件作為橋梁,來讓兩個組件之間通信,這其實就是主模塊模式
下面的例子中,兩個子組件通過父組件來實現顯示數字同步的功能
class Parent extends Component { constructor() { this.onChange = function (num) { this.setState({num}) }.bind(this); } render() { return (); } }
主模塊模式的優點就是解耦,把兩個子組件之間的耦合關系,解耦成子組件和父組件之間的耦合,把分散的東西收集在一起好處非常明顯,能帶來更好的可維護性和可擴展性
任意組件任意組件包括上面的三種關系組件,上面三種關系應該優先使用上面介紹的方法,對于任意的兩個組件間通信,總共有三種辦法,分別是共同祖先法,消息中間件和狀態管理
基于我們上面介紹的爺孫組件和兄弟組件,只要找到兩個組件的共同祖先,就可以將任意組件之間的通信,轉化為任意組件和共同祖先之間的通信,這個方法的好處就是非常簡單,已知知識就能搞定,缺點就是上面兩種模式缺點的疊加,除了臨時方案,不建議使用這種方法
另一種比較常用的方法是消息中間件,就是引入一個全局消息工具,兩個組件通過這個全局工具進行通信,這樣兩個組件間的通信,就通過全局消息媒介完成了
還記得上面介紹的消息基類嗎?下面的例子中,組件1和組件2通過全局event進行通信
class EventEimtter { constructor() { this.eventMap = {}; } sub(name, cb) { const eventList = this.eventMap[name] = this.eventMap[name] || {}; eventList.push(cb); } pub(name, ...data) { (this.eventMap[name] || []).forEach(cb => cb(...data)); } } // 全局消息工具 const event = new EventEimtter; // 一個組件 class Element1 extends Component { constructor() { // 訂閱消息 event.sub("element2update", () => {console.log("element2 update")}); } } // 另一個組件。 class Element2 extends Component { constructor() { // 發布消息 setTimeout(function () { event.pub("element2update") }, 2000) } }
消息中間件的模式非常簡單,利用了觀察者模式,將兩個組件之間的耦合解耦成了組件和消息中心+消息名稱的耦合,但為了解耦卻引入全局消息中心和消息名稱,消息中心對組件的侵入性很強,和第三方組件通信不能使用這種方式
小型項目比較適合使用這種方式,但隨著項目規模的擴大,達到中等項目以后,消息名字爆炸式增長,消息名字的維護成了棘手的問題,重名概率極大,沒有人敢隨便刪除消息信息,消息的發布者找不到消息訂閱者的信息等
其實上面的問題也不是沒有解決辦法,重名的問題可以通過制定規范,消息命名空間等方式來極大降低沖突,其他問題可以通過把消息名字統一維護到一個文件,通過對消息的中心化管理,可以讓很多問題都很容易解決
如果你的項目非常大,上面兩種方案都不合適,那你可能需要一個狀態管理工具,通過狀態管理工具把組件之間的關系,和關系的處理邏輯從組建中抽象出來,并集中化到統一的地方來處理,Redux就是一個非常不錯的狀態管理工具
除了Redux,還有Mobx,Rematch,reselect等工具,本文不展開介紹,有機會后面多帶帶成文,這些都是用來解決不同問題的,只要根據自己的場景選擇合適的工具就好了
總結組件間的關系千變萬化,都可以用上面介紹的方法解決,對于不同規模的項目,應該選擇適合自己的技術方案,上面介紹的不同方式解耦的程度是不一樣的,關于不同耦合關系的好壞,可以看我之前的文章《圖解7種耦合關系》
本文節選自我的新書《React 狀態管理與同構實戰》,感興趣的同學可以繼續閱讀本書,這本書由我和前端自身技術侯策合力打磨,凝結了我們在學習、實踐 React 框架過程中的積累和心得。除了 React 框架使用介紹以外,著重剖析了狀態管理以及服務端渲染同構應用方面的內容。同時吸取了社區大量優秀思想,進行歸納比對。
本書受到百度公司副總裁沈抖、百度高級前端工程師董睿,以及知名JavaScript語言專家阮一峰、Node.js布道者狼叔、Flarum中文社區創始人 justjavac、新浪移動前端技術專家小爝、知乎知名博主顧軼靈等前端圈眾多專家大咖的聯合力薦。
有興趣的讀者可以點擊下面的鏈接購買,再次感謝各位的支持與鼓勵!懇請各位批評指正!
京東:https://item.jd.com/12403508....
當當:http://product.dangdang.com/2...
原文網址:http://yanhaijing.com/javascr...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/97845.html
摘要:組件間通信父組件中放入子組件將自身屬性與方法傳給子組件子組件即父組件的一部分所以只要父組件重新子組件也必定重新子組件接收父組件傳過來的屬性與方法調用父組件方法為父組件中的父組件子組件調用子組件自己的方法調用父組件方法父組件向子組件通信數 組件間通信 1.父組件中放入子組件,將自身屬性與方法傳給子組件,子組件即父組件的一部分,所以只要父組件重新render,子組件也必定重新render....
摘要:可以看到,父級更新了,子組件也同步更新了,也就完成了父組件向子組件發起通信的目的。當然我們也可以通過適當的組件設計來避免過深的組件嵌套通信。所以這就解決了遠程組件通信深度組件嵌套的問題。組件間通信可以選擇像這樣的庫來處理。 0x000 概述 這一章講組件間通信,組件通信分為很多種: 父組件向子組件發起通信 子組件向父組件發起通信 兄弟組件間通訊 遠程組件通信 在組件通信中,有一種錯...
摘要:本身提供哪幾種通信方式首先靈感源于,支持雙向綁定,本質還是單向數據流。跟一樣,組件間最基本的數據流是通過向子組件傳遞數據。但是在卻很少使用,因為組件可以自定義事件,即后面的組件間通信方式其實就是訂閱發布模式。 例子是在 jsrun.net 平臺編寫,不支持移動端平臺,所以本文建議在 PC 端進行閱讀。 Vue 是數據驅動的視圖框架,那么組件間的數據通信是必然的事情,那么組件間如何進行數...
摘要:父組件聲明自己支持父組件提供一個函數,用來返回相應的對象子組件聲明自己需要使用我胡漢三又回來了點擊我如果是父組件向子組件單向通信,可以使用變量,如果子組件想向父組件通信,同樣可以由父組件提供一個回調函數,供子組件調用,回傳參數。 在使用 React 的過程中,不可避免的需要組件間進行消息傳遞(通信),組件間通信大體有下面幾種情況: 父組件向子組件通信 子組件向父組件通信 跨級組件之間...
摘要:首次發表在個人博客需要組件之進行通信的幾種情況父組件向子組件通信子組件向父組件通信跨級組件通信沒有嵌套關系組件之間的通信父組件向子組件通信數據流動是單向的父組件向子組件通信也是最常見的父組件通過向子組件傳遞需要的信息子組件向父組件通信利用 showImg(https://segmentfault.com/img/remote/1460000012361466?w=1240&h=667)...
閱讀 2571·2021-08-20 09:38
閱讀 1354·2019-08-30 15:43
閱讀 592·2019-08-29 17:13
閱讀 1601·2019-08-29 14:01
閱讀 1314·2019-08-29 13:29
閱讀 2321·2019-08-23 18:29
閱讀 2046·2019-08-23 17:51
閱讀 1914·2019-08-23 17:16