<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                &emsp;&emsp;根據組件之間的嵌套關系(即層級關系)可分為4種通信方式:父子、兄弟、跨級和無級。 ## 一、父子通信 &emsp;&emsp;在React中,數據是自頂向下單向流動的,而父組件通過props向子組件傳遞需要的信息是組件之間最常見的通信方式,如下代碼所示,父組件Parent向子組件Child傳遞了一個name屬性,其值為一段字符串“strick”。 ~~~js class Parent extends React.Component { render() { return <Child name="strick">子組件</Child>; } } class Child extends React.Component { render() { return <input name={this.props.name} type="text" />; } } ~~~ &emsp;&emsp;當需要子組件向父組件傳遞信息時,也能通過組件的props實現,只是要多傳一個回調函數,如下所示。 ~~~js class Parent extends React.Component { callback(value) { console.log(value); //輸出從子組件傳遞過來的值 } render() { return <Child callback={this.callback} />; } } class Child extends React.Component { constructor(props) { super(props); this.state = { name: "" }; } handle(e) { this.props.callback(e.target.value); //調用父組件的回調函數 this.setState({ name: e.target.value }); //更新文本框中的值 } render() { return <input value={this.state.name} type="text" onChange={this.handle.bind(this)} />; } } ~~~ &emsp;&emsp;父組件Parent會傳給子組件Child一個callback()方法,子組件中的文本框注冊了一個onChange事件,在事件處理程序handle()中將回調父組件的callback()方法,并把文本框的值傳遞過去,以此達到反向通信的效果。 ## 二、兄弟通信 &emsp;&emsp;當兩個組件擁有共同的父組件時,就稱它們為兄弟組件,注意,它們可以不在一個層級上,如圖6所示,C與D或E都是兄弟關系。 :-: ![](https://img.kancloud.cn/94/97/94977caa098df65392b115251266d383_303x303.png =200x) :-: 圖6 組件樹 &emsp;&emsp;兄弟之間不能直接通信,需要借助狀態提升的方式間接實現信息的傳遞,即把組件之間要共享的狀態提升至最近的父組件中,由父組件來統一管理。而任意一個兄弟組件可通過從父組件傳來的回調函數更新共享狀態,新的共享狀態再通過父組件的props回傳給子組件,從而完成一次兄弟之間的通信。在下面的例子中,會有兩個文本框(如圖7所示),當向其中一個輸入數字時,鄰近的文本框會隨之改變,要么加一,要么減一。 :-: ![](https://img.kancloud.cn/d4/7e/d47e85cbb2d26c7114f7c23790655947_714x86.gif =400x) 圖7 兩個文本框 ~~~js class Parent extends React.Component { constructor(props) { super(props); this.state = { type: "p", digit: 0 }; this.plus = this.plus.bind(this); this.minus = this.minus.bind(this); } plus(digit) { this.setState({ type: "p", digit }); } minus(digit) { this.setState({ type: "m", digit }); } render() { let { type, digit } = this.state; let pdigit = type == "p" ? digit : (digit+1); let mdigit = type == "m" ? digit : (digit-1); return ( <> <Child type="p" digit={pdigit} onDigitChange={this.plus} /> <Child type="m" digit={mdigit} onDigitChange={this.minus} /> </> ); } } class Child extends React.Component { constructor(props) { super(props); this.handle = this.handle.bind(this); } handle(e) { this.props.onDigitChange(+e.target.value); } render() { return ( <input value={this.props.digit} type="text" onChange={this.handle} /> ); } } ~~~ &emsp;&emsp;上面代碼實現了一次完整的兄弟之間的通信,具體過程如下所列。 &emsp;&emsp;(1)首先在父組件Parent中定義兩個兄弟組件Child,其中type屬性為“p”的子組件用于遞增,綁定了plus()方法;type屬性為“m”的子組件用于遞減,綁定了minus()方法。 &emsp;&emsp;(2)然后在子組件Child中接收傳遞過來的digit屬性和onDigitChange()方法,前者會作為文本框的值,后者會在事件處理程序onChange()中被調用。 &emsp;&emsp;(3)如果在遞增文本框中修改數值,那么就將新值傳給plus()方法。遞減文本框的處理過程與之類似,只是將plus()方法替換成minus()方法。 &emsp;&emsp;(4)最后更新父組件中的兩個狀態:type和digit,完成信息的傳遞。 ## 三、跨級通信 &emsp;&emsp;在一棵組件樹中,當多個組件需要跨級通信時,所處的層級越深,那么需要過渡的中間層就越多,完成一次通信將變得非常繁瑣,而在數據傳遞過程中那些作為橋梁的組件,其代碼也將變得冗余且臃腫。 &emsp;&emsp;在React中,還可用Context實現跨級通信。Context能存放組件樹中需要全局共享的數據,也就是說,一個組件可以借助Context跨越層級直接將數據傳遞給它的后代組件。如圖8所示,左邊的數據會通過組件的props逐級顯式地傳遞,右邊的數據會通過Context讓所有組件都可訪問。 :-: ![](https://img.kancloud.cn/d2/24/d2242b886e85a235140fa9494bf47b03_1300x642.png =500x) 圖8 props和context &emsp;&emsp;隨著React v16.3的發布,引入了一種全新的Context,修正了舊版本中較為棘手的問題,接下來的篇幅將著重分析這兩個版本的Context。 **1)舊的Context** &emsp;&emsp;在舊版本的Context中,首先要在頂層組件內添加getChildContext()方法和靜態屬性childContextTypes,前者用于生成一個context對象(即初始化Context需要攜帶的數據),后者通過[prop-types庫](https://www.cnblogs.com/strick/p/10569909.html)限制該對象的屬性的數據類型,兩者缺一不可。在下面的示例中,Grandpa是頂層組件,Son是中間組件,要傳遞的是一個包含name屬性的對象。 ~~~js //頂層組件 class Grandpa extends React.Component { getChildContext() { return { name: "strick" }; } render() { return <Son />; } } Grandpa.childContextTypes = { name: PropTypes.string }; //中間組件 class Son extends React.Component { render() { return <Grandson />; } } ~~~ &emsp;&emsp;然后給后代組件(例如下面的Grandson)添加靜態屬性contextTypes,限制要接收的屬性的數據類型,最后就能通過讀取this.context得到由頂層組件提供的數據。 ~~~js class Grandson extends React.Component { render() { return <p>{this.context.name}</p>; } } Grandson.contextTypes = { name: PropTypes.string }; ~~~ &emsp;&emsp;從上面的示例中可以看出,跨級通信的準備工作并不簡單,需要在兩處做不同的配置。React官方建議慎用舊版的Context,因為它相當于JavaScript中的全局變量,容易造成數據流混亂、重名覆蓋等各種副作用,并且在未來的React版本中有可能被廢棄。 &emsp;&emsp;雖然在功能上Context實現了跨級通信,但本質上數據還是像props一樣逐級傳遞的,因此如果某個中間組件的shouldComponentUpdate()方法返回false的話,就會阻止下層的組件更新Context中的數據。接下來會演示這個致命的缺陷,沿用上一個示例,對兩個組件做些調整。在Grandpa組件中,先讓Context保存組件的name狀態,再新增一個按鈕,并為其注冊一個能更新組件狀態的點擊事件;在Son組件中,添加shouldComponentUpdate()方法,它的返回值是false。在把Grandpa組件掛載到DOM中后,點擊按鈕就能發現Context的更新傳播終止于Son組件。 ~~~js class Grandpa extends React.Component { constructor(props) { super(props); this.state = { name: "strick" }; this.click = this.click.bind(this); } getChildContext() { return { name: this.state.name }; } click() { this.setState({ name: "freedom" }); } render() { return ( <> <Son /> <button onClick={this.click}>提交</button> </> ); } } class Son extends React.Component { shouldComponentUpdate() { return false; } render() { return <Grandson />; } } ~~~ **2)新的Context** &emsp;&emsp;這個版本的Context不僅采用了更符合React風格的聲明式寫法,還可以直接將數據傳遞給后代組件而不用逐級傳遞,一舉沖破了shouldComponentUpdate()方法的限制。下面仍然使用上一節的三個組件,完成一次新的跨級通信。 ~~~js const NameContext = React.createContext({name: "strick"}); class Grandpa extends React.Component { render() { return ( <NameContext.Provider value={{name: "freedom"}}> <Son /> </NameContext.Provider> ); } } class Son extends React.Component { render() { return <Grandson />; } } class Grandson extends React.Component { render() { return ( <NameContext.Consumer>{context => <p>{context.name}</p>}</NameContext.Consumer> ); } } ~~~ &emsp;&emsp;通過上述代碼可知,新的Context由三部分組成: &emsp;&emsp;(1)React.createContext()方法,接收一個可選的defaultValue參數,返回一個Context對象(例如NameContext),包含兩個屬性:Provider和Consumer,它們是一對相呼應的組件。 &emsp;&emsp;(2)Provider,來源組件,它的value屬性就是要傳送的數據,Provider可關聯多個來自于同一個Context對象的Consumer,像NameContext.Provider只能與NameContext.Consumer配合使用。 &emsp;&emsp;(3)Consumer,目標組件,出現在Provider之后,可接收一個返回React元素的函數,如果Consumer能找到對應的Provider,那么函數的參數就是Provider的value屬性,否則就讀取defaultValue的值。 &emsp;&emsp;注意,Provider組件會通過Object.is()對其value屬性的新舊值做比較,以此確定是否更新作為它后代的Consumer組件。 ## 四、無級通信 &emsp;&emsp;當兩個沒有嵌套關系(即無級)的組件需要通信時,可以借助消息隊列實現。下面是一個用觀察者模式實現的簡易消息隊列庫,其處理過程類似于事件系統,如果將消息看成事件,那么訂閱消息就是綁定事件,而發布消息就是觸發事件。 ~~~js class EventEmitter { constructor() { this.events = {}; } sub(event, listener) { //訂閱消息 if (!this.events[event]) { this.events[event] = { listeners: [] }; } this.events[event].listeners.push(listener); } pub(name, ...params) { //發布消息 for (const listener of this.events[name].listeners) { listener.apply(this, params); } } } ~~~ &emsp;&emsp;EventEmitter只包含了三個方法,它們的功能如下所列: &emsp;&emsp;(1)構造函數,初始化了一個用于緩存各類消息的容器。 &emsp;&emsp;(2)sub()方法,將回調函數用消息名稱分類保存。 &emsp;&emsp;(3)pub()方法,依次執行了指定名稱下的消息集合。 &emsp;&emsp;下面用一個示例演示無級通信,在Sub組件的構造函數中,會訂閱一次消息,消息名稱為"TextBox",回調函數會接收一個參數,并將其輸出到控制臺。 ~~~js let emitter = new EventEmitter(); class Sub extends React.Component { constructor(props) { super(props); emitter.sub("TextBox", value => console.log(value)); } render() { return <p>訂閱消息</p>; } } ~~~ &emsp;&emsp;在下面的Pub組件中,為文本框注冊了onChange事件,在事件處理程序handle()中發布名為"TextBox"的消息集合,并將文本框中的值作為參數傳遞到回調函數中。 ~~~js class Pub extends React.Component { constructor(props) { super(props); this.state = { value: "" }; } handle(e) { const value = e.target.value; emitter.pub("TextBox", value); this.setState({ value }); } render() { return <input value={this.state.value} onChange={this.handle.bind(this)} />; } } ~~~ &emsp;&emsp;Sub組件和Pub組件會像下面這樣,以兄弟的關系掛載到DOM中。當修改文本框中的內容時,就會觸發消息的發布,從而完成了一次它們之間的通信。 ~~~js ReactDOM.render( <> <Sub /> <Pub /> </>, document.getElementById("container") ); ~~~ &emsp;&emsp;當業務邏輯復雜到一定程度時,普通的消息隊列可能就捉襟見肘了,此時可以考慮引入Mobx、Redux等專門的狀態管理工具來實現組件之間的通信。 ***** > 原文出處: [博客園-React躬行記](https://www.cnblogs.com/strick/category/1455720.html) [知乎專欄-React躬行記](https://zhuanlan.zhihu.com/pwreact) 已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎瀏覽。 ![](https://box.kancloud.cn/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200) 推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看