第四部分:编写干净高效的 React 代码——最佳实践和优化技术
欢迎阅读“ 2023 年 React 最佳实践”系列文章的第四部分!在本部分中,我们将探讨各种技巧和策略,帮助您在 React 应用程序中编写简洁高效的代码。遵循这些最佳实践,您可以提高代码库的可维护性、性能和可读性。
让我们深入学习如何编写干净高效的 React 代码,它不仅运行良好,而且更易于理解、维护和扩展。
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 父组件重新渲染时,该组件也会重新渲染。
为了优化性能,我们可以使用 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 来保证代码质量
利用 ESLint 等 linter 工具可以极大地提高 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 中常用的一个术语,用于描述如何删除死代码。它依赖于 import 和 export 语句来检测代码模块是否被导出和导入,以便在 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
当我们使用多个参数或道具将用户相关信息传递给组件或函数时,记住每个参数的顺序和用途可能具有挑战性,尤其是当参数数量增加时。
// ❌ 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;
}
当我们使用显式返回时,可能会使小函数定义不必要地变得更长且更难阅读。
由于额外的花括号和显式返回语句,它可能会导致代码更加混乱。
// ✅ 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
,它包含多个状态变量和事件处理程序和元素。
随着组件的增长,管理、调试和理解变得越来越困难。
您可以查看下面的博客来解决这个问题,建议将这些庞大的组件分解为更小、可重复使用且集中的组件。

第 3 部分:组件结构 - 在 React 中构建可重用和可维护的组件!
萨蒂什·库马尔·N · May 22 '23
敬请期待即将发布的React 优化技巧博客。我们将探索更多策略来提升组件的性能和效率。不要错过这些宝贵的见解!
编码愉快!😊👩💻👨💻
文章来源:https://dev.to/sathishskdev/part-4-writing-clean-and-efficient-react-code-best-practices-and-optimization-techniques-423d