大家好,这里是大家的林语冰。本期《前端翻译计划》共享的是使用最佳实践和已验证技术增强您的 React 开发。
免责声明
本文属于是语冰的直男翻译了属于是,略有删改,仅供粉丝参考,英文原味版请临幸 React Js Clean Code Guide。
React 通过高效且给力的技术方案来构建 UI,彻底改变了前端开发。虽然但是,随着 React App 的复杂性熵增,维护精简、可读和可维护的代码至关重要。在本指南中,我们将探讨编写精简 React 代码的最佳实践,这些代码不仅有效,而且易于理解和维护。
1. 组件结构和组织
结构良好的组件层次结构可简化导航,并有助于了解 App 的流程。请遵循以下准则:
- SRP(单一责任原则):让您的组件聚焦单一责任。如果组件熵增,请考虑将其分解为更小、更易于管理的"乐高积木"。
- 容器和展示组件:将组件分为两类:容器组件(负责数据和逻辑)和展示组件(聚焦于渲染 UI)。这种分离增强了代码的清晰度和可复用性。
- 文件夹结构 :在逻辑文件夹结构中组织组件。将相关组件组合在一起,并考虑使用
components
、containers
和utils
等目录,按部就班。
2. 描述性命名
React 中的描述性命名是指为 React App 代码库中的变量、函数、组件、文件和其他元素选择有意义且不言而喻的名称的实践。描述性命名的目标是使阅读或协作处理代码的其他开发者立即清楚地了解每个元素的用途和功能。这种做法增强了代码的可读性、可维护性和对 App 的整体理解。
以下是 React 代码使用描述性命名的若干准则:
- 组件:选择准确表示其在 App 中的用途或角色的组件名称。使用描述性名词或名词短语,清楚地传达组件的功能。
jsx
// 推荐:描述性组件名
<UserProfile />
// 避免:含糊不清的组件名
<Component1 />
- 变量和函数:选择变量和函数名称,解释其用途或它们所持有/操作的数据。变量使用 camelCase(驼峰命名法),函数使用小写字母。
js
// 推荐:描述性变量和函数名
const userDisplayName = 'John Doe'
function calculateTotalPrice(items) {
// ...
}
// 避免:含糊不清的简写名
const uName = 'John Doe'
function calc(items) {
// ...
}
- 文件和目录:以反映其内容的方式命名文件和目录。使用小写字母、连字符和下划线提高可读性。
md
// 推荐: 描述性文件和目录名
UserProfile.js
user-profile/
- Props 和状态:在传递 Props 或定义组件状态时,为它们命名,表明其用途和它们保存的数据。
- 事件句柄:定义事件处理函数时,请使用描述它们执行的操作或它们处理的事件的名称。
js
// 推荐:描述性事件处理名
function handleButtonClick() {
// ...
}
// 避免:通用事件处理名
function handleClick() {
// ...
}
- 注释:如有必要,请使用注释为复杂代码提供其他上下文或解释。但是,应优先编写心照不宣且没有过多注释的代码。
描述性命名促进了清晰的沟通,并辅助未来开发者(包括未来的您自己)理解 React App 中各种元素的用途和功能。命名元素时,请考虑拥有易于阅读、维护和协作的代码的长期好处。
3. 一致的格式
React 的一致格式涉及在整个 App 中遵守一组代码布局、缩进、间距和其他风格方面的规则和约定。格式的一致性提高了代码的可读性、可维护性和开发者之间的协作。以下是在 React 中保持一致格式若干准则:
- 缩进:对每个嵌套级别使用一致的缩进。常见的选择是为每个级别使用两个或四个空格。选择一种风格并在整个项目中坚持下去。
jsx
// 使用空格缩进的示例
function MyComponent() {
return (
<div>
<p>Hello, world!</p>
</div>
)
}
- 花括号位置:将左花括号放在与其关联区块的同一行。这是 JS 的常见做法。
js
// 推荐:左花括号在同一行
function MyComponent() {
// ...
}
// 避免:左花括号在新行
function MyComponent() {
// ...
}
- 空格和格式:在运算符、对象字面量内部以及括号前/后使用一致的空格。在逗号和冒号后添加空格。
- 代码行长度:旨在保持代码行的合理长度(通常约为 80-100 个字符),提高可读性。如果一行太长,请考虑将其分成多行。
jsx
// 推荐:换行以保持代码行长度可控
<MyComponent prop1={value1} prop2={value2} prop3={value3} />
// 避免:降低可读性的长代码
<MyComponent prop1={value1} prop2={value2} prop3={value3} prop4={value4} prop5={value5} />
- 一致的命名:对变量、函数、组件和其他标识符使用一致的命名约定。选择命名样式(camelCase、PascalCase(帕斯卡命名法)等)并统一应用。
- 注释 :遵循一致的评论样式。使用单行注释(
//
) 进行简短解释,使用多行注释(/* */
)进行较长解释或区块注释。 - 组织导入:保持导入的逻辑井井有条和分组。您可以使用 ESLint 或 Prettier 等工具自动格式化导入。
- 代码对齐:垂直对齐相似的代码块,以获得更好的视觉分组和清晰度。
- 代码格式化:考虑使用代码格式化工具(比如 Prettier)在代码库中自动强制实施一致的格式。这些工具可以集成到您的开发工作流程中,以确保格式保持一致,而无需手动操作。
通过在整个 React 代码库中保持一致的格式设置实践,您可以让开发者更容易理解代码并参与代码,降低出错的风险并提高整体代码质量。
4. 避免魔术数字和字符串
在编程中,"魔术数字(Magic Numbers)"是指直接出现在代码中而没有任何解释的数值。同样,"魔术字符串"是指在没有上下文的情况下直接在代码中使用的字符串值。这两者都会使代码难以理解和维护。
将魔术数字和字符串替换为常量或变量,提高代码可维护性并降低出错的风险。
js
// 最渣事件
if (status === 2) { ... }
// 使用常量
const STATUS_COMPLETED = 2;
if (status === STATUS_COMPLETED) { ... }
5. 解构
解构是编程语言中的一项功能,它允许您从数组、对象或元组等数据结构中提取单个元素,以更简洁直观的方式将其赋值给变量。它简化了从复杂数据结构中提取值的过程,使代码更简洁易读。
解构对象和数组可以增强代码可读性,使您的意图更清晰。
js
// 反面教材
const username = user.name
const age = user.age
// 使用解构赋值
const { name: username, age } = user
6. 避免嵌套三v算符
三元运算符(通常简称为"三元")是编写条件表达式的简明方法。它允许您快速评估条件,并根据条件是 true
还是 false
返回两个值之一。三元运算符采用以下形式:
js
condition ? expression_if_true : expression_if_false
虽然三元运算符很简洁,但要避免深度嵌套,因为这会使代码难以理解。相反,请考虑使用 if
语句或将复杂的逻辑分解为单独的函数。
7. 注释和文档
注释用于在代码中提供说明、注释或上下文。它们不会被 JS 引擎执行,也不会影响代码的行为。注释对于使其他开发者(包括未来的您自己)能够理解您的代码至关重要。JS 中有两种类型的注释:单行注释和多行注释。
使用注释来解释代码中的复杂逻辑、意图或决策。虽然但是,请努力编写需要最少注释的不言而喻的代码。
8. 复用和模块化
通过创建可复用组件和工具函数来复用代码。这样可以减少代码重复,并使代码库更易于维护。
9. 状态管理
React 状态管理是指管理和控制定义组件或 App 的行为和外观的数据的过程。React 组件可以具有内部状态,即可以随时间变化并在更新时触发重新渲染的数据。虽然但是,随着 App 熵增,跨组件管理状态可能会变得复杂。状态管理技术方案通过提供管理、共享和更新状态的结构化方案,辅助处理这种复杂性。
有若干状态管理技术方案可用于 React App,每种技术方案都适用于不同级别的复杂性和用例:
1. 局部组件状态:
对于交互性有限的简单组件,使用 useState
钩子或 this.state
以及 this.setState
方法(针对类组件)管理组件内的状态足矣。
2. Context API:
Context API 是一个内置技术方案,用于在多个组件之间共享状态,而无需通过中间组件传递 props。它适用于中级的状态共享。
3. Redux:
Redux 是一个人气状态管理库,它为 App 状态提供了一个中心化 store。它对于具有大量共享状态或组件之间频繁交互的复杂 App 特别有用。
4. MobX:
MobX 是另一个状态管理库,旨在通过使用可观察对象来简化状态管理。它非常适合首选更被动的状态管理方法的 App。
5. React Query:
React Query 专注于管理异步数据,比如获取和缓存 API 响应。它对于管理远程数据以及处理数据提取、缓存、同步等特别有用。
6. Apollo 客户端:
如果您的 App 使用了 GraphQL API,Apollo Client 提供了一个给力的技术方案来管理数据、查询和变更。它提供了用于缓存、数据规范化和订阅支持的工具。
根据 App 的复杂性和项目的特定要求选择状态管理技术方案。无论采用何种技术方案,状态管理的主要目标是使 App 的数据保持一致、可维护,并可供需要它的组件访问,同时最大限度地减少不必要的重绘。
10. 错误处理
React 错误处理涉及管理和响应 App 执行过程中可能发生的意外错误。正确的错误处理通过提供有意义的反馈和防止崩溃来增强 UX(用户体验)。以下是关于 React 中错误处理的简述:
错误边界:
React 提供了一个"错误边界"的概念来优雅地处理组件的错误。错误边界是一个 React 组件,它在其子组件树中的任何位置捕获 JS 错误并展示备胎 UI,而不是让整个 App 崩溃。您可以使用 componentDidCatch
生命周期方法定义错误边界。
try-catch 区块:
为了处理组件方法中的错误,您可以使用标准的 JS try
和 catch
块。这对于异步操作(比如 API 调用)特别有用。
jsx
class MyComponent extends React.Component {
async fetchData() {
try {
const response = await fetch('https://api.example.com/data')
const data = await response.json()
// 数据处理
} catch (error) {
console.error('Error fetching data:', error)
// 向用户展示错误信息
}
}
render() {
// ...
}
}
展示错误信息:
发生错误时,请务必展示清晰且用户友好的错误消息,辅助用户了解已有问题。您可以根据错误处理的状态在组件中渲染错误消息。
jsx
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = { error: null }
}
async fetchData() {
try {
// ...
} catch (error) {
console.error('Error fetching data:', error)
this.setState({ error: 'An error occurred while fetching data.' })
}
}
render() {
const { error } = this.state
return (
<div>
{error ? <p>{error}</p> : <p>Data loaded successfully!</p>}
{/* ... */}
</div>
)
}
}
通过实现错误边界、使用 try-catch 区块和提供信息丰富的错误消息,您可以创建更给力且用户友好的 React App,该 App 可以优雅地处理意外错误并提高整体用户满意度。
11. 测试
测试是软件开发的一个关键方面,它确保您的 React App 如期运行,即使在它们迭代时也足够可靠。适当的测试有助于及早发现错误,提供对代码更改的信心,并有助于提高 App 的整体稳定性。以下是关于在 React 中进行测试的简短说明:
React 测试类型:
- 单元测试:这涉及单独测试单个组件或功能,以验证它们是否如期工作。用于单元测试 React 组件的常用库包括 Jest 和 React Testing Library。
- 集成测试:集成测试侧重于不同组件之间的交互,确保它们能够正确地协同工作。这些测试有助于识别组合组件时可能出现的问题。
- E2E(端到端)测试:E2E 测试模拟用户与整个 App 的交互。Cypress 和 Selenium 等工具用于测试用户流和交互。
React 测试福利:
- Bug 预防:编写测试有助于在 Bug 到达生产环境之前识别和解决它们,降低用户出现意外问题的可能性。
- 代码置信度:经过充分测试的代码使您确信,在重构或添加新功能时,所做的更改不会破坏现存功能。
- 文档:测试可作为代码预期工作方式的文档。它们提供了对组件预期行为的见解。
React 测试库:
- Jest:Jest 是一个人气的 JS App 测试框架,以其易用性、内置断言和并行运行测试的能力而闻名。
- React Testing Library:该库专注于测试用户与组件交互的方式。它鼓励编写类似于用户与应用交互方式的测试。
- Cypress:Cypress 是一个 E2E 测试框架,可让您编写在真实浏览器中运行的测试。它提供了一种可视化和交互式的方式来测试您的 App。
诉诸 Jest 和 React Testing Library 进行 React 示例测试:
jsx
// MyComponent.js
import React from 'react'
function MyComponent(props) {
const { value } = props
return <div>{value}</div>
}
export default MyComponent
jsx
// MyComponent.test.js
import React from 'react'
import { render } from '@testing-library/react'
import MyComponent from './MyComponent'
test('renders value prop correctly', () => {
const { getByText } = render(<MyComponent value="Test Value" />)
const valueElement = getByText('Test Value')
expect(valueElement).toBeInTheDocument()
})
在此示例中,一个简单的测试检查传递给 MyComponent
的 value
prop 是否正确渲染。
12. 代码审查
与任何软件开发过程一样,React 中的代码审查涉及团队成员对代码更改的协作检查。这种做法有助于确保代码库的质量、正确性和可维护性。以下是关于 React 中代码审查的简短说明:
代码审查的目的:
- 质量保证:代码审查有助于在错误、逻辑错误和潜在问题到达生产环境之前发现它们。这使 App 更加稳定和可靠。
- 知识共享:审查代码使团队成员接触到不同的编码风格、最佳实践和技术方案,促进学习和技能提升。
- 一致性:代码审查强制执行编码标准、命名约定和设计模式,从而在整个项目中生成一致且连贯的代码。
- 优化:代码审查提供了一个机会,可以提出改进、优化和更优雅的技术方案。
完结撒花
编写精简的 React 代码不仅仅是为了美观;这为可维护和可扩展的 App 奠定基础。通过遵循这些最佳实践,您不仅可以创建有效的代码,还可以创建更易于理解、修改和协作的代码。请记住,精简代码是一项持续的工作,随着您不断提高编码技能,您的 React App 将受益于更高的清晰度和可维护性。
您现在收看的是《前端翻译计划》,学废了的小伙伴可以订阅此专栏合集,我们每天佛系投稿,欢迎持续关注前端生态。谢谢大家的点赞,掰掰~