従来のコンポーネント間の通信と React コンポーネント間の通信の分析と比較 (コード例)

不言
リリース: 2018-09-17 15:57:21
オリジナル
1198 人が閲覧しました

この記事の内容は、従来のコンポーネント間の通信と React コンポーネント間の通信の分析と比較 (コード例) です。必要な方は参考にしていただければ幸いです。手伝う。

React の最小の論理単位はコンポーネントです。コンポーネント間に結合関係がある場合、この記事では React におけるコンポーネント通信のさまざまな方法を紹介します。任意のコンポーネント間の通信は、親子コンポーネント、祖父と孫コンポーネント、兄弟コンポーネント、および任意のコンポーネントの 4 つのタイプのコンポーネント間通信に分類できます。最初の 3 つは任意のコンポーネントとしても数えられることに注意してください。

#親コンポーネントと子コンポーネントの間の通信は、親コンポーネントから子コンポーネントへの通信と、親コンポーネントから子コンポーネントへの通信の 2 つの状況に分けられます。子コンポーネントから親コンポーネントへ まず、コンポーネントはサブコンポーネントと通信します。

従来のアプローチは、初期化中のパラメーターの受け渡しと、インスタンス段階でのメソッドの呼び出しの 2 つの状況に分けられます。

class Child {
    constructor(name) {
        // 获取dom引用
        this.$p = document.querySelector('#wp');

        // 初始化时传入name
        this.updateName(name);
    }
    updateName(name) {
        // 对外提供更新的api
        this.name = name;
    
        // 更新dom
        this.$p.innerHTML = name;
    }
}

class Parent {
    constructor() {
        // 初始化阶段
        this.child = new Child('yan');
        
        setTimeout(() => {
            // 实例化阶段
            this.child.updateName('hou');
        }, 2000);
    }
}
ログイン後にコピー
React では、この 2 つの状況はすべてプロパティを通じて均一に処理されます。その理由は、プロパティが更新されたときに React がサブコンポーネントを自動的に再レン​​ダリングするためです。 #次の例では、サブコンポーネントは 2 秒後に自動的に再レン​​ダリングされ、新しいプロパティ値を取得します

class Child extends Component {
    render() {
        return <p>{this.props.name}</p>
    }
}

class Parent extends Component {
    constructor() {
        // 初始化阶段
        this.state = {name: 'yan'};

        setTimeout(() => {
            // 实例化阶段
            this.setState({name: 'hou'})
        }, 2000);
    }
    render() {
        return <Child name={this.state.name} />
    }
}
ログイン後にコピー

サブコンポーネントが親コンポーネントと通信する方法を見てみましょう。従来の方法が 2 つあります。 1 つはコールバック関数で、もう 1 つはサブコンポーネントのメッセージ インターフェイスをデプロイすることです。


まずコールバック関数を見てみましょう。たとえば、コールバック関数の利点は次のとおりです。欠点は、初期化中に渡す必要があり、取り消すことができないことと、渡せる関数が 1 つだけであることです。

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 <p></p>
    }
}

class Parent extends Component {
    render() {
        return <Child cb={() => {console.log('update')}} />
    }
}
ログイン後にコピー
祖父と孫コンポーネントの例を見てみましょう。 父と息子のコンポーネントは、実際には孫と孫のコンポーネントの特殊なケースと見なすことができます。ここでの孫と孫のコンポーネントは、祖父と孫を指すだけでなく、一般に先祖と子孫コンポーネント間の通信を分離することができます。親と子コンポーネント間の通信の問題は、階層ごとに属性を渡すというリダクション方法によって簡単に解決できます。は、複数の親子コンポーネント間の通信の問題に分解されます。

レイヤーごとの転送の利点は、非常にシンプルであり、既存の知識で解決できることです。問題は、それが無駄になることです。コードが多く、非常に面倒です。ブリッジとして機能する中間のコンポーネントは、独自のプロパティに属さない多くのコンポーネントを導入します。

