自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何寫出更優(yōu)雅的 React 組件 - 代碼結(jié)構(gòu)篇

開發(fā) 前端
我們從代碼結(jié)構(gòu)的角度來談?wù)勅绾卧O(shè)計(jì)一個(gè)更優(yōu)雅的 React 組件。優(yōu)秀的組件有著一個(gè)清晰的目錄結(jié)構(gòu)。這里的目錄結(jié)構(gòu)分為項(xiàng)目級結(jié)構(gòu)、單組件級結(jié)構(gòu)。

[[438981]]

在日常團(tuán)隊(duì)開發(fā)中大家寫的組件質(zhì)量參差不齊,風(fēng)格千差萬別。會因?yàn)楹芏嘈枨髮?dǎo)致組件無法擴(kuò)展,或難以維護(hù)。導(dǎo)致很多業(yè)務(wù)組件的功能重復(fù),使用起來相當(dāng)難受。我們從代碼結(jié)構(gòu)的角度來談?wù)勅绾卧O(shè)計(jì)一個(gè)更優(yōu)雅的 React 組件。

組件目錄結(jié)構(gòu)

優(yōu)秀的組件有著一個(gè)清晰的目錄結(jié)構(gòu)。這里的目錄結(jié)構(gòu)分為項(xiàng)目級結(jié)構(gòu)、單組件級結(jié)構(gòu)。

容器組件/展示組件

在項(xiàng)目中我們的目錄結(jié)構(gòu)可以根據(jù)組件和業(yè)務(wù)耦合來劃分,和業(yè)務(wù)的耦合程度越低, 可復(fù)用性越強(qiáng)。展示組件只關(guān)注展示層, 可以在多個(gè)地方被復(fù)用, 它不耦合業(yè)務(wù)。容器組件主要關(guān)注業(yè)務(wù)處理,容器組件通過組合展示組件來構(gòu)建完整視圖。

示例:

  1. src/ 
  2.   components/ (通用組件,與業(yè)務(wù)無關(guān),可被其他所有組件調(diào)用) 
  3.     Button/ 
  4.       index.tsx 
  5.   containers/ (容器組件,與業(yè)務(wù)深度耦合,可被頁面組件調(diào)用) 
  6.     Hello/ 
  7.       Kitty/ (容器組件中的特有組件,不能與其他容器組件共享) 
  8.       index.tsx 
  9.     World/ 
  10.       components/ 
  11.       index.tsx 
  12.   hooks/ (公共的 hooks) 
  13.   pages/ (頁面組件,特定的頁面,無復(fù)用性) 
  14.     my-app/ 
  15.   store/ (狀態(tài)管理) 
  16.   services/ (接口定義) 
  17.   utils/ (工具類) 

組件目錄結(jié)構(gòu)

我們可以根據(jù)文件類型/功能/職責(zé)等劃分不同的目錄。

  1. 根據(jù)文件類型可以分出 images 等目錄
  2. 根據(jù)文件功能可以分出 __tests__ 、demo 等目錄
  3. 根據(jù)文件職責(zé)可以分出 types 、utils 、hooks 等目錄
  4. 根據(jù)組件的特點(diǎn)可以用目錄劃分歸類
  1. HelloWorld/ (普通的業(yè)務(wù)組件) 
  2.   __tests__/ (測試用例) 
  3.   demo/ (組件示例) 
  4.   Bar/ (特有組件分類) 
  5.     Kitty.tsx (特有組件) 
  6.     Kitty.module.less 
  7.   Foo/ 
  8.   hooks/ (自定義 hooks) 
  9.   images/ (圖片目錄) 
  10.   types/ (類型定義) 
  11.   utils/ (工具類方法) 
  12.   index.tsx (出口文件) 

比如我最近寫的一個(gè)表格組件的目錄結(jié)構(gòu):

  1. ├─SheetTable 
  2. │  ├─Cell 
  3. │  ├─Header 
  4. │  ├─Layer 
  5. │  ├─Main 
  6. │  ├─Row 
  7. │  ├─Store 
  8. │  ├─types 
  9. │  └─utils 

組件內(nèi)部結(jié)構(gòu)

組件內(nèi)部需要保持良好的順序邏輯,統(tǒng)一團(tuán)隊(duì)規(guī)范。約定俗成后,這樣一目了然定義可以讓我們更清晰地去 Review。

導(dǎo)入順序

導(dǎo)入順序?yàn)?node_modules -> @/ 開頭文件 -> 相對路徑文件 -> 當(dāng)前組件樣式文件

  1. // 導(dǎo)入 node_modules 依賴 
  2. import React from'react'
  3. // 導(dǎo)入公共組件 
  4. import Button from'@/components/Button'
  5. // 導(dǎo)入相對路徑組件 
  6. import Foo from'./Foo'
  7. // 導(dǎo)入對應(yīng)同名的 .less 文件,命名為 styles 
  8. import styles from'./Kitty.module.less'

