ホームページ  >  記事  >  ウェブフロントエンド  >  React の全体的なプロセスは何ですか? React の全体的なプロセスの紹介 (例付き)

React の全体的なプロセスは何ですか? React の全体的なプロセスの紹介 (例付き)

寻∝梦
寻∝梦オリジナル
2018-09-11 12:04:261779ブラウズ

この記事では主にreactの全体的なプロセスを紹介します

reactのコンポーネント化

Reactのコンポーネントは明らかにdom viewとstate dataで構成されており、2つの部分は別個のものです。状態はデータセンターであり、その状態によってビューの状態が決まります。このとき、私たちがいつも賞賛してきた MVC 開発モデルとは少し異なるようで、コントローラーがなければユーザーとの対話をどのように処理し、誰がデータの変更を管理するのかということに気づきました。ただし、これは React が考慮すべきことではなく、UI のレンダリングのみを担当します。データをリッスンして動的に dom を変更する他のフレームワークとは異なり、react は setState を使用してビューの更新を制御します。 setState は自動的に render 関数を呼び出して、ビューの再レンダリングをトリガーします。setState を呼び出さずに状態データに変更があるだけの場合は、更新はトリガーされません。 コンポーネントとは独立した機能を持つビューモジュールであり、小さなコンポーネントが多数集まって大きなコンポーネントを構成し、ページ全体がコンポーネントで構成されます。その利点は再利用とメンテナンスです。

reactの差分アルゴリズム

reactの差分アルゴリズムはどこで使用されていますか?コンポーネントが更新されると、react は新しい仮想 DOM ツリーを作成し、以前に保存された DOM ツリーと比較します。このプロセスでは diff アルゴリズムが使用されるため、コンポーネントの初期化時には使用されません。 React は、同じコンポーネントは同様の構造を持ち、異なるコンポーネントは異なる構造を持つと仮定します。この仮定に基づいて、レイヤーごとの比較を実行し、対応するノードが異なることが判明した場合は、古いノードとそれに含まれるすべての子ノードを直接削除し、新しいノードに置き換えます。同一ノードの場合は属性のみ変更されます。

リストの差分アルゴリズムは少し異なります。リストは通常​​、同じ構造を持っているため、リスト ノードを削除、挿入、並べ替える場合、単一ノードの全体的な操作は、各置換を 1 つずつ比較するよりもはるかに優れています。リストの場合、react が誰が誰であるかを認識できるようにキー値を設定する必要があります。もちろん、キー値を書き込まなくても問題ありませんが、これにより通常は警告が表示され、react のパフォーマンスを向上させるためにキー値を追加するよう通知されます。

反応コンポーネントはどのようにして生まれたのですか?

コンポーネントの作成メソッドは React.createClass() です - クラスを作成します。反応システムは内部でクラスシステムを設計し、それを使用して反応コンポーネントを作成します。ただし、これは必須ではありません。es6 クラスを使用してコンポーネントを作成することもできます。これは、Facebook によって公式に推奨されている記述方法でもあります。


2 つの記述メソッドは同じ機能を実現しますが、原則が異なります。es6 クラスのクラスはコンストラクターの構文糖とみなされ、extends が実装されます。クラス間の継承 - React.Component のすべてのプロパティとメソッドを継承するクラス Main を定義します。これがコンポーネントのライフサイクル関数の由来です。コンストラクターは、オブジェクトをインスタンス化するときに呼び出されるコンストラクターです。Super は、親クラスのコンストラクターを呼び出して親クラスのインスタンス オブジェクトを作成し、サブクラスのコンストラクターを使用してそれを変更します。これは、es5 のプロトタイプ継承とは異なります。プロトタイプ継承では、まずインスタンス化されたオブジェクトが作成され、次に親のプロトタイプ メソッドが継承されます。これを理解すると、コンポーネントを見るときがより明確になります。

コンポーネント を使用すると、実際には Main クラス (new Main) のインスタンス化になりますが、react はこのプロセスをカプセル化してラベルのように見せます。 (さらに詳しく知りたい場合は、PHP 中国語 Web サイト

React Reference Manual のコラムにアクセスして学習してください)

注意すべき点が 3 つあります: 1. 定義されたクラス名の最初の文字は大文字にする必要があります。 2. クラスにはキーワードになる場合、クラス セレクターには代わりに className を使用する必要があります。 3. クラスとモジュールはデフォルトで strict モードを使用するため、use strict で実行モードを指定する必要はありません。

コンポーネントのライフサイクル

コンポーネントは、初期化中に 5 つのフック関数をトリガーします:

1、getDefaultProps()

デフォルトのプロパティを設定する dufaultProps を使用してコンポーネントのデフォルトのプロパティを設定することもできます。

2. getInitialState()

es6 クラス構文を使用する場合、コンストラクターで this.state を直接定義できます。 this.props にアクセスできるようになりました。

3.componentWillMount()

は、コンポーネントが初期化されるときにのみ呼び出されます。このとき、コンポーネントが更新されるときは呼び出されません。変更することができます。

4. render()

React の最も重要なステップ、仮想 dom の作成、diff アルゴリズムの実行、dom ツリーの更新はすべてここで行われます。この時点では、状態を変更することはできません。

5. コンポーネントがレンダリングされた後に、componentDidMount()

が呼び出されます。これは、一度だけ呼び出されます。

は、更新時に 5 つのフック関数もトリガーします:

6、componentWillReceivePorps(nextProps)

これは、コンポーネントの初期化時には呼び出されませんが、コンポーネントが新しい props を受け入れるときに呼び出されます。

7. shouldComponentUpdate(nextProps, nextState)

反応パフォーマンスの最適化の非常に重要な部分です。コンポーネントが新しい状態またはプロパティを受け入れるときに呼び出されます。比較の前後の 2 つのプロパティと状態が同じであるかどうかを設定できます。同じ属性の状態は確実に同じものを生成するため、更新を防ぐために false を返します。 DOM ツリーなので、新しい DOM ツリーを作成し、古い DOM ツリーを diff アルゴリズムと比較する必要はありません。これにより、特に DOM 構造が複雑な場合に、パフォーマンスが大幅に節約されます。ただし、this.forceUpdate を呼び出すと、この手順はスキップされます。いいえ、もっと言います

10. コンポーネントの初期化時には、componentDidUpdate()

は呼び出されません。このとき、コンポーネントの更新が完了した後に呼び出されます。

アンインストールフック機能もあります

11. コンポーネントがアンマウントされる直前に、componentWillUnmount()

が呼び出されます。この時点で、一部のイベント リスナーとタイマーをクリアする必要があります。

上記のことから、React には合計 10 個の周期関数 (レンダリングは 1 回繰り返されます) があることがわかります。これらの 10 個の関数は、うまく使用すると、開発効率とコンポーネントのパフォーマンスを向上させることができます。 。

react-router

Router と Route は React のコンポーネントであり、内部ルーティング ルールを作成し、一致するルーティング アドレスに従って対応するコンポーネントを表示するだけの設定オブジェクトです。 Route にはルーティング アドレスとコンポーネントをバインドするネスト機能があり、ルーティング アドレスの包含関係を示します。これはコンポーネント間のネストとは直接関係しません。 Route はバインドされたコンポーネントに 7 つの属性 (children、history、location、params、route、routeParams、routes) を渡すことができます。各属性にはルーティング関連の情報が含まれます。よく使われるのは、子(ルーティングの包含関係で区別される構成要素)、ロケーション(アドレス、パラメータ、アドレス切り替え方式、キー値、ハッシュ値を含む)です。 act-router は、a タグをカプセル化した Link タグを提供します。react-router は、リンクをクリックしてジャンプすることをデフォルトの方法ではなく、pushState を使用してジャンプします。ハッシュ値を変更します。ページを切り替えるプロセスでは、[リンク] ラベルまたは [戻る] ボタンをクリックすると、最初に URL アドレスが変更され、ルーターはアドレスの変更を監視し、ルートのパス属性に従って対応するコンポーネントと照合して状態を変更します。 value を対応するコンポーネントに渡し、setState を呼び出すと、render 関数がトリガーされて dom が再レンダリングされます。

ページ数が多い場合、特に単一ページのアプリケーションでは、初期のレンダリング速度が非常に遅くなり、オンデマンドでロードする必要があります。ページに切り替えるときに、対応する js ファイルを読み込みます。 webpackでロードオンデマンドに反応する方法は非常に簡単で、Routeのコンポーネントを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>
    <route>
        <indexroute></indexroute>//首页
        <route></route>
        <route></route>//帮助中心
        <route></route>//销售记录
        <redirect></redirect>
    </route>
</router>
);

コンポーネント間の通信

React は上から下にデータを転送する一方向のデータ フローを推奨しますが、ボトムアップ間の通信や、同じデータ フロー上にないコンポーネント間の通信は複雑になります。通信の問題を解決するには、親子関係のみがある場合、親がコールバック関数を属性として子に渡し、子がその関数を直接呼び出して親と通信することができます。

コンポーネント レベルが深くネストされている場合、Context を使用して情報を渡すことができます。これにより、層ごとに関数を渡す必要がなく、this.context を通じてどのレベルの子にも直接アクセスできます。

兄弟関係にあるコンポーネントは相互に直接通信できません。転送ステーションとしては同じ層の上位のみを使用できます。兄弟コンポーネントが最上位コンポーネントである場合、それらが通信できるようにするには、その外側のコンポーネントがデータを保存し、情報を送信する役割を果たします。これが実際の redux です。物事をします。

コンポーネント間の情報は、グローバル イベントを通じて渡すこともできます。別のページではパラメーターを介してデータを渡すことができ、次のページでは location.query を使用してデータを取得できます。

まず redux について話しましょう:

redux は主に、store、reducer、action の 3 つの部分で構成されます。

store はオブジェクトであり、次の 4 つの主要なメソッドがあります:

1.

用于action的分发——在createStore中可以用middleware中间件对dispatch进行改造,比如当action传入dispatch会立即触发reducer,有些时候我们不希望它立即触发,而是等待异步操作完成之后再触发,这时候用redux-thunk对dispatch进行改造,以前只能传入一个对象,改造完成后可以传入一个函数,在这个函数里我们手动dispatch一个action对象,这个过程是可控的,就实现了异步。

2、subscribe:

监听state的变化——这个函数在store调用dispatch时会注册一个listener监听state变化,当我们需要知道state是否变化时可以调用,它返回一个函数,调用这个返回的函数可以注销监听。
let unsubscribe = store.subscribe(() => {console.log('state发生了变化')})

3、getState:

获取store中的state——当我们用action触发reducer改变了state时,需要再拿到新的state里的数据,毕竟数据才是我们想要的。getState主要在两个地方需要用到,一是在dispatch拿到action后store需要用它来获取state里的数据,并把这个数据传给reducer,这个过程是自动执行的,二是在我们利用subscribe监听到state发生变化后调用它来获取新的state数据,如果做到这一步,说明我们已经成功了。

4、replaceReducer:

替换reducer,改变state修改的逻辑。

store可以通过createStore()方法创建,接受三个参数,经过combineReducers合并的reducer和state的初始状态以及改变dispatch的中间件,后两个参数并不是必须的。store的主要作用是将action和reducer联系起来并改变state。

action是一个对象,其中type属性是必须的,同时可以传入一些数据。action可以用actionCreactor进行创造。dispatch就是把action对象发送出去。

reducer是一个函数,它接受一个state和一个action,根据action的type返回一个新的state。根据业务逻辑可以分为很多个reducer,然后通过combineReducers将它们合并,state树中有很多对象,每个state对象对应一个reducer,state对象的名字可以在合并时定义。

像这个样子:

const reducer = combineReducers({
     a: doSomethingWithA,
     b: processB,
     c: c
})

combineReducers其实也是一个reducer,它接受整个state和一个action,然后将整个state拆分发送给对应的reducer进行处理,所有的reducer会收到相同的action,不过它们会根据action的type进行判断,有这个type就进行处理然后返回新的state,没有就返回默认值,然后这些分散的state又会整合在一起返回一个新的state树。

接下来分析一下整体的流程,首先调用store.dispatch将action作为参数传入,同时用getState获取当前的状态树state并注册subscribe的listener监听state变化,再调用combineReducers并将获取的state和action传入。combineReducers会将传入的state和action传给所有reducer,reducer会根据state的key值获取与自己对应的state,并根据action的type返回新的state,触发state树的更新,我们调用subscribe监听到state发生变化后用getState获取新的state数据。

redux的state和react的state两者完全没有关系,除了名字一样。

上面分析了redux的主要功能,那么react-redux到底做了什么?

react-redux

如果只使用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中任何组件都可以通过contex获取store。也就意味着我们可以在任何一个组件里利用dispatch(action)来触发reducer改变state,并用subscribe监听state的变化,然后用getState获取变化后的值。但是并不推荐这样做,它会让数据流变的混乱,过度的耦合也会影响组件的复用,维护起来也更麻烦。

connect --connect(mapStateToProps, mapDispatchToProps, mergeProps, options)是一个函数,它接受四个参数并且再返回一个函数--wrapWithConnect,wrapWithConnect接受一个组件作为参数wrapWithConnect(component),它内部定义一个新组件Connect(容器组件)并将传入的组件(ui组件)作为Connect的子组件然后return出去。

所以它的完整写法是这样的:connect(mapStateToProps, mapDispatchToProps, mergeProps, options)(component)

mapStateToProps(state, [ownProps]):

mapStateToProps 接受两个参数,store的state和自定义的props,并返回一个新的对象,这个对象会作为props的一部分传入ui组件。我们可以根据组件所需要的数据自定义返回一个对象。ownProps的变化也会触发mapStateToProps

function mapStateToProps(state) {
   return { todos: state.todos };
}

mapDispatchToProps(dispatch, [ownProps]):

mapDispatchToProps如果是对象,那么会和store绑定作为props的一部分传入ui组件。如果是个函数,它接受两个参数,bindActionCreators会将action和dispatch绑定并返回一个对象,这个对象会和ownProps一起作为props的一部分传入ui组件。所以不论mapDispatchToProps是对象还是函数,它最终都会返回一个对象,如果是函数,这个对象的key值是可以自定义的

function mapDispatchToProps(dispatch) {
   return {
      todoActions: bindActionCreators(todoActionCreators, dispatch),
      counterActions: bindActionCreators(counterActionCreators, dispatch)
   };
}

mapDispatchToProps返回的对象其属性其实就是一个个actionCreator,因为已经和dispatch绑定,所以当调用actionCreator时会立即发送action,而不用手动dispatch。ownProps的变化也会触发mapDispatchToProps。

mergeProps(stateProps, dispatchProps, ownProps):

mapStateToProps() と mapDispatchToProps() によって返されたオブジェクトとコンポーネント自体の props を新しい props にマージし、コンポーネントに渡します。デフォルトでは、Object.assign({}, ownProps, stateProps,dispatchProps) の結果が返されます。

オプション:

pure = true は、Connect コンテナコンポーネントがストアの状態と shouldComponentUpdate 内の ownProps の浅い比較を実行して、変更が発生したかどうかを判断し、パフォーマンスを最適化することを意味します。 false の場合、比較は実行されません。

実際、connect は何も行いません。ほとんどのロジックは、それが返す WrapWithConnect 関数に実装されます。正確には、これは、wrapWithConnect で定義された Connect コンポーネントに実装されます。

以下は完全な React --> redux --> React プロセスです:

1. Provider コンポーネントは redux ストアを props として受け入れ、コンテキストを通じてそれを渡します。

2. connect 関数は、初期化中に mapDispatchToProps オブジェクトをストアにバインドします。mapDispatchToProps が関数の場合、Connect コンポーネントがストアを取得した後、受信した store.dispatch とアクションに従って、bindActionCreators を通じてバインドされます。返されたオブジェクトはストアにバインドされます。connect 関数は、wrapWithConnect 関数を返します。同時に、wrapWithConnect が呼び出され、ui コンポーネントが渡されます。wrapWithConnect は、Connect extends クラスを内部的に使用します。 コンポーネントは Connect コンポーネントを定義します。その後、Connect コンポーネントはコンテキストを通じてストアを取得し、store.getState を通じて完全な状態オブジェクトを取得し、stateProps オブジェクト、mapDispatchToProps を返します。 object または mapDispatchToProps 関数は、dispatchProps オブジェクトを返します。stateProps、dispatchProps、および Connect コンポーネントの props は、Object.assign() または mergeProps を通じて props にマージされ、ui コンポーネントに渡されます。次に、ComponentDidMount で store.subscribe を呼び出し、コールバック関数 handleChange を登録して、状態の変化を監視します。

3. このとき、ui コンポーネントは props 内で actionCreator を見つけることができ、dispatch では自動的に getState が呼び出され、状態全体が取得されます。ストアは状態の変更をリッスンするために登録され、アクションは CombineReducers に渡され、状態のキー値に従ってサブ Reducers にアクションが渡されます。 -reducers。reducers は action.type を判断するために順番に実行され、存在する場合は新しい状態が返され、存在しない場合はデフォルトに戻ります。 combineReducers は、子 Reducer によって返された個々の状態を新しい完全な状態に再度結合します。この時点で状態が変化しました。 Connect コンポーネントで呼び出されるサブスクライブは、状態の変化をリッスンしてから、handleChange 関数を呼び出します。内部的には、handleChange 関数は最初に getState を呼び出して新しい状態の値を取得し、古い状態と新しい状態の浅い比較を実行します。異なる場合は、mapStateToProps を呼び出し、古い状態と新しい状態を簡単に比較します。それらが同じでない場合は、this.setState() を呼び出して Connect コンポーネントの更新をトリガーし、ui コンポーネントを渡して、ui コンポーネントの更新をトリガーします。このとき、ui コンポーネントは新しい props を取得して反応します。 -->redux -->React の 1 回限りのプロセスが終了します。

上記は少し複雑ですが、プロセスの簡略化されたバージョンは次のとおりです:

1. Provider コンポーネントは redux ストアを props として受け入れ、コンテキストを通じてそれを渡します。

2. connect 関数は Provider からストアを受け取り、mapStateToProps、mapDispatchToProps、コンポーネントの 3 つのパラメーターを受け取り、state と actionCreator を props としてコンポーネントに渡します。このとき、コンポーネントは actionCreator 関数を呼び出してリデューサーをトリガーできます。関数を使用して新しい状態を返し、connect で状態の変化を監視し、setState を呼び出してコンポーネントを更新し、新しい状態をコンポーネントに渡します。

Connect は非常に簡潔に記述できます。mapStateToProps と mapDispatchToProps は渡されるコールバック関数です。名前は固定されておらず、名前を記述する必要さえありません。

簡易版: connect(state => state, action)(Component);

プロジェクト構築

上記は、react、react-router、redux に関する知識ポイントです。しかし、それらを統合して完全なプロジェクトを構築する方法は次のとおりです。

1. まず、react.js、redux、react-router などの基本的なファイルを引用します。npm でインストールし、ファイル内で直接引用することをお勧めします。

2.react.js、redux、react-router から必要なオブジェクトとメソッドを導入します。

「react」から React、{Component, PropTypes} をインポートします。 applyMiddleware} from 'redux';
import { Router, Route, Redirect, IndexRoute, browserHistory, hashHistory } from 'react-router';

3. 要件に従ってトップレベル ui コンポーネントを作成し、各トップレベル ui コンポーネントを作成します。ページに相当します。

4. actionCreators と Reducer を作成し、combineReducers を使用してすべての Reducer を 1 つの大きな Reducer にマージします。 createStore を使用してストアを作成し、combineReducers と applyMiddleware を導入します。

5. connect を使用して、actionCreator、reuder を最上位の u​​i コンポーネントに関連付け、新しいコンポーネントを返します。

6. connect によって返された新しいコンポーネントを使用して、react-router と連携してルーティングをデプロイし、ルーティング コンポーネント Router を返します。

7. Router を最上位コンポーネント Provider に配置し、Provider の属性として Store を導入します。

8. render を呼び出して Provider コンポーネントをレンダリングし、ページのタグに配置します。

トップレベルの ui コンポーネントは実際には、プロバイダー、ルーター、ルート、接続の 4 つのコンポーネント層でカバーされていることがわかります。これらの 4 つはビューに変更を加えず、機能するだけです。

通常、トップレベルの ui コンポーネントで props を出力すると、一連のプロパティが表示されます。

上の図のトップレベルの ui コンポーネントには合計 18 のプロパティがあります。反応すると、これらのプロパティがどこから来たのか混乱するかもしれません。実際、これらの属性は 5 つの場所から来ます: React の全体的なプロセスは何ですか? React の全体的なプロセスの紹介 (例付き)

コンポーネントの 1 つのカスタム属性、actionCreator によって返される 6 つのオブジェクト、reducer によって返される 4 つの状態オブジェクト、 Connect コンポーネント、および Router によって挿入される 7 つの属性。

react で遭遇する落とし穴といくつかの小さな知識ポイントをまとめます

React を使用するときによく遭遇するさまざまな問題は、遭遇する問題について混乱して無力に感じることになるでしょう。 次に、一般的な問題を分析しましょう。反応する際の注意点など。

1. setState() は非同期です。

this.setState() は render メソッドを呼び出しますが、state の値は render メソッドで割り当てられます。したがって、this.setState()実行直後に取得したstateの値は変化しません。レンダー関数が呼び出されないため、同様の状態の直接割り当ては更新をトリガーしません。

2. コンポーネントのライフサイクル
componentWillMount とcomponentDidMount は初期化中にのみ呼び出されます。

componentWillReceivePorps、 shouldComponentUpdate、componentWillUpdata、componentDidUpdate は、初期化中ではなく、コンポーネントが更新されたときにのみ呼び出されます。


3. Reducer はコンポーネントの更新を開始するために新しいオブジェクトを返す必要があります
connect 関数では、新しい状態と古い状態が軽く比較されるため、状態の値が変化するだけで参照アドレスが変化しない場合、connect は考慮します。それらは同じであり、更新をトリガーするものではありません。

4. リデューサーによって返される状態が変化するかどうかに関係なく、subscribe に登録されているすべてのコールバック関数がトリガーされます。

5. コンポーネント名の最初の文字は大文字にする必要があります。これがクラス名の標準です。

6. コンポーネントをアンインストールする前に、dom 要素に追加されたリスニング イベントとタイマーを手動でクリアする必要があります。これらは React の制御範囲内にないため、手動でクリアする必要があります。

7. オンデマンドでロードするときにコンポーネントがデフォルトのエクスポートを通じて公開される場合、デフォルトを require.ensure に追加する必要があります。

require.ensure([], require => {
    cb(null, require('../Component/saleRecord').default)
},'saleRecord')

8、react的路由有hashHistory和browserHistory,hashHistory由hash#控制跳转,一般用于正式线上部署,browserHistory就是普通的地址跳转,一般用于开发阶段。

9、标签里用到的,for 要写成htmlFor,因为for已经成了关键字。

10、componentWillUpdate中可以直接改变state的值,而不能用setState。

11、如果使用es6class类继承react的component组件,constructor中必须调用super,因为子类需要用super继承component的this,否则实例化的时候会报错。

本篇文章到这就结束了(想看更多就到PHP中文网React使用手册栏目中学习),有问题的可以在下方留言提问。

以上がReact の全体的なプロセスは何ですか? React の全体的なプロセスの紹介 (例付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。