React では、祖先コンポーネントはコンテキストを通じて子孫コンポーネントにプロパティを直接渡すことができます。これは、スタートレックのワームホールに似ています。コンテキストの特別なブリッジを介して、コンポーネントがメッセージを渡すために任意のレベルを越えることができます。

通信する必要があるコンポーネント間でこのワームホールを開くにはどうすればよいでしょうか。双方向の宣言が必要です。つまり、祖先コンポーネントで属性を宣言し、子孫コンポーネントで属性を再度宣言し、その後、子孫コンポーネントで属性を読み取ることができます。 example

import PropTypes from 'prop-types';

class Child extends Component {
    // 后代组件声明需要读取context上的数据
    static contextTypes = {
        text: PropTypes.string
    }
    render() {
        // 通过this.context 读取context上的数据
        return <p>{this.context.text}</p>
    }
}


class Ancestor extends Component {
    // 祖先组件声明需要放入context上的数据
    static childContextTypes = {
        text: PropTypes.string
    }
    // 祖先组件往context放入数据
    getChildContext() {
        return {text: 'yanhaijing'}
    }
}
ログイン後にコピー
context の利点は、レイヤーごとの送信の手間を省き、双方向の宣言によってデータの可視性を制御できることです。ただし、欠点は次のとおりです。これもグローバル変数と同様に明らかですが、制御しないと簡単に混乱を引き起こす可能性があり、また、名前の重複の問題も発生しやすくなります。

私の個人的な提案は、一部の読み取り専用情報を共有することです。すべてのコンポーネントは、ログインしているユーザー情報などのコンテキストを使用して転送できます。

ヒント: React Router のルーティングは、コンテキストを通じてルーティング属性を渡すことです

兄弟コンポーネント

2 つのコンポーネントが兄弟である場合、親コンポーネントは 2 つのコンポーネント間で通信するためのブリッジとして使用されます。これは実際にはメイン モジュール モードです。

次の例では、2 つのコンポーネントが通信します。サブコンポーネントは、親コンポーネントを通じてデジタル同期を表示する機能を実現します。

class Parent extends Component {
    constructor() {
        this.onChange = function (num) {
            this.setState({num})
        }.bind(this);
    }
    render() {
        return (
            <p>
                <Child1 num={this.state.num} onChange={this.onChange}>
                <Child2 num={this.state.num} onChange={this.onChange}>
            </p>
        );
    }
}
ログイン後にコピー
メイン モジュール パターンの利点は、2 つのサブコンポーネント間の結合関係をサブコンポーネントとサブコンポーネント間の結合に分離することです。分散したものを収集する利点は非常に明白であり、保守性と拡張性が向上します。

任意のコンポーネント

任意のコンポーネントには、上記の 3 つの関係コンポーネントが含まれている必要があります。上記で紹介したメソッドを優先してください。通信には、共通祖先メソッド、メッセージ ミドルウェア、状態管理の 3 つの方法があります。

基于我们上面介绍的爷孙组件和兄弟组件,只要找到两个组件的共同祖先,就可以将任意组件之间的通信,转化为任意组件和共同祖先之间的通信,这个方法的好处就是非常简单,已知知识就能搞定,缺点就是上面两种模式缺点的叠加,除了临时方案,不建议使用这种方法

另一种比较常用的方法是消息中间件,就是引入一个全局消息工具,两个组件通过这个全局工具进行通信,这样两个组件间的通信,就通过全局消息媒介完成了

还记得上面介绍的消息基类吗?下面的例子中,组件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等工具,本文不展开介绍,有机会后面单独成文,这些都是用来解决不同问题的,只要根据自己的场景选择合适的工具就好。

以上が従来のコンポーネント間の通信と React コンポーネント間の通信の分析と比較 (コード例)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
関連するチュートリアル
人気のおすすめ
最新のコース
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート