我非常喜歡使用React,因為我覺得它最大優點就是夠簡單。 在簡單和容易之間還是存在區別 的,我的意思是React也很簡單。當然你需要一些時間來了解它,當你掌握其核心內容後,其他的事都是水到渠成的了。下文將介紹比較困難的部分。
這些指標(耦合&內聚)或多或少的給我們改變程式設計習慣帶了挑戰。它們經常被運用在類別形式的物件導向程式設計中。我們也將參考並且運用同樣的規則在編寫React元件上。
耦合指元素之間的相互連結與依賴關係。如果你改變一個元素需要同步的更新另一個元素,我們稱此為緊密耦合。而鬆散耦合指的是改變一個元素時,並不需要改變另一個元素。舉個例子,顯示銀行轉帳金額功能。如果展示金額依賴匯率計算,那麼內部轉換結構變更時,展示的程式碼也會被更新。如果我們設計基於一個元素介面的,鬆散耦合的系統,這樣元素的改變並不會影響視圖層展示。很明顯,鬆散耦合的元件更易於管理和控制。
內聚則是元件是否只為一件事情負責。這個指標是依照單一原則和unix原則:專註一件事情並且做好這件事。如果帳戶餘額格式化展示需要計算相關匯率和檢查是否有查閱歷史權限,那麼這個包含很多功能職責,而這些功能並不相互依賴。也許,權限處理和匯率應該是不同的元件。另一方面,如果有多個組件,一個用於整數部分,一個用於小數部分,另一個用於貨幣展示,程式設計師想展示餘額,他們則需要找到所有組件進行組裝。其中的挑戰則是創造高度內聚的組件。
建立元件有很多種方法。我們希望在合理程度下元件是可以被重複使用的 。我們也希望建構的小元件可以用在更大的元件中。理想情況下,我們想建立鬆散耦合和高度聚合的元件,這樣我們的系統更有利於管理和擴展。在React元件中props 類似函數中的參數,它們也可以看做是無狀態功能的元件。我們需要思考在元件中如何定義props和元件如何被重複使用。
接下來,我們以費用管理系統為背景,分析費用詳細的格式,來介紹如果構建組件:
type Expense { description: string category: string amount: number doneAt: moment }
根據模型,會有以下幾種對費用格式的程式建模方式:
無props
#傳遞一個expense物件
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,就不會為我們帶來任何彈性,而且元件也只能用於單一的場景。在費用明細的例子中,我們可以看到,最初組件是需要接受一些props。不過在某些場景下,沒有任何props也是一個好的解決方式。首先,我們可以使用一些元件,其props的內容是一些不會輕易更改的內容,例如:商標,logo或公司資訊等。
const Logo = () => ( <p className='logo'> <img src='/logo.png' alt='DayOne logo'/> </p> )寫出盡可能小的元件使得系統更容易維護。保持資訊只保存在一處而且只需要在一處進行修改。不要在多處寫重複的程式碼。 傳遞expense物件在費用明細確定情況下,我們需要傳遞資料給元件。首先,我們需要傳遞一個expense物件。
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> )傳遞費用物件給費用明細的元件是非常有意義的。費用明細的格式是高度一致的,它顯示費用的資料。無論什麼時候需要改變格式,這是唯一可以修改的地方。改變費用明細的格式也不會對費用對象本身帶來什麼副作用。 這個元件是和費用物件緊密耦合,這是一個壞的事情嗎?當然不是,但我們必須意識到這是如何影響我們系統的。 傳遞一個物件作為props,費用明細的元件將會依賴費用內部結構。當我們改變費用的內部結構時候,我們將需要更改費用明細組件。當然,我們只需要在一處進行修改。 這樣的設計如何適應未來的改變呢? 如果我們增加、改變或刪除一個字段,我們將只需改變一個組件。如果我們需要增加一個其他的日曆格式化展示?我們可以為日曆格式化增加一個新的prop。
const ExpenseDetails = ({ expense, dateFormat }) => ( <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.format(dateFormat)}</span></p> </p> )
#
我们开始增加属性来使组件更加灵活。如果只有几个选项,那么一切都是很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中文網其他相關文章!