ホームページ > 記事 > ウェブフロントエンド > React コンポーネントを構築する最も完全な方法
私は React を使うのがとても好きです。その最大の利点は十分にシンプルなことだと思うからです。 シンプルと簡単には違いがあります。つまり、React も簡単です。もちろん、核となる内容をマスターすれば、他のことはすべて理解できるようになるまでには時間がかかります。難しい部分については以下で説明します。
これらの指標 (結合と凝集) は、プログラミングの習慣を変える上で多かれ少なかれ課題をもたらします。これらはクラスベースのオブジェクト指向プログラミングでよく使用されます。 React コンポーネントを作成するときにも、同じルールを参照して適用します。
カップリングとは、要素間の相互接続と依存関係を指します。 1 つの要素を変更し、別の要素を同期的に更新する必要がある場合、これを密結合と呼びます。疎結合とは、1 つの要素が変更されたときに、別の要素を変更する必要がないことを意味します。たとえば、銀行振込金額関数を示します。表示される金額が為替レート計算に依存している場合、内部換算構造が変更されると表示されるコードが更新されます。要素インターフェイスに基づいて疎結合システムを設計する場合、要素の変更はビュー レイヤの表示に影響しません。明らかに、疎結合コンポーネントは管理と制御が容易です。
凝集性とは、コンポーネントが 1 つのことのみを担当するかどうかです。この指標は、単一の原則と Unix の原則に従っています。つまり、1 つのことに焦点を当て、それをうまく実行します。口座残高の書式設定された表示で、関連する為替レートの計算と履歴の表示権限が利用可能かどうかの確認が必要な場合、これには多くの機能上の責任が含まれており、これらの機能は相互に依存しません。おそらく、許可の処理と為替レートは別のコンポーネントであるはずです。一方、複数のコンポーネント (整数部、小数部、通貨表示用) があり、プログラマーが残高を表示したい場合は、組み立てるすべてのコンポーネントを見つける必要があります。課題は、凝集性の高いコンポーネントを作成することです。
コンポーネントを作成するにはさまざまな方法があります。私たちはコンポーネントを適度な範囲で再利用できるようにしたいと考えています。また、大きなコンポーネント内で使用できる小さなコンポーネントも構築したいと考えています。理想的には、システムの管理と拡張が容易になるように、疎結合で高度に集約されたコンポーネントを構築したいと考えています。 React コンポーネントの Props は関数のパラメーターに似ており、ステートレス関数を持つコンポーネントとみなすこともできます。コンポーネント内でプロパティをどのように定義するか、またコンポーネントをどのように再利用できるかを考える必要があります。
次に、経費管理システムを背景として使用し、経費の詳細な形式を分析し、コンポーネントの構築方法を紹介します:
type Expense { description: string category: string amount: number doneAt: moment }
モデルによると、経費形式には次のようなプログラム モデリング方法があります。
小道具はありません
経費オブジェクトを渡します
必要な属性を渡します
すべての属性のマップを渡します
フォーマットされたサブオブジェクトを渡します
以下について議論します上記の配送方法を使用した場合のメリットとデメリットを個別に説明します。ただし、使用シナリオや依存するシステムに応じて、上記のいずれかの方法を使用する場合は常に注意を払う必要があります。それが私たちもやっていることであり、適切に抽象的なシーンを構築します。
これは最も単純な解決策であり、多くの場合、静的データを書き込むコンポーネントを構築します。
const ExpenseDetails = () => ( <p className='expense-details'> <p>Category: <span>Food</span></p> <p>Description: <span>Lunch</span></p> <p>Amount: <span>10.15</span></p> <p>Date: <span>2017-10-12</span></p> </p> )
props を渡さないと柔軟性がなくなり、コンポーネントは 1 つのシーンでのみ使用できます。経費詳細の例では、最初にコンポーネントがいくつかの小道具を受け入れる必要があることがわかります。ただし、シナリオによっては、小道具を持たないことも良い解決策となります。まず、商標、ロゴ、会社情報など、簡単に変更できないコンテンツをプロパティに含むコンポーネントを使用できます。
const Logo = () => ( <p className='logo'> <img src='/logo.png' alt='DayOne logo'/> </p> )
コンポーネントをできるだけ小さく書くと、システムの保守が容易になります。情報を 1 か所に保管し、変更する必要があるのは 1 か所だけです。複数の場所に重複したコードを記述しないでください。
経費の詳細が決定したら、データをコンポーネントに渡す必要があります。まず、経費オブジェクトを渡す必要があります。
const ExpenseDetails = ({ expense }) => ( <p className='expense-details'> <p>Category: <span>{expense.category}</span></p> <p>Description: <span>{expense.description}</span></p> <p>Amount: <span>{expense.amount}</span></p> <p>Date: <span>{expense.doneAt}</span></p> </p> )
経費オブジェクトを経費詳細コンポーネントに渡すのは理にかなっています。経費詳細形式は一貫性が高く、経費データが表示されます。形式を変更する必要がある場合は、ここが変更できる唯一の場所です。経費詳細の形式を変更しても、経費オブジェクト自体に副作用はありません。
このコンポーネントは経費オブジェクトと密接に結合されています。これは悪いことですか?もちろんそうではありませんが、これがシステムにどのような影響を与えるかを認識する必要があります。 オブジェクトを小道具として渡すと、経費詳細コンポーネントは経費の内部構造に依存します。経費の内部構造を変更する場合は、経費詳細コンポーネントを変更する必要があります。もちろん、変更する必要があるのは 1 か所だけです。
このデザインは将来の変化にどのように適応しますか? フィールドを追加、変更、または削除する場合、変更する必要があるコンポーネントは 1 つだけです。別の書式設定されたカレンダー表示を追加する必要がある場合はどうすればよいでしょうか?カレンダーの書式設定用の新しいプロパティを追加できます。
りー
我们开始增加属性来使组件更加灵活。如果只有几个选项,那么一切都是很ok的。系统业务开始扩展后问题就来了,在不同的场景下我们需要维护大量的props。
const ExpenseDetails = ({ expense, dateFormat, withCurrency, currencyFormat, isOverdue, isPaid ... })
增加props可以使得组件重用性更好,但你可能设计了多重功能职责的组件。这种规则也同样在函数写法中运用。可以用多个参数来创建一个函数,当参数的数目超过3个或者4个时候,意味着这个函数在做很多事情了,也许这时候应该将函数拆成更小的函数来的更加简单。
随着组件props的增加,我们将其拆分成定义明确的组件,比如:OverdueExpenseDetails, PaidExpenseDetails等。
为了减少对象自身的内容,我们可以只传递必要的属性值。
const ExpenseDetails = ({ category, description, amount, date }) => ( <p className='expense-details'> <p>Category: <span>{category}</span></p> <p>Description: <span>{description}</span></p> <p>Amount: <span>{amount}</span></p> <p>Date: <span>{date}</span></p> </p> )
我们分别传递属性,这样我们将一部分工作责任转移给了组件使用者。如果费用的内部结构发生变化,他将不会影响费用明细的格式化。但可能影响每个使用组件的地方,因为我们可能需要修改props。当我们以独立的属性传递props时候,一个组件将更加抽象了。
只传递需要的字段对未来设计改动是如何影响的?增加、更新或者删除字段将不会很容易。无论何时我们要增加一个字段,我们不仅仅要改变费用细节的实现,也需要改变每个使用组件的地方。另外一个方面,支持多种日历格式化几乎是现成的,我们可以传递日历作为prop,也可以传递格式化后的日历。
<ExpenseDetails category={expense.category} description={expense.description} amount={expense.amount} date={expense.doneAt.format('YYYY-MM-DD')} />
决定如何展示特定的字段应该在掌握在具体使用组件的人手中,这将不是费用明细组件关心的内容。
为了达到组件抽象化,我们可以传递一个map的属性值。
const ExpenseDetails = ({ expense }) => ( <p class='expense-details'> { _.reduce(expense, (acc, value, key) => { acc.push(<p>{key}<span>{value}</span></p>) }, []) } </p> )
使用组件的人控制费用明细的格式化,传递给组件的对象格式则必须正确。
const expense = { "Category": "Food", "Description": "Lunch", "Amount": 10.15, "Date": 2017-10-12 }
这个方案有很多缺陷,我们很难控制组件展示的样式,并且展示顺序也没有指定。因此,如果我们需要某种顺序的话,可以采用array代替map来解决这个问题。但是仍然还有缺陷。
传递map 和array作为props 不与费用耦合,也根本与它不一致。增加和删除新属性虽然只改变了prop,但是我们无法控制组件本身的格式。如果我们只改变类别的格式化,这不是一个可行的办法。(确切地说,总有一个办法来解决,例如,传递另外一个格式化后的props。这个解决方案似乎不再简单了。)
我们也可以只通过直接传对一个子对象,这样就能考虑更少的组件内需要如何展示。
const ExpenseDetails = ({ children }) => ( <p class='expense-details'> { children } </p> )
在这种情况下,费用明细只是一个提供结构和样式的容器。展示所有信息则是使用组件的人必须提供的。
<ExpenseDetails> <p>Category: <span>{expense.category}</span></p> <p>Description: <span>{expense.description}</span></p> <p>Amount: <span>{expense.amount}</span></p> <p>Date: <span>{expense.doneAt}</span></p> </ExpenseDetails>
在费用明细这个案例中,我们需要重复许多工作,因此这也许不是一个好的解决方案。尽管如此,灵活性则是巨大的,因为有可能有很多不同的格式化操作。增删改只需要改变使用组件时候传入的值。日期格式也是一样的,我们虽然失去了功能内聚的特点,但这也是我们不得不付出的代价。
正如你所看到,我们讨论了它们的不同优缺点和可能性。哪一个最好呢,这取决于:
项目本身
项目阶段
组件自身,需要很多特殊的组件组成还是只需要简单的一些选项值
自己的习惯
使用环境-适合频繁的改变和被多次使用
没有一个万能的解决方案,一个方案也并不能适用所有场景。我们如何构建组件对于系统的维护和系统可扩展方面有着深远的影响。这完全依赖于组件所使用的环境。非常幸运的是,我们有很多可使用的方案。组件是功能的抽象集合,它既能构建小系统也能构建大系统。所以这仅仅只是一个选择问题。
相关推荐:
以上がReact コンポーネントを構築する最も完全な方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。