天天看點

編寫幹淨高效的React代碼 - 最佳實踐和優化技術

作者:曉楓Motivation

我們将探索各種技術和政策,以便在 React 應用程式中編寫幹淨高效的代碼。通過遵循這些最佳實踐,您可以提高代碼庫的可維護性、性能和可讀性。

讓我們深入了解如何編寫幹淨、高效的 React 代碼,這些代碼不僅運作良好,而且更易于了解、維護和擴充。

(更|多優質内|容:java567 點 c0m)

1. 實作錯誤邊界以優雅地處理元件錯誤

使用錯誤邊界包裝您的元件或應用程式的特定部分,以便以受控方式捕獲和處理錯誤。

這可以防止整個應用程式崩潰并提供回退 UI 或錯誤消息,進而改善使用者體驗并更輕松地調試問題。

高階元件 (HOC) - withErrorBoundary:

// HOC for error boundary
 const withErrorBoundary = (WrappedComponent) => {
   return (props) => {
     const [hasError, setHasError] = useState(false);
     const [errorInfo, setErrorInfo] = useState(null);
 
     useEffect(() => {
       const handleComponentError = (error, errorInfo) => {
         setHasError(true);
         setErrorInfo(errorInfo);
         // You can also log the error to an error reporting service here
       };
 
       window.addEventListener('error', handleComponentError);
 
       return () => {
         window.removeEventListener('error', handleComponentError);
       };
     }, []);
 
     if (hasError) {
       // You can customize the fallback UI or error message here
       return <div>Something went wrong. Please try again later.</div>;
     }
 
     return <WrappedComponent {...props} />;
   };
 };           

用法:

// HOC for error boundary
 import withErrorBoundary from './withErrorBoundary';
 
 const Todo = () => {
   // Component logic and rendering
 }
 
 const WrappedComponent = withErrorBoundary(Todo);           

2.使用React.memo作為功能元件

React.memo 是一個高階元件,可以記住功能元件的結果,防止不必要的重新渲染。

通過使用 React.memo 包裝功能元件,您可以在元件的 props 未更改時跳過重新渲染來優化性能。

這是一個例子,

// ❌ 
 const TodoItem = ({text}) => {
   return <div> {text} </div>
 } 
 
 // Todo
 
 const Todo = () => {
  //... Component logic
 
 return <div>
  //.. Other elements
    <TodoItem //.. />
 </div>
 }           

在此示例中,我們有一個名為 TodoItem 的功能元件,它接收 name 屬性并呈現 todo 文本。

預設情況下,隻要Todo 父元件重新渲染,該元件就會重新渲染。

為了優化性能,我們可以用 React.memo 包裝TodoItem,建立該元件的記憶版本。這個記憶元件隻有在它的 props 發生改變時才會重新渲染。

// ✅ Memoized version of TodoItem using React.memo
 const TodoItem = React.memo(({text}) => {
   return <div> {text} </div>
 })            

通過使用React.memo,我們可以防止不必要的重新渲染并優化功能元件的性能。

然而,值得注意的是,React.memo 執行props 的淺層比較。如果您的元件接收複雜的資料結構作為 props,請確定正确處理 prop 更新以實作準确的記憶。

3. 使用 Linting 來提高代碼品質

使用 linter 工具(例如 ESLint)可以極大地提高 React 項目中的代碼品質和一緻性。

通過使用 linter,您可以:

  • 確定代碼風格一緻
  • 捕獲錯誤和有問題的模式
  • 提高代碼的可讀性和可維護性
  • 執行編碼标準和約定

4.避免預設導出

預設導出的問題在于,它可能會導緻更難了解哪些元件正在被導入并在其他檔案中使用。它還限制了導入的靈活性,因為預設導出每個檔案隻能有一個預設導出。

// ❌ Avoid default export
 const Todo = () => {
   // component logic...
 };
 
 export default Todo;             

相反,建議在 React 中使用命名導出:

// ✅ Use named export
 const Todo = () => {
 
 }
 
 export { Todo };           

使用命名導出可以在導入元件時提供更好的清晰度,使代碼庫更有組織性并且更易于導航。

  • 命名導入與tree shake配合得很好。
