この記事では、フロントエンドのレスポンシブ プログラミング ソリューションとその欠点について詳しく説明します (コード付き)。必要な方は参考にしていただければ幸いです。
例えば、現実世界の多くの部分は、他の人からの質問を受け取り、それに応じた答えを返します。開発プロセスでは、他の人にインスピレーションを与えることを期待して、レスポンシブ デザインを多く適用し、経験を積みました。
リアクティブ プログラミングと通常のプログラミングの考え方の主な違いは、リアクティブ プログラミングはプッシュ方式で動作するのに対し、ノンリアクティブ プログラミングはプル方式で動作することです。たとえば、イベントは非常に一般的なリアクティブ プログラミングです:
button.on('click', () => { // ...})
非リアクティブな方法では、次のようになります:
while (true) { if (button.clicked) { // ... } }
明らかに、コードの優雅さと実行効率の両方の点で。 、非レスポンシブメソッドはレスポンシブデザインほど優れていません。
イベント エミッター
イベント エミッターは、ほとんどの人がよく知っているイベント実装であり、非常にシンプルで実用的です。次のような非同期検索などの単純なレスポンシブ デザインを実装できます。 Event Emitter の実装には多くの欠点があり、componentWillUnmount でリソースを手動で解放する必要があることがわかります。たとえば、検索時に複数のデータソースを集約する必要がある場合、表現力が不十分です:
class Input extends Component { state = { value: '' } onChange = e => { this.props.events.emit('onChange', e.target.value) } afterChange = value => { this.setState({ value }) } componentDidMount() { this.props.events.on('onChange', this.afterChange) } componentWillUnmount() { this.props.events.off('onChange', this.afterChange) } render() { const { value } = this.state return ( <input value={value} onChange={this.onChange} /> ) } } class Search extends Component { doSearch = (value) => { ajax(/* ... */).then(list => this.setState({ list })) } componentDidMount() { this.props.events.on('onChange', this.doSearch) } componentWillUnmount() { this.props.events.off('onChange', this.doSearch) } render() { const { list } = this.state return ( <ul> {list.map(item => <li key={item.id}>{item.value}</li>)} </ul> ) } }
明らかに開発効率は非常に低いです。
ReduxRedux では、イベント ストリームを使用して応答性を実装します。Reducer は純粋な関数である必要があるため、応答性を実装する唯一の方法はサブスクリプションまたはミドルウェアを使用することです。
ストアをサブスクライブした場合、Redux はどのデータが変更されたかを正確に取得できないため、ダーティ チェックのみを使用できます。例:
class Search extends Component { foo = '' bar = '' doSearch = () => { ajax({ foo, bar }).then(list => this.setState({ list })) } fooChange = value => { this.foo = value this.doSearch() } barChange = value => { this.bar = value this.doSearch() } componentDidMount() { this.props.events.on('fooChange', this.fooChange) this.props.events.on('barChange', this.barChange) } componentWillUnmount() { this.props.events.off('fooChange', this.fooChange) this.props.events.off('barChange', this.barChange) } render() { // ... } }
watcher(store)
このメソッドには 2 つの欠点があります。1 つは、データが複雑でデータ量が比較的大きい場合に効率の問題が発生することです。もう 1 つは、mapState 関数が依存する場合です。文脈上、それは非常に困難です。 React-reduxでは、connect関数のmapStateToPropsの2番目のパラメータはpropsであり、必要なコンテキストを取得するために上位コンポーネントを介してPropsを渡すことができますが、この方法ではリスナーはReactコンポーネントとなり、コンポーネントとしてマウントされます。この応答性をコンポーネントから独立させたい場合、問題が発生します。
もう 1 つの方法は、ミドルウェアのデータ変更を監視することです。 Redux の設計のおかげで、特定のイベント (アクション) をリッスンすることで、対応するデータ変更を取得できます。
function createWatcher(mapState, callback) { let previousValue = null return (store) => { store.subscribe(() => { const value = mapState(store.getState()) if (value !== previousValue) { callback(value) } previousValue = value }) } }const watcher = createWatcher(state => { // ...}, () => { // ...})
この方法でほとんどの問題は解決できますが、Redux ではミドルウェアとリデューサーが実際にすべてのイベント (アクション) を暗黙的にサブスクライブします。これは明らかに不合理ですが、パフォーマンスの問題はまったく問題ありません。
オブジェクト指向の応答性ECMASCRIPT 5.1 ではゲッターとセッターが導入されており、ゲッターとセッターを通じて応答性を実装できます。
const search = () => (dispatch, getState) => { // ...}const middleware = ({ dispatch }) => next => action => { switch action.type { case 'FOO_CHANGE': case 'BAR_CHANGE': { const nextState = next(action) // 在本次dispatch完成以后再去进行新的dispatch setTimeout(() => dispatch(search()), 0) return nextState } default: return next(action) } }
Mobx と Vue はこのメソッドを使用して応答性を実装します。もちろん、互換性を考慮しない場合は Proxy を使用することもできます。
複数の値に応答して新しい値を取得する必要がある場合、Mobx でこれを行うことができます:
class Model { _foo = '' get foo() { return this._foo } set foo(value) { this._foo = value this.search() } search() { // ... } }// 当然如果没有getter和setter的话也可以通过这种方式实现class Model { foo = '' getFoo() { return this.foo } setFoo(value) { this.foo = value this.search() } search() { // ... } }
Mobx は、実行時に時間が依存する値を収集し、これらの値が変更されたときに(トリガー セッター) 時間の値の再計算は、明らかに EventEmitter のアプローチよりもはるかに便利で効率的であり、Redux のミドルウェアよりも直感的です。
ただし、ここには欠点もあります。getter に基づいて計算された属性は y = f(x) の状況しか記述できません。ただし、実際には f は非同期関数であるため、y = await になります。 f(x) については、この状況はゲッターでは説明できません。
この状況では、Mobx が提供する autorun を通じて実装できます:
class Model { @observable hour = '00' @observable minute = '00' @computed get time() { return `${this.hour}:${this.minute}` } }
ランタイムの依存関係収集プロセスは完全に暗黙的であるため、ここでよく発生する問題は、予期しない依存関係の収集です:
class Model { @observable keyword = '' @observable searchResult = [] constructor() { autorun(() => { // ajax ... }) } }
明らかに、ここでの読み込みは行われるべきではありません。この問題に対処するために、追加のコードが追加されますが、追加のコードによりエラーが発生する可能性が高くなります。 あるいは、必須フィールドを手動で指定することもできますが、この方法ではいくつかの追加操作が必要です:
class Model { @observable loading = false @observable keyword = '' @observable searchResult = [] constructor() { autorun(() => { if (this.loading) { return } // ajax ... }) } }
そして、タイムラインを記述する必要がある場合、Mobx はそれを行うことが多少できません。たとえば、次のように遅延させる必要があります。 5 数秒以内に再度検索します。
関連する推奨事項:
レスポンシブ バックエンドのレスポンシブ フロントエンド フレームワーク バージョンをパズルで正式にリリース_html/css_WEB-ITnose非常にシンプルなレスポンシブ フロントエンド開発フレームワークを使用する_html/css_WEB-ITnose以上がフロントエンドの応答性プログラミング ソリューションとその欠点の詳細な紹介 (コード付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。