欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

在React中應(yīng)用SOLID原則的方法

 更新時(shí)間:2022年07月15日 10:19:27   作者:GUOZE  
SOLID?是一套原則,它們主要是關(guān)心代碼質(zhì)量和可維護(hù)性的軟件專業(yè)人員的指導(dǎo)方針,本文給大家分享如何在React中應(yīng)用SOLID原則,感興趣的朋友一起看看吧

在面向?qū)ο缶幊蹋∣OP)中,SOLID 原則是設(shè)計(jì)模式的基礎(chǔ),它的每個(gè)字母代表一種設(shè)計(jì)原則:

  • 單一職責(zé)原則(SRP)
  • 開(kāi)放封閉原則(OCP)
  • 里氏替換原則(LSP)
  • 接口隔離原則(ISP)
  • 依賴倒置原則(DIP)

下面就來(lái)看看每個(gè)原則的含義以及如何在 React 中應(yīng)用 SOLID 原則!

1、單一職責(zé)原則(SRP)

單一職責(zé)原則的定義是每個(gè)類應(yīng)該只有一個(gè)職責(zé), 也就是只做一件事。這個(gè)原則是最容易解釋的,因?yàn)槲覀兛梢院?jiǎn)單地將其理解為“每個(gè)功能/模塊/組件都應(yīng)該只做一件事”。

在所有這些原則中,單一職責(zé)原則是最容易遵循的,也是最有影響力的一項(xiàng),因?yàn)樗鼧O大提高了代碼的質(zhì)量。為了確保組件只做一件事,可以這樣:

  • 將功能較多的大型組件拆分為較小的組件。
  • 將與組件功能無(wú)關(guān)的代碼提取到單獨(dú)的函數(shù)中。
  • 將有聯(lián)系的功能提取到自定義 Hooks 中。

下面來(lái)看一個(gè)顯示活躍用戶列表的組件:

const ActiveUsersList = () => {
  const [users, setUsers] = useState([]) 
  useEffect(() => {
    const loadUsers = async () => {  
      const response = await fetch('/some-api')
      const data = await response.json()
      setUsers(data)
    }  
    loadUsers()
  }, [])
  const weekAgo = new Date();
  weekAgo.setDate(weekAgo.getDate() - 7);
  return (
    <ul>
      {users.filter(user => !user.isBanned && user.lastActivityAt >= weekAgo).map(user => 
         <li key={user.id}>
           <img src={user.avatarUrl} />
           <p>{user.fullName}</p>
           <small>{user.role}</small>
         </li>
      )}
    </ul>    
  )
}

這個(gè)組件雖然代碼不多,但是做了很多事情:獲取數(shù)據(jù)、過(guò)濾數(shù)據(jù)、渲染數(shù)據(jù)。來(lái)看看如何分解它。

首先,只要同時(shí)使用了 useState? 和 useEffect,就可以將它們提取到自定義 Hook 中:

const useUsers = () => {
  const [users, setUsers] = useState([])
  useEffect(() => {
    const loadUsers = async () => {  
      const response = await fetch('/some-api')
      const data = await response.json()
      setUsers(data)
    }
    loadUsers()
  }, []) 
  return { users }
}
const ActiveUsersList = () => {
  const { users } = useUsers()  
  const weekAgo = new Date()
  weekAgo.setDate(weekAgo.getDate() - 7)
  return (
    <ul>
      {users.filter(user => !user.isBanned && user.lastActivityAt >= weekAgo).map(user => 
        <li key={user.id}>
          <img src={user.avatarUrl} />
          <p>{user.fullName}</p>
          <small>{user.role}</small>
        </li>
      )}
    </ul>    
  )
}

現(xiàn)在,useUsers Hook只關(guān)心一件事——從API獲取用戶。它使我們的組件代碼更具可讀性。

接下來(lái)看一下組件渲染的 JSX。每當(dāng)我們對(duì)對(duì)象數(shù)組進(jìn)行遍歷時(shí),都應(yīng)該注意它為每個(gè)數(shù)組項(xiàng)生成的 JSX 的復(fù)雜性。如果它是一個(gè)沒(méi)有附加任何事件處理函數(shù)的單行代碼,將其保持內(nèi)聯(lián)是完全沒(méi)有問(wèn)題的。但對(duì)于更復(fù)雜的JSX,將其提取到單獨(dú)的組件中可能是一個(gè)更好的主意:

const UserItem = ({ user }) => {
  return (
    <li>
      <img src={user.avatarUrl} />
      <p>{user.fullName}</p>
      <small>{user.role}</small>
    </li>
  )
}
const ActiveUsersList = () => {
  const { users } = useUsers() 
  const weekAgo = new Date()
  weekAgo.setDate(weekAgo.getDate() - 7)
  return (
    <ul>
      {users.filter(user => !user.isBanned && user.lastActivityAt >= weekAgo).map(user => 
        <UserItem key={user.id} user={user} />
      )}
    </ul>    
  )
}

這里將用于呈現(xiàn)用戶信息的邏輯提取到了一個(gè)單獨(dú)的組件中,從而使我們的組件更小、更可讀。

最后,從 API 獲取到的用戶列表中過(guò)濾出所有非活躍用戶的邏輯是相對(duì)獨(dú)立的,可以在其他部分重用,所以可以將其提取到一個(gè)公共函數(shù)中:

const getOnlyActive = (users) => {
  const weekAgo = new Date()
  weekAgo.setDate(weekAgo.getDate() - 7)
  return users.filter(user => !user.isBanned && user.lastActivityAt >= weekAgo)
}
const ActiveUsersList = () => {
  const { users } = useUsers()
  return (
    <ul>
      {getOnlyActive(users).map(user => 
        <UserItem key={user.id} user={user} />
      )}
    </ul>    
  )
}

到現(xiàn)在為止,通過(guò)上面三步拆解,組件已經(jīng)變得比較簡(jiǎn)單。但是,仔細(xì)觀察會(huì)發(fā)現(xiàn),這個(gè)組件還有優(yōu)化的空間。目前,組件首先獲取數(shù)據(jù),然后需要對(duì)數(shù)據(jù)進(jìn)行過(guò)濾。理想情況下,我們只想獲取數(shù)據(jù)并渲染它,而不需要任何額外的操作。所以,可以將這個(gè)邏輯封裝到一個(gè)新的自定義 Hook 中,最終的代碼如下:

// 獲取數(shù)據(jù)
const useUsers = () => {
  const [users, setUsers] = useState([])
  
  useEffect(() => {
    const loadUsers = async () => {  
      const response = await fetch('/some-api')
      const data = await response.json()
      setUsers(data)
    }
    loadUsers()
  }, [])
  
  return { users }
}
// 列表渲染
const UserItem = ({ user }) => {
  return (
    <li>
      <img src={user.avatarUrl} />
      <p>{user.fullName}</p>
      <small>{user.role}</small>
    </li>
  )
}
// 列表過(guò)濾
const getOnlyActive = (users) => {
  const weekAgo = new Date()
  weekAgo.setDate(weekAgo.getDate() - 7) 
  return users.filter(user => !user.isBanned && user.lastActivityAt >= weekAgo)
}
const useActiveUsers = () => {
  const { users } = useUsers()
  const activeUsers = useMemo(() => {
    return getOnlyActive(users)
  }, [users])
  return { activeUsers }
}
const ActiveUsersList = () => {
  const { activeUsers } = useActiveUsers()
  return (
    <ul>
      {activeUsers.map(user => 
        <UserItem key={user.id} user={user} />
      )}
    </ul>    
  )
}

在這里,我們創(chuàng)建了useActiveUsers Hook 來(lái)處理獲取和過(guò)濾數(shù)據(jù)的邏輯,而組件只做了最少的事情——渲染它從 Hook 中獲取的數(shù)據(jù)。

現(xiàn)在,這個(gè)組件只剩下兩個(gè)職責(zé):獲取數(shù)據(jù)和渲染數(shù)據(jù),當(dāng)然我們也可以在組件的父級(jí)獲取數(shù)據(jù),并通過(guò) props 傳入該組件,這樣只需要渲染組件就可以了。當(dāng)然,還是要視情況而定。我們可以簡(jiǎn)單地將獲取并渲染數(shù)據(jù)看作是“一件事”。

總而言之,遵循單一職責(zé)原則,我們有效地采用了大量獨(dú)立的代碼并使其更加模塊化,模塊化的代碼更容易測(cè)試和維護(hù)。

2、開(kāi)放封閉原則(OCP)

開(kāi)放封閉原則指出“ 一個(gè)軟件實(shí)體(類、模塊、函數(shù))應(yīng)該對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉 ”。開(kāi)放封閉原則主張以一種允許在不更改源代碼的情況下擴(kuò)展組件的方式來(lái)構(gòu)造組件。

下面來(lái)看一個(gè)場(chǎng)景,有一個(gè)可以在不同頁(yè)面上使用的 Header? 組件,根據(jù)所在頁(yè)面的不同,Header 組件的 UI 應(yīng)該有略微的不同:

const Header = () => {
  const { pathname } = useRouter()
  return (
    <header>
      <Logo />
      <Actions>
        {pathname === '/dashboard' && <Link to="/events/new">Create event</Link>}
        {pathname === '/' && <Link to="/dashboard">Go to dashboard</Link>}
      </Actions>
    </header>
  )
}
const HomePage = () => (
  <>
    <Header />
    <OtherHomeStuff />
  </>
)
const DashboardPage = () => (
  <>
    <Header />
    <OtherDashboardStuff />
  </>
)

這里,根據(jù)所在頁(yè)面的不同,呈現(xiàn)指向不同頁(yè)面組件的鏈接。那現(xiàn)在考慮一下,如果需要將這個(gè)Header?組件添加到更多的頁(yè)面中會(huì)發(fā)生什么呢?每次創(chuàng)建新頁(yè)面時(shí),都需要引用 Header? 組件,并修改其內(nèi)部實(shí)現(xiàn)。這種方式使得 Header 組件與使用它的上下文緊密耦合,并且違背了開(kāi)放封閉原則。

為了解決這個(gè)問(wèn)題,我們可以使用組件組合。Header? 組件不需要關(guān)心它將在內(nèi)部渲染什么,相反,它可以將此責(zé)任委托給將使用 children 屬性的組件:

const Header = ({ children }) => (
  <header>
    <Logo />
    <Actions>
      {children}
    </Actions>
  </header>
)
const HomePage = () => (
  <>
    <Header>
      <Link to="/dashboard">Go to dashboard</Link>
    </Header>
    <OtherHomeStuff />
  </>
)
const DashboardPage = () => (
  <>
    <Header>
      <Link to="/events/new">Create event</Link>
    </Header>
    <OtherDashboardStuff />
  </>
)

使用這種方法,我們完全刪除了 Header? 組件內(nèi)部的變量邏輯。現(xiàn)在可以使用組合將想要的任何內(nèi)容放在Header中,而無(wú)需修改組件本身。

遵循開(kāi)放封閉原則,可以減少組件之間的耦合,使它們更具可擴(kuò)展性和可重用性。

3、里氏替換原則(LSP)

里氏替換原則可以理解為 對(duì)象之間的一種關(guān)系,子類型對(duì)象應(yīng)該可以替換為超類型對(duì)象 。這個(gè)原則嚴(yán)重依賴類繼承來(lái)定義超類型和子類型關(guān)系,但它在 React 中可能不太適用,因?yàn)槲覀儙缀醪粫?huì)處理類,更不用說(shuō)類繼承了。雖然遠(yuǎn)離類繼承會(huì)不可避免地將這一原則轉(zhuǎn)變?yōu)橥耆煌臇|西,但使用繼承編寫(xiě) React 代碼會(huì)使代碼變得糟糕(React 團(tuán)隊(duì)不推薦使用繼承)。因此,對(duì)于這一原則不再過(guò)多解釋。

4、接口隔離原則(ISP)

根據(jù)接口隔離原則的說(shuō)法,客戶端不應(yīng)該依賴它不需要的接口。為了更好的說(shuō)明 ISP 所針對(duì)的問(wèn)題,來(lái)看一個(gè)呈現(xiàn)視頻列表的組件:

type Video = {
  title: string
  duration: number
  coverUrl: string
}
type Props = {
  items: Array<Video>
}
const VideoList = ({ items }) => {
  return (
    <ul>
      {items.map(item => 
        <Thumbnail 
          key={item.title} 
          video={item} 
        />
      )}
    </ul>
  )
}

Thumbnail 組件的實(shí)現(xiàn)如下:

type Props = {
  video: Video
}
const Thumbnail = ({ video }: Props) => {
  return <img src={video.coverUrl} />
}

Thumbnail? 組件非常小并且很簡(jiǎn)單,但它有一個(gè)問(wèn)題:它希望將完整的視頻對(duì)象作為 props? 傳入,但是僅有效地使用其屬性之一(coverUrl)。

除了視頻,我們還需要渲染直播的縮略圖,這兩種媒體資源會(huì)混合在同一個(gè)列表中。

下面來(lái)定義直播的類型 LiveStream :

type LiveStream = {
  name: string
  previewUrl: string
}

這是更新后的 VideoList 組件:

type Props = {
  items: Array<Video | LiveStream>
}
const VideoList = ({ items }) => {
  return (
    <ul>
      {items.map(item => {
        if ('coverUrl' in item) {
          return <Thumbnail video={item} />
        } else {
          // 直播組件,該怎么寫(xiě)?
        }
      })}
    </ul>
  )
}

