引言
React 和 TypeScript 是现代前端开发中最受欢迎的组合之一。React 提供了灵活的组件化开发方式,而 TypeScript 则通过静态类型检查提升了代码的可靠性和可维护性。然而,在实际开发中,如何高效地结合两者并遵循最佳实践,是每个开发者都需要面对的问题。
本文总结了 React + TypeScript 开发中的 50 条最佳实践与规范,涵盖了代码风格、类型定义、性能优化、常见问题解决方案等多个方面,帮助开发者编写高质量、可维护的代码。
React + TypeScript 开发规范与最佳实践
本文总结了 React + TypeScript 开发中的 50 条规范与经验,涵盖注释、代码风格、类型定义、性能优化等多个方面,帮助开发者编写高质量、可维护的代码。
1. 注释
1.1 文件顶部注释
- 描述文件功能、作者和创建日期。
tsx
// 文件功能:用户信息展示组件
// 作者:GitHub Copilot
// 日期:2023-10-01
1.2 模块注释
- 在模块或函数前添加注释,说明功能和参数。
tsx
/**
* 获取用户信息
* @param id 用户 ID
* @returns 用户信息
*/
const getUser = (id: number) => { /* ... */ };
1.3 业务代码注释
- 对复杂逻辑进行详细说明。
tsx
// 检查用户是否已登录,如果未登录则跳转到登录页
if (!isLoggedIn) {
redirectToLogin();
}
1.4 变量注释
- 对关键变量的用途进行简要说明。
tsx
// 用户的唯一标识符
const userId: number = 123;
2. 引用组件顺序
- 顺序:先引入核心库(如 React),再引入第三方库,最后引入本地组件。
- 分组:使用空行分隔不同类型的引入。
tsx
// 核心库
import React from 'react';
// 第三方库
import { Button } from 'antd';
// 本地组件
import UserCard from './UserCard';
3. 引号
- 统一使用单引号,避免混用。
tsx
const message = 'Hello, React + TypeScript!';
4. 缩进
- 使用 2 个空格缩进,保持一致性。
tsx
const App: React.FC = () => {
return (
<div>
<h1>React + TypeScript</h1>
</div>
);
};
5. 分号
- 每行语句后必须加分号,避免自动插入分号的潜在问题。
tsx
const name = 'React';
const age = 10;
console.log(`${name} is ${age} years old.`);
6. 括号
- 代码块必须使用大括号,即使只有一行代码。
tsx
if (age > 18) {
console.log('Adult');
}
7. 空格
- 操作符两侧加空格 ,如
a + b
。 - 函数参数间加空格 ,如
function myFunc(a, b)
。
tsx
const add = (a: number, b: number): number => {
return a + b;
};
8. 换行
- 超过 80 字符换行,逻辑分块换行。
tsx
const longString =
'This is a very long string that exceeds the recommended line length of 80 characters.';
9. 数组、对象
- 数组 :使用
[]
定义数组,避免使用new Array()
。 - 对象:属性按逻辑分组,避免无序。
tsx
const numbers: number[] = [1, 2, 3];
const user = {
id: 1,
name: 'John Doe',
age: 30,
};
10. 命名
- 变量/函数:使用驼峰命名法。
- 组件:使用大驼峰命名法。
- 常量:全大写,单词间用下划线分隔。
tsx
const userName = 'John';
const getUserName = (): string => {
return userName;
};
const UserCard: React.FC = () => {
return <div>{userName}</div>;
};
11. 类型断言
- 尽量避免
as any
,优先使用明确的类型断言。
tsx
const inputElement = document.getElementById('input') as HTMLInputElement;
inputElement.value = 'Hello';
12. interface 声明顺序
- 先声明基础类型,再扩展类型。
tsx
interface BaseProps {
id: number;
}
interface UserProps extends BaseProps {
name: string;
age: number;
}
13. TS 好用的相关工具泛型
- Partial:将类型的所有属性变为可选。
- Pick:从类型中选取部分属性。
- Omit:从类型中排除部分属性。
tsx
type PartialUser = Partial<UserProps>; // 所有属性变为可选
type UserName = Pick<UserProps, 'name'>; // 仅选取 name 属性
type UserWithoutAge = Omit<UserProps, 'age'>; // 排除 age 属性
14. TS 一些好用的小 Tips
- 非空断言 :确保值不为
null
或undefined
。
tsx
const userName: string | null = 'John';
console.log(userName!); // 非空断言
15. 规范其他
- 代码风格统一:使用 ESLint 和 Prettier。
- 路径别名:通过
tsconfig.json
配置。
json
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@components/*": ["components/*"],
"@hooks/*": ["hooks/*"]
}
}
}
16. State 声明
- 仅当初始 state 需要从 props 计算得到时,才在构造函数中声明 state。
- 其他情况下使用静态属性声明 state。
tsx
class MyComponent extends React.Component {
state = {
count: 0,
};
}
17. 渲染默认值
- 为可能为空的数据提供默认值。
tsx
const UserCard: React.FC<{ name?: string }> = ({ name = 'Guest' }) => {
return <p>姓名: {name}</p>;
};
18. 不确定的属性
- 避免滥用
...
展开运算符,确保属性存在。
tsx
const props = { name: 'John', age: 30 };
const user = { ...props, gender: 'male' }; // 确保属性存在
19. 数据格式转换
- 通过工具函数集中处理。
tsx
const formatUser = (user: UserProps): string => {
return `${user.name}, ${user.age} years old`;
};
20. 判断条件真假
- 避免直接使用非布尔值作为条件,明确判断真假。
tsx
if (userName !== '') {
console.log('Valid name');
}
21. 简单组件可以使用函数代替
- 对于无状态组件,优先使用函数组件。
tsx
const Greeting: React.FC = () => <h1>Hello, World!</h1>;
22. 对于常用的属性进行缓存
- 避免重复计算属性值。
tsx
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
23. Input 输入框使用 trim()
- 去除用户输入的多余空格。
tsx
const handleInput = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value.trim();
console.log(value);
};
24. 使用 location
跳转前需要先转义
- 防止 URL 注入攻击。
tsx
const safeUrl = encodeURIComponent('/path/to/resource
');
window.location.href = safeUrl;
25. 使用 react-router
- 推荐使用
useNavigate
替代history.push
。
tsx
const navigate = useNavigate();
navigate('/home');
26. 同时开发,数据请求 API 目录 Git 冲突方案
- 将 API 请求模块拆分为多个文件,按功能划分。
tsx
// userApi.ts
export const getUser = () => { /* ... */ };
// productApi.ts
export const getProduct = () => { /* ... */ };
27. 组件嵌套过深
- 通过 Context 或自定义 Hook 减少嵌套。
tsx
const UserContext = createContext<User | null>(null);
28. 代码过滤掉未考虑到的情况
- 为所有分支提供默认处理。
tsx
switch (status) {
case 'success':
// ...
break;
case 'error':
// ...
break;
default:
console.error('Unhandled status');
}
29. 异步请求需要 try-catch
- 捕获错误,避免程序崩溃。
tsx
const fetchData = async () => {
try {
const response = await fetch('/api/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error fetching data:', error);
}
};
30. setState
有三种用法
- 直接赋值、函数式更新、合并更新。
tsx
this.setState({ count: this.state.count + 1 });
this.setState((prevState) => ({ count: prevState.count + 1 }));
31. setState
可能是同步的
- React 的
setState
在某些情况下是异步的 (如事件处理函数中),但在其他情况下(如setTimeout
中)可能是同步的。 - 解决方案 :不要依赖
setState
的立即更新结果,使用回调函数获取最新状态。
tsx
this.setState((prevState) => ({ count: prevState.count + 1 }));
32. 不要在 setState
前面加 await
- 原因 :
await
会导致setState
的执行顺序发生变化,可能引发状态更新问题。 - 解决方案:将异步逻辑与状态更新分开处理。
tsx
const handleClick = async () => {
const data = await fetchData();
this.setState({ data });
};
33. 阻止事件默认行为
- 使用
event.preventDefault()
阻止默认行为,如表单提交或链接跳转。
tsx
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log('Form submitted');
};
34. 在 componentWillUnmount
中去除副作用
- 在组件卸载时清理副作用,如取消订阅、清除计时器等。
tsx
useEffect(() => {
const timer = setInterval(() => console.log('Tick'), 1000);
return () => clearInterval(timer); // 清理计时器
}, []);
35. key
的使用
key
必须唯一 ,避免使用索引作为key
,以防止列表重新渲染问题。
tsx
const items = ['Apple', 'Banana', 'Cherry'];
items.map((item) => <li key={item}>{item}</li>);
36. for-in
中一定要有 hasOwnProperty
的判断
- 避免直接读取原型链上的属性。
tsx
const obj = { a: 1, b: 2 };
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
console.log(key, obj[key]);
}
}
37. 第三方库函数的使用
- 优先使用类型定义完善的第三方库 ,如
lodash
、axios
。 - 避免直接修改第三方库的返回值。
tsx
import _ from 'lodash';
const sorted = _.sortBy([3, 1, 2]);
38. 防止 XSS 攻击
- 避免直接插入 HTML ,使用 React 的
dangerouslySetInnerHTML
时需谨慎。
tsx
const SafeComponent: React.FC<{ content: string }> = ({ content }) => {
return <div>{content}</div>; // 避免直接插入 HTML
};
39. 在组件中获取真实 DOM
- 使用
useRef
获取 DOM 元素。
tsx
const inputRef = useRef<HTMLInputElement>(null);
const focusInput = () => {
if (inputRef.current) {
inputRef.current.focus();
}
};
return <input ref={inputRef} />;
40. 减少魔法数字
- 将魔法数字提取为常量,提升代码可读性。
tsx
const MAX_RETRIES = 3;
for (let i = 0; i < MAX_RETRIES; i++) {
console.log('Retrying...');
}
41. 如果需要优化 React 性能(一般用不到)
- 使用
React.memo
、useMemo
和useCallback
优化性能。
tsx
const MemoizedComponent = React.memo(({ value }: { value: string }) => {
return <p>{value}</p>;
});
42. Event 事件对象类型
- 为事件对象指定正确的类型 ,如
React.MouseEvent
、React.ChangeEvent
。
tsx
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log(e.currentTarget);
};
43. 使用私有属性取代 state
状态
- 对于不需要触发重新渲染的状态,使用私有属性代替
state
。
tsx
const MyComponent = () => {
let privateCounter = 0;
const increment = () => {
privateCounter++;
console.log(privateCounter);
};
return <button onClick={increment}>Increment</button>;
};
44. 代码细粒度的思考
- 将复杂逻辑拆分为多个小函数或组件,提升代码可读性和复用性。
tsx
const calculateTotal = (items: number[]) => items.reduce((a, b) => a + b, 0);
45. if-else
等判断太多了,后期难以维护
- 使用对象映射或策略模式替代过多的
if-else
判断。
tsx
const actions = {
success: () => console.log('Success'),
error: () => console.log('Error'),
};
const handleAction = (type: string) => {
actions[type]?.();
};
46. 不要使用 renderXXX
,要使用函数式组件
- 推荐使用函数式组件代替类组件中的
renderXXX
方法。
tsx
const RenderUser = () => <div>User Component</div>;
47. a
标签安全问题
- 为外部链接添加
rel="noopener noreferrer"
,防止安全问题。
tsx
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
External Link
</a>
48. void 0
替代 undefined
- 使用
void 0
代替undefined
,避免被重写的风险。
tsx
const value = void 0;
49. 前端不要操作 cookie
- 推荐使用第三方库(如
js-cookie
)操作cookie
,避免安全问题。
tsx
import Cookies from 'js
-cookie
';
Cookies.set('token', '12345');
const token = Cookies.get('token');
50. 代码检查插件
- 使用 ESLint 和 Prettier 统一代码风格。
- 推荐规则 :
eslint-plugin-react
、@typescript-eslint
.
bash
# 安装依赖
npm install eslint prettier eslint-plugin-react @typescript-eslint/eslint-plugin --save-dev
总结
通过遵循以上规范和最佳实践,可以显著提升 React + TypeScript 项目的代码质量和开发效率。这些规范涵盖了从代码风格到性能优化的方方面面,适用于团队协作和个人开发。希望本文能为你的开发工作提供帮助!