Tree Shaking 是 JavaScript 上下文中常用的一個術語,用于描述死代碼的删除。它依賴于導入和導出語句來檢測是否導出和導入代碼子產品以在 JavaScript 檔案之間使用。
  • 重構變得更加容易。
  • 更容易識别和了解子產品的依賴關系。

5.使用對象解構

當我們使用點符号直接屬性通路來通路對象的各個屬性時,對于簡單的情況來說效果很好。

// ❌ Avoid direct property access using dot notation
 const todo = {
    id: 1,
    name: "Morning Task",
    completed: false
 }
 
 const id = todo.id;
 const name = todo.name;
 const completed = todo.completed;           

這種方法對于簡單的情況可以很好地工作,但是當處理較大的對象或僅需要屬性的子集時,它可能會變得困難且重複。

另一方面,對象解構提供了一種更簡潔、更優雅的方式來提取對象屬性。它允許您在一行代碼中解構一個對象,并使用類似于對象文字表示法的文法将多個屬性配置設定給變量。

// ✅ Use object destructuring
 const { id, name = "Task", completed } = todo;            
  • 它減少了重複通路對象屬性的需要。
  • 支援預設值的配置設定。
  • 允許變量重命名和别名。

6.使用片段

片段通過在渲染多個元素時避免不必要的包裝 div 來實作更清晰的代碼。

// ❌ Avoid unnecessary wrapper div
 const Todo = () => (
   <div>
     <h1>Title</h1>
     <ul>
       // ...
     </ul>
   </div>
 );           

在上面的代碼中,不必要的包裝 div 會給 DOM 結構增加不必要的複雜性,可能會影響網頁的可通路性。

// ✅ Use fragments
 const Todo = () => (
   <>
     <h1>Title</h1>
     <ul>
       // ...
     </ul>
   </>
 );           

7. 更喜歡傳遞對象而不是多個 props

當我們使用多個參數或 props 将使用者相關資訊傳遞給元件或函數時,記住每個參數的順序和用途可能會很困難,尤其是當參數數量增加時。

// ❌ Avoid passing multiple arguments
 const updateTodo = (id, name, completed) => {
  //...
 }
 
 // ❌ Avoid passing multiple props
 const TodoItem = (id, name, completed) => {
   return(
     //...
   )
 }           

當參數數量增加時,維護和重構代碼變得更具挑戰性。犯錯誤的可能性會增加,例如省略參數或提供不正确的值。

// ✅ Use object arguments
 const updateTodo = (todo) => {
  //...
 }
 
 const todo = {
    id: 1,
    name: "Morning Task",
    completed: false
 }
 
 updateTodo(todo);           
  • 函數變得更加自我描述并且更容易了解。
  • 減少因參數順序不正确而導緻錯誤的機會。
  • 無需更改函數簽名即可輕松添加或修改屬性。
  • 簡化調試或測試函數以将對象作為參數傳遞的過程。

8.使用箭頭功能

箭頭函數提供了更簡潔的文法和詞法作用域,消除了顯式綁定的需要this并提高了代碼可讀性。

// ❌
 function sum(a, b) {
   return a + b;
 }           

上面的代碼可能會導緻冗長的代碼,并可能導緻對 . 的上下文和綁定的誤解this。

// ✅ Use arrow function
 const sum = (a, b) => a + b;           
  • 箭頭函數使代碼更加緊湊和富有表現力。
  • 它自動綁定上下文,減少相關錯誤的機會this。
  • 它提高了代碼的可維護性。

9. 使用枚舉代替數字或字元串

// ❌ Avoid Using numbers or strings
 switch(status) {
   case 1:
     return //...
   case 2:
     return //...
   case 3:
     return //...
 }           

上面的代碼更難了解和維護,因為數字的含義可能不會立即清晰。

// ✅ Use Enums
 const Status = {
   NOT_STARTED: 1,
   IN_PROGRESS: 2,
   COMPLETED: 3
 }
 
 const { NOT_STARTED, IN_PROGRESS COMPLETED } = Status;
 
 switch(status) {
   case NOT_STARTED:
     return //...
   case IN_PROGRESS:
     return //...
   case COMPLETED:
     return //...
 }           
  • 枚舉具有有意義且自我描述的值。
  • 提高代碼可讀性。
  • 減少拼寫錯誤或錯誤值的可能性。
  • 更好的類型檢查、編輯器自動完成和文檔。