使用 組件名 + Props 形式命名 Props 類型并導(dǎo)出。

類型與參數(shù)書寫的順序保持一致,一般以 [a-z] 的順序定義。變量的注釋禁止放末尾,原因是會導(dǎo)致編輯器識別錯(cuò)位,無法正確提示

  1. /** 
  2.  * 類型定義(命名:組件名 + Props) 
  3.  */ 
  4. export interface KittyProps { 
  5.   /** 
  6.    * 多行注釋(建議) 
  7.    */ 
  8.   email: string; 
  9.   // 單行注釋(不推薦) 
  10.   mobile: string; 
  11.   username: string; // 末尾注釋(禁止) 

使用 React.FC 定義

  1. const Kitty: React.FC<KittyProps> = ({ email, mobile, usename }) => {}; 

泛型,代碼提示更智能

以下例子,可以用過泛型讓 value 和 onChange 回調(diào)中的類型保持一致,并做到編輯器智能類型提示。

注意:泛型組件無法使用 React.FC 類型

  1. export interface FooProps<Value> { 
  2.   value: Value; 
  3.   onChange: (value: Value) =>void; 
  4.  
  5. exportfunction Foo<Value extends React.Key>(props: FooProps<Value>) {} 

禁止直接使用 any 類型

無論隱式和顯式的方式,都不推薦使用 any 類型。定義了 any 的參數(shù)會讓使用該組件的人產(chǎn)生極度困惑,無法明確地知道其中的類型。我們可以通過泛型的方式去聲明。

  1. // 隱式 any (禁止) 
  2. let foo; 
  3. function bar(param) {} 
  4.  
  5. // 顯式 any (禁止) 
  6. let hello: any
  7. function world(param: any) {} 
  8.  
  9. // 使用泛型繼承,縮小類型范圍 (推薦) 
  10. function Tom<P extends Record<string, any>>(param: P) {} 

一個(gè)組件對應(yīng)一個(gè)樣式文件

我們以組件的顆粒度大小為抽象單元,樣式文件則應(yīng)與組件本身保持一致。不推薦交叉引入樣式文件的做法,這樣會導(dǎo)致重構(gòu)混亂,無法明確當(dāng)前這個(gè)樣式被多少個(gè)組件使用。

  1. - Tom.tsx 
  2. - Tom.module.less 
  3. - Kitty.tsx 
  4. - Kitty.module.less 

內(nèi)聯(lián)樣式

避免偷懶,要時(shí)刻保持優(yōu)雅,隨手一個(gè) style={} 是極為不推薦的。這樣不僅每次渲染都有重新創(chuàng)建的消耗,而且是清晰的 JSX 上的噪點(diǎn),影響閱讀。

組件行數(shù)限制

組件需要明確的注釋,并保持 300 行以內(nèi)的代碼行數(shù)。代碼行數(shù)可以通過配置 eslint 來做到限制(可以跳過注釋/空行的的統(tǒng)計(jì)):

  1. 'max-lines-per-function': [2, { max: 320, skipComments: true, skipBlankLines: true }], 

組件內(nèi)部編寫代碼的順序

組件內(nèi)部的順序?yàn)?state -> custom Hooks -> effects -> 內(nèi)部 function -> 其他邏輯 -> JSX

  1. /** 
  2.  * 組件注釋(簡明概要) 
  3.  */ 
  4. const Kitty: React.FC<KittyProps> = ({ email }) => { 
  5.   // 1. state 
  6.  
  7.   // 2. custom Hooks 
  8.  
  9.   // 3. effects 
  10.  
  11.   // 4. 內(nèi)部 function 
  12.  
  13.   // 5. 其他邏輯... 
  14.  
  15.   return ( 
  16.     <div className={styles.wrapper}> 
  17.       {email} 
  18.       <Child /> 
  19.     </div> 
  20.   ); 
  21. }; 

事件函數(shù)命名區(qū)分

內(nèi)部方法按照 handle{Type}{Event} 命名,例如 handleNameChange。暴露外部的方法按照 on{Type}{Event},例如 onNameChange。這樣做的好處可以直接通過函數(shù)名區(qū)分是否為外部參數(shù)。

例如 antd/Button 組件片段:

  1. const handleClick = (e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement, MouseEvent>) => { 
  2.   const { onClick, disabled } = props; 
  3.   if (innerLoading || disabled) { 
  4.     e.preventDefault(); 
  5.     return
  6.   } 
  7.   (onClick as React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>)?.(e); 
  8. }; 

繼承原生元素 props 定義

原生元素 props 都繼承了 React.HTMLAttributes。某些特殊元素也會擴(kuò)展自己的屬性,例如 InputHTMLAttributes。

我們定義一個(gè)自定義組件則可以通過繼承 React.InputHTMLAttributes ,讓其類型具有所有 input 的特性。

  1. export interface KittyProps extends React.InputHTMLAttributes<HTMLInputElement> { 
  2.   /** 
  3.    * 新增支持回車鍵事件 
  4.    */ 
  5.   onPressEnter?: React.KeyboardEventHandler<HTMLInputElement>; 
  6.  
  7. function Kitty({ onPressEnter, onKeyUp, ...restProps }: KittyProps) { 
  8.   function handleKeyUp(e: React.KeyboardEvent<HTMLInputElement>) { 
  9.     if (e.code.includes('Enter') && onPressEnter) { 
  10.       onPressEnter(e); 
  11.     } 
  12.     if (onKeyUp) { 
  13.       onKeyUp(e); 
  14.     } 
  15.   } 
  16.  
  17.   return<input onKeyUp={handleKeyUp} {...restProps} />; 

避免循環(huán)依賴

如果你寫的組件包含了循環(huán)依賴, 這時(shí)候你需要考慮拆分和設(shè)計(jì)模塊文件

  1. // --- Foo.tsx --- 
  2. import Bar from'./Bar'
  3.  
  4. export interface FooProps {} 
  5.  
  6. exportconst Foo: React.FC<FooProps> = () => {}; 
  7. Foo.Bar = Bar; 
  8.  
  9. // --- Bar.tsx ---- 
  10. import { FooProps } from'./Foo'

上面 Foo 和 Bar 組件就形成了一個(gè)簡單循環(huán)依賴, 盡管它不會造成什么運(yùn)行時(shí)問題. 解決方案就是將 FooProps 抽取到單獨(dú)的文件:

  1. // --- types.ts --- 
  2. export interface FooProps {} 
  3.  
  4. // --- Foo.tsx --- 
  5. import Bar from'./Bar'
  6. import { FooProps } from'./types'
  7.  
  8. exportconst Foo: React.FC<FooProps> = () => {}; 
  9. Foo.Bar = Bar; 
  10.  
  11. // --- Bar.tsx ---- 
  12. import { FooProps } from'./types'

相對路徑不要超過兩級

當(dāng)項(xiàng)目復(fù)雜的情況下,目錄結(jié)構(gòu)會越來越深,文件會有很長的 ../ 路徑,這樣看起來很不優(yōu)雅:

  1. import { ButtonProps } from'../../../components/Button'

我們可以通過在 tsconfig.json 中配置

  1. "paths": { 
  2.   "@/*": ["src/*"

和 vite 中配置

  1. alias: { 
  2.   '@/': `${path.resolve(process.cwd(), 'src')}/`, 

現(xiàn)在我們可以導(dǎo)入相對于 src 的模塊:

  1. import { ButtonProps } from'@/components/Button'

當(dāng)然更徹底一點(diǎn),可以使用 monorepo 的項(xiàng)目管理方式來解耦各個(gè)組件。只要搭建一套腳手架,就能管理(構(gòu)建、測試、發(fā)布)多個(gè) package

不要直接使用 export default 導(dǎo)出未命名的組件

這種方式導(dǎo)出的組件在 React Inspector 查看時(shí)會顯示為 Unknown

  1. // 錯(cuò)誤做法 
  2. exportdefault () => {}; 
  3.  
  4. // 正確做法 
  5. exportdefaultfunction Kitty() {} 
  6.  
  7. // 正確做法:先聲明后導(dǎo)出 
  8. function Kitty() {} 
  9.  
  10. exportdefault Kitty; 

結(jié)語

以上是寫 React 組件在目錄結(jié)構(gòu)以及編碼規(guī)則上需要注意的點(diǎn),后續(xù)我們講解如何在思維上保持優(yōu)雅。

 

責(zé)任編輯:姜華 來源: 前端星辰
相關(guān)推薦

2021-12-13 14:37:37

React組件前端

2022-03-11 12:14:43

CSS代碼前端

2022-05-13 08:48:50

React組件TypeScrip

2023-12-21 10:26:30

??Prettier

2016-11-25 13:50:15

React組件SFC

2021-01-04 07:57:07

C++工具代碼

2019-09-20 15:47:24

代碼JavaScript副作用

2017-09-01 14:18:50

前端React組件

2020-05-14 09:15:52

設(shè)計(jì)模式SOLID 原則JS

2018-07-12 14:20:33

SQLSQL查詢編寫

2020-07-15 08:17:16

代碼

2020-05-08 14:45:00

JS代碼變量

2022-08-09 13:22:26

Hooksreactvue

2020-05-11 15:23:58

CQRS代碼命令

2021-09-01 08:55:20

JavaScript代碼開發(fā)

2013-06-07 14:00:23

代碼維護(hù)

2021-11-30 10:20:24

JavaScript代碼前端

2022-02-08 19:33:13

技巧代碼格式

2022-02-17 10:05:21

CSS代碼前端

2020-12-19 10:45:08

Python代碼開發(fā)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號