這時(shí)就發(fā)現(xiàn)一個(gè)問(wèn)題,我們可以輕松的區(qū)分視頻和直播對(duì)象,但是不能將后者傳遞給Thumbnail?組件,因?yàn)閂ideo和LiveStream?類型不兼容。它們包含了不同的屬性來(lái)保存縮略圖:視頻對(duì)象調(diào)用coverUrl?,直播對(duì)象調(diào)用previewUrl?。這就是使組件依賴了比實(shí)際更多的props的原因所在。

下面來(lái)重構(gòu) Thumbnail? 組件以確保它僅依賴于它需要的props:

type Props = {
  coverUrl: string
}
const Thumbnail = ({ coverUrl }: Props) => {
  return <img src={coverUrl} />
}

通過(guò)這樣修改,現(xiàn)在我們可以使用它來(lái)渲染視頻和直播的對(duì)略圖:

type Props = {
  items: Array<Video | LiveStream>
}
const VideoList = ({ items }) => {
  return (
    <ul>
      {items.map(item => {
        if ('coverUrl' in item) {
          return <Thumbnail coverUrl={item.coverUrl} />
        } else {
          return <Thumbnail coverUrl={item.previewUrl} />
        }
      })}
    </ul>
  )
}

當(dāng)然,這段代碼還可以簡(jiǎn)化一下:

type Props = {
  items: Array<Video | LiveStream>
}
const VideoList = ({ items }) => {
  return (
    <ul>
      {items.map(item => (
         <Thumbnail coverUrl={'coverUrl' in item ? item.coverUrl : item.previewUrl} />
      ))}
    </ul>
  )
}

接口隔離原則主張最小化系統(tǒng)組件之間的依賴關(guān)系,使它們的耦合度降低,從而提高可重用性。

5、依賴倒置原則(DIP)

依賴倒置原則指出“要依賴于抽象,不要依賴于具體”。換句話說(shuō),一個(gè)組件不應(yīng)該直接依賴于另一個(gè)組件,而是它們都應(yīng)該依賴于一些共同的抽象。這里,“組件”是指應(yīng)用程序的任何部分,可以是 React 組件、函數(shù)、模塊或第三方庫(kù)。這個(gè)原則可能很難理解,下面來(lái)看一個(gè)具體的例子。

有一個(gè) LoginForm 組件,它在提交表單時(shí)將用戶憑據(jù)發(fā)送到某些 API:

import api from '~/common/api'
const LoginForm = () => {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')

  const handleSubmit = async (evt) => {
    evt.preventDefault()
    await api.login(email, password)
  }
  return (
    <form onSubmit={handleSubmit}>
      <input type="email" value={email} onChange={e => setEmail(e.target.value)} />
      <input type="password" value={password} onChange={e => setPassword(e.target.value)} />
      <button type="submit">Log in</button>
    </form>
  )
}

在這段代碼中,LoginForm? 組件直接引用了 api 模塊,因此它們之間存在緊密耦合。這種依賴關(guān)系就會(huì)導(dǎo)致一個(gè)組件的更改會(huì)影響其他組件。依賴倒置原則就提倡打破這種耦合,下面來(lái)看看如何實(shí)現(xiàn)這一點(diǎn)。

首先,從 LoginForm? 組件中刪除對(duì) api? 模塊的直接引用,而是允許通過(guò) props 傳入所需的回調(diào)函數(shù):

type Props = {
  onSubmit: (email: string, password: string) => Promise<void>
}
const LoginForm = ({ onSubmit }: Props) => {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const handleSubmit = async (evt) => {
    evt.preventDefault()
    await onSubmit(email, password)
  }
  return (
    <form onSubmit={handleSubmit}>
      <input type="email" value={email} onChange={e => setEmail(e.target.value)} />
      <input type="password" value={password} onChange={e => setPassword(e.target.value)} />
      <button type="submit">Log in</button>
    </form>
  )
}

通過(guò)這樣修改,LoginForm? 組件不再依賴于 api? 模塊。向 API 提交憑證的邏輯是通過(guò) onSubmit 回調(diào)函數(shù)抽象出來(lái)的,現(xiàn)在由父組件負(fù)責(zé)提供該邏輯的具體實(shí)現(xiàn)。

為此,創(chuàng)建了一個(gè) ConnectedLoginForm? 組件來(lái)將表單提交邏輯委托給 api 模塊:

import api from '~/common/api'
const ConnectedLoginForm = () => {
  const handleSubmit = async (email, password) => {
    await api.login(email, password)
  }
  return (
    <LoginForm onSubmit={handleSubmit} />
  )
}

ConnectedLoginForm? 組件充當(dāng) api? 和 LoginForm 之間的粘合劑,而它們本身保持完全獨(dú)立。這樣就可以對(duì)這兩個(gè)組件進(jìn)行單獨(dú)的修改和維護(hù),而不必?fù)?dān)心修改會(huì)影響其他組件。

依賴倒置原則旨在最小化應(yīng)用程序不同組件之間的耦合。你可能已經(jīng)注意到,最小化是所有 SOLID 原則中反復(fù)出現(xiàn)的關(guān)鍵詞——從最小化單個(gè)組件的職責(zé)范圍到最小化它們之間的依賴關(guān)系等等。

6、小結(jié)

通過(guò)上面的示例,相信你已經(jīng)對(duì)如何在 React 中使用 SOLID 原則有了一定的了解。應(yīng)用SOLID 原則使我們的 React 代碼更易于維護(hù)和健壯。

但是需要注意,虔誠(chéng)地遵循這些原則可能會(huì)造成破壞并導(dǎo)致代碼過(guò)度設(shè)計(jì),因此我們應(yīng)該學(xué)會(huì)識(shí)別對(duì)組件的進(jìn)一步分解或解耦何時(shí)會(huì)導(dǎo)致其復(fù)雜度增加而幾乎沒(méi)有任何好處。

到此這篇關(guān)于如何在React中應(yīng)用SOLID原則?的文章就介紹到這了,更多相關(guān)React SOLID原則內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • react中定義變量并使用方式

    react中定義變量并使用方式

    這篇文章主要介紹了react中定義變量并使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • 一文詳解React類組件中的refs屬性

    一文詳解React類組件中的refs屬性

    react中的ref類似于vue中的ref,都是可以操作dom的,這篇文章我們通過(guò)一個(gè)demo來(lái)學(xué)習(xí)這個(gè)屬性,文中有詳細(xì)的代碼示例供大家參考,具有一定的參考價(jià)值,需要的朋友可以參考下
    2023-08-08
  • React如何利用相對(duì)于根目錄進(jìn)行引用組件詳解

    React如何利用相對(duì)于根目錄進(jìn)行引用組件詳解

    這篇文章主要給大家介紹了關(guān)于React如何使用相對(duì)于根目錄進(jìn)行引用組件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-10-10
  • 詳解在React項(xiàng)目中安裝并使用Less(用法總結(jié))

    詳解在React項(xiàng)目中安裝并使用Less(用法總結(jié))

    這篇文章主要介紹了詳解在React項(xiàng)目中安裝并使用Less(用法總結(jié)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • React中hook函數(shù)與useState及useEffect的使用

    React中hook函數(shù)與useState及useEffect的使用

    這篇文章主要介紹了React中hook函數(shù)與useState及useEffect的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2022-10-10
  • React路由的history對(duì)象的插件history的使用解讀

    React路由的history對(duì)象的插件history的使用解讀

    這篇文章主要介紹了React路由的history對(duì)象的插件history的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • 使用React-Router時(shí)出現(xiàn)的錯(cuò)誤及解決

    使用React-Router時(shí)出現(xiàn)的錯(cuò)誤及解決

    這篇文章主要介紹了使用React-Router時(shí)出現(xiàn)的錯(cuò)誤及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • React?Hooks使用startTransition與useTransition教程示例

    React?Hooks使用startTransition與useTransition教程示例

    這篇文章主要為大家介紹了React?Hooks使用startTransition與useTransition教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • react源碼層探究setState作用

    react源碼層探究setState作用

    寫(xiě)react的時(shí)候,踩了幾次坑發(fā)現(xiàn)setstate之后state不會(huì)立刻更新,于是判定setstate就是異步的方法,但是直到有一天,我想立刻拿到更新的state去傳參另一個(gè)方法的時(shí)候,才問(wèn)自己,為什么setstate是異步的?準(zhǔn)確地說(shuō),在React內(nèi)部機(jī)制能檢測(cè)到的地方,setState就是異步的
    2022-10-10
  • React?Redux管理庫(kù)示例詳解

    React?Redux管理庫(kù)示例詳解

    這篇文章主要介紹了如何在React中直接使用Redux,目前redux在react中使用是最多的,所以我們需要將之前編寫(xiě)的redux代碼,融入到react當(dāng)中去,本文給大家詳細(xì)講解,需要的朋友可以參考下
    2022-12-12

最新評(píng)論