10. 使用布爾屬性的簡寫

// ❌
 <Dropdown multiSelect={true} />           
// ✅ Use shorthand
 <Dropdown multiSelect />           

布爾屬性的簡寫文法通過減少不必要的冗長并使意圖清晰來提高代碼的可讀性。

11.避免使用索引作為關鍵道具

// ❌ Avoid index as key
 const renderItem = (todo, index) => {
   const {name} = todo;
   return <li key={index}> {name} </>
 }           

使用索引作為關鍵屬性可能會導緻不正确的渲染尤其是在添加、删除或重新排序清單項時。

它可能會導緻性能不佳群組件更新不正确。

// ✅ Using unique and stable identifiers
 const renderItem = (todo, index) => {
   const {id, name} = todo;
   return <li key={id}> {name} </>
 }           
  • 有效地更新和重新排序清單中的元件。
  • 減少潛在的渲染問題。
  • 避免不正确的元件更新。

12. 在小函數中使用隐式傳回

// ❌ Avoid using explicit returns 
 const square = value => {
   return value * value;
 }           

當我們使用顯式傳回時,可能會使小函數定義變得不必要地更長且更難以閱讀。

由于附加的花括号和顯式的 return 語句,可能會導緻代碼更加混亂。

// ✅ Use implicit return
 const square = value => value * value;           
  • 隐式傳回減少了代碼的冗長。
  • 提高代碼可讀性。
  • 它可以通過關注主要邏輯而不是傳回語句來增強代碼的可維護性。

13.使用PropTypes進行類型檢查

// ❌ Bad Code
 const Button = ({ text, enabled }) => 
       <button enabled>{text}</button>;           

上面的代碼可能會導緻傳遞不正确的prop 類型,這可能會導緻運作時錯誤或意外行為。

// ✅ Use PropTypes
 import PropTypes from 'prop-types';
 
 const Button = ({ enabled, text }) => 
       <button enabled> {text} </button>;
 
 Button.propTypes = {
   enabled: PropTypes.bool
   text: PropTypes.string.isRequired,
 };           
  • 它有助于捕獲編譯時的錯誤。
  • 它提供了更好的了解群組件的預期類型。
  • PropTypes 充當使用該元件的其他開發人員的文檔。

14. 更喜歡使用模闆文字

// ❌ Bad Code
 const title = (seriesName) => 
       "Welcome to " + seriesName + "!";           

上面的代碼可能會導緻冗長的代碼,并使字元串插值或連接配接變得更加困難。

// ✅ Use template literals
 const title = (seriesName) => 
       `Welcome to ${seriesName}!`;           
  • 它通過允許在字元串内進行變量插值來台灣字元串操作。
  • 它使代碼更具表現力并且更易于閱讀。
  • 它支援多行字元串,無需額外的解決方法。
  • 改進代碼格式。

15.避免使用巨大的元件

避免在 React 中使用大型元件對于維護幹淨、子產品化和可維護的代碼至關重要。

大型元件往往更複雜、更難了解并且容易出現問題。讓我們通過一個例子來說明這個概念:

// ❌ Avoid huge component
 const Todo = () => {
   // State Management
   const [text, setText] = useState("");
   const [todos, setTodos] = useState([])
   //... More states
 
   // Event Handlers
   const onChangeInput = () => //...
   // Other event handlers
 
   return (
    <div>
       <input //.. />
       <input //.. />   
       <button //.. />
       <list //... >
         <list-item //..>
       </list/>
    </div>
   )
 };
 
 export default Todo;             

在上面的示例中,我們有一個名為 的元件Todo,其中包含多個狀态變量以及事件處理程式和元素。

随着元件的增長,管理、調試和了解變得更加困難。

請繼續關注即将推出的專門讨論React 優化技術的部落格。我們将探索其他政策來提高元件的性能和效率。不要錯過這些寶貴的見解!

快樂編碼!‍‍

(更|多優質内|容:java567 點 c0m)