将您的代码带到另一边
在今天的冒险之旅中,我将带您探索代码开发的另一面。我们将携手探索代码重构的艺术,看看它如何彻底改变您的代码库。听起来太棒了!经历这段冒险之后,您将学会如何将您的代码从“优秀”提升到“卓越”,或从“卓越”迈向“更高水平”。您是一名开发人员,您有责任将您的应用程序的性能、可扩展性和可维护性提升到新的高度。
随着软件项目的发展,代码库变得复杂且难以管理是很常见的。这时,代码重构便应运而生。它是一个在不改变现有代码行为的情况下,以更高效、更有效的方式重构现有代码,从而改进现有代码的过程。
重构不会给软件或产品添加任何新特性或功能。它可以提高代码库的质量、可读性和可维护性。从长远来看,这将使代码库更易于开发。
为什么要重构代码?
提高代码质量:
重构通过降低代码复杂度、消除重复并改进代码的整体设计来提高代码质量。最终,代码将更易于阅读和理解,从而降低引入错误或缺陷的风险。
提高可维护性:
通过降低代码复杂性并改进代码设计,重构可以更轻松地长期维护和更新代码。这可以节省项目开发人员的时间和精力,并降低在进行更改时引入错误的风险。
更好的性能:
重构可以通过优化算法、删除不必要的代码和减少资源使用来提升代码性能,从而缩短执行时间并提高效率。
更轻松的协作:
通过提高代码质量并降低复杂性,重构可以让开发人员更轻松地开展项目协作。这可以提高团队生产力,并降低在共享代码上引入错误或缺陷的风险。
随着我们的冒险不断前进,现在是时候执行代码重构并突破到另一边了。
代码格式化
在团队协作或维护代码库时,务必考虑代码格式。不同的开发人员对缩进、换行符和引号的偏好可能有所不同。这会导致代码风格不一致。不一致的代码风格会使代码看起来杂乱无章,难以阅读。因此,在整个项目中保持一致的代码风格至关重要。
使用Prettier等重构工具大有裨益。如果您不熟悉 Prettier,我们来简单介绍一下。Prettier是.prettierrc
一款流行且用户友好的自动化代码格式化工具。添加到项目后,它会根据预设的样式设置来格式化代码。您也可以根据自己的喜好创建包含以下配置的文件来自定义格式化规则:
{
"arrowParens": "always",
"bracketSameLine": false,
"bracketSpacing": true,
"embeddedLanguageFormatting": "auto",
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"jsxSingleQuote": false,
"printWidth": 80,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"requirePragma": false,
"semi": true,
"singleAttributePerLine": false,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false,
"vueIndentScriptAndStyle": false
}
React 中没有状态或生命周期方法的类组件
原始代码:
import React, { Component } from "react";
class Adventure extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log("Migration happened");
}
render() {
return (
<div>
<p>Break on Through To the Other Side</p>
<button onClick={this.handleClick}>Migrate me</button>
</div>
);
}
}
重构代码:
import React from "react";
function Adventure() {
const handleClick = () => {
console.log("Migration happened");
};
return (
<div>
<p>Break on Through To the Other Side</p>
<button onClick={handleClick}>Migrate me</button>
</div>
);
}
如果组件需要状态或生命周期方法,则使用类组件,否则使用函数组件。
从React 16.8开始,随着Hooks的加入,你可以在函数组件中使用状态、生命周期方法和其他一些之前只能在类组件中使用的功能。因此,我们始终建议使用函数组件,除非你需要的 React 功能(例如错误边界)在函数组件中尚不存在。
避免<div>
在 React 中额外包装
原始代码:
import React from "react";
function Adventure() {
return (
<div>
<p>Break on Through To the Other Side</p>
<button>Migrate me</button>
</div>
);
}
重构代码:
import React, { Fragment } from "react";
function Adventure() {
return (
<Fragment>{/* or use a shorter syntax <> </> */}
<p>Break on Through To the Other Side</p>
<button>Migrate me</button>
</Fragment>
);
}
这是 React 中一种常见的模式,用于一个组件返回多个元素。Fragment允许你将子元素列表分组,而无需在 DOM 中添加额外的节点。
为什么片段比容器更好divs
?
- 由于不创建额外的 DOM 节点,Fragment 的速度更快,内存占用更少。这只有在非常大且深度极深的树上才会真正发挥作用。
- 一些 CSS 机制(如 Flexbox 和 CSS Grid)具有特殊的父子关系,
divs
在中间添加会难以保持所需的布局。
命名约定
命名是编写简洁易维护代码的重要环节,需要花时间为函数和变量选择描述性强且有意义的名称。在命名函数和变量时,重要的是选择那些一目了然、能够传达代码目的和功能的名称。
对于 React,组件名称应始终采用 Pascal 大小写UserDashboard
,Dashboard
如 等。使用 Pascal 大小写来区分组件与默认 JSX 元素标签。
组件内部定义的方法/函数应该采用驼峰式命名法getUserData()
,showUserData()
如 等。
对于应用程序中全局使用的常量字段,请尝试仅使用大写字母。例如,
const PI = 3.14;
从代码中删除不必要的注释
原始代码:
import React, { Fragment } from "react";
function Adventure() {
console.log("test");
return (
<Fragment>
{/* <h1>Adventure</h1> */}
<p>Break on Through To the Other Side</p>
<button>Migrate me</button>
</Fragment>
);
}
重构代码:
import React from "react";
function Adventure() {
return (
<>
<p>Break on Through To the Other Side</p>
<button>Migrate me</button>
</>
);
}
仅在需要的地方添加注释,以便您以后更改代码时不会感到困惑。
另外,不要忘记删除诸如console.log
调试器、未使用的注释代码之类的语句。
在 React 中解构 Props
原始代码:
function Adventure(props) {
return (
<>
<h1>Hello, {props.username}</h1>
<button>Migrate me</button>
</>
);
}
重构代码:
function Adventure({ username }) {
return (
<>
<h1>Hello, {username}</h1>
<button>Migrate me</button>
</>
);
}
ES6中引入了解构。JavaScript 函数中的这种特性允许你轻松提取表单数据并从对象或数组中分配变量。此外,解构 props 使代码更简洁易读。
尊重进口秩序
原始代码:
import { formatCurrency, toNumber } from "@/utils";
import { useDrag } from "react-dnd";
import "./styleB.css";
import { useFormik } from "formik";
import React, { useEffect, useState } from "react";
import Button from "@/components/Button";
import TextField from "@/components/TextField";
import PropTypes from 'prop-types';
重构代码:
import React, { useEffect, useState } from "react";
import PropTypes from 'prop-types';
import { useDrag } from "react-dnd";
import { useFormik } from "formik";
import { formatCurrency, toNumber } from "@/utils";
import Button from "@/components/Button";
import TextField from "@/components/TextField";
import "./styleB.css";
组织导入是编写干净且可维护的 React 代码的必需部分。
进口订单:
- 反应导入
- 库导入(按字母顺序)
- 从项目中绝对导入(按字母顺序)
- 相对导入(按字母顺序)
- 导入 * 作为
- 进口 '。/。
每种类型都应该用空行分隔。这样可以让你导入的所有组件、第三方库等都清晰易懂。
将代码拆分成多个较小的函数。每个函数承担单一职责。
编写简洁易维护代码的关键原则之一是保持函数和组件简洁且专注。这意味着将代码拆分成多个较小的函数,每个函数承担单一职责。
当一个函数或组件承担过多职责时,它会变得更加难以阅读、理解和维护。通过将其分解为更小、更集中的函数,可以提高代码的可读性和可维护性。
不要重复自己
“不要重复自己”(DRY)是软件开发的基本原则。它鼓励开发人员避免在整个代码库中重复代码或逻辑。DRY 原则是编写可维护和可扩展代码的必要条件,它适用于编码标准和代码重构。
在编码规范方面,DRY 原则建议您避免在代码库中重复代码或逻辑。这意味着您应该努力编写可在应用程序中多个位置使用的可重用代码。例如,与其为应用程序中的每个输入字段编写相同的验证逻辑,不如编写一个可重用的函数来执行验证,并在每个输入字段中使用它。
强烈建议避免在渲染中使用箭头函数
提高组件性能的一种方法是避免在 render() 方法中使用箭头函数。
下面的实例有什么问题?
每次渲染组件时,都会创建一个新的函数实例。实际上,如果组件只渲染一两次,这没什么大不了的。但在其他情况下,它会影响性能。render() 方法中的箭头函数可能会导致组件不必要的重新渲染。
原始代码:
function Adventure() {
const [message, setMessage] = useState("Migration not started");
return (
<>
<h1>Mesage: {message}</h1>
<button onClick={() => setMessage("Migration happened")}>
Migrate me
</button>
</>
);
}
因此,如果您关心性能,请在渲染中使用之前声明该函数,如下所示:
重构代码:
function Adventure() {
const [message, setMessage] = useState("Migration not started");
const onMessage = () => {
setMessage("Migration happened");
};
return (
<>
<h1>Mesage: {message}</h1>
<button onClick={onMessage}>Migrate me</button>
</>
);
}
除了在方法中使用箭头函数外render()
,您还可以在方法外部将方法声明为实例方法render()
。这样可以确保该方法仅创建一次,并且不会在每次渲染时重新创建。
减少 React Bundle 大小
在 React 应用中使用第三方库时,保持包大小尽可能小非常重要。过大的包大小会对应用的性能产生负面影响,尤其是在网速较慢的情况下。
减少包大小的一种方法是仅加载库中实际需要的部分。无需导入整个库,您可以使用一种名为“摇树优化 (tree shake)”的技术,只导入所需的特定方法。这可以显著减少包的大小。
假设你正在使用流行的Lodash库,并且只需要从pick()
对象中获取特定属性的方法。你无需导入整个 Lodash 库,只需导入该pick()
方法即可,如下所示:
import pick from 'lodash/pick';
const pickedUserProps = pick(userProps, ['name', 'email']);
而不是
import lodash form 'lodash';
const pickedUserProps = lodash.pick(userProps, ['name', 'email']);
用于useMemo
缓存昂贵的计算
在构建 React 应用时,执行计算或生成数据是很常见的,这些计算或数据可能耗费大量的计算资源。这可能会对应用的性能产生负面影响,尤其是在频繁或大量执行这些计算的情况下。
为了解决应用程序的性能问题,您可以使用React 中的useMemo钩子来缓存昂贵的计算。useMemo
这是一个内置的 React 钩子,可以记住函数的结果,以便仅在其依赖项发生变化时重新计算。
假设你有一个组件,它根据从 API 获取的数据生成一个项目列表。该列表是使用一个执行一些昂贵计算的函数生成的:
function generateList(data) {
// Perform some expensive calculations here
// ...
return list;
}
function Adventure() {
const [data, setData] = useState([]);
useEffect(() => {
// Fetch data from API
// ...
setData(data);
}, []);
const list = generateList(data);
return (
<ul>
{list.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
在上述示例中,generateList
即使数据没有发生变化,每次组件重新渲染时都会调用该函数。这可能会导致不必要的计算,并降低应用程序的性能。
为了优化这一点,您可以使用useMemo
钩子来缓存结果generateList
:
function Adventure() {
const [data, setData] = useState([]);
useEffect(() => {
// Fetch data from API
// ...
setData(data);
}, []);
const list = useMemo(() => generateList(data), [data]);
// ...
}
删除过多的 if-else 条件
If-else 语句是大多数编程语言的常见功能,它们通常用于根据特定条件控制代码执行流程。然而,过多的 if-else 语句会使代码难以阅读、维护和调试。
去除过多 if-else 条件的一种方法是使用 switch 语句。switch 语句与 if-else 语句类似,但它旨在以更简洁、更高效的方式处理多种情况。我们来看一个例子:
function checkGrade(grade) {
if (grade >= 90) {
return "A";
} else if (grade >= 80) {
return "B";
} else if (grade >= 70) {
return "C";
} else if (grade >= 60) {
return "D";
} else {
return "F";
}
}
在上面的例子中,我们使用 if-else 语句来检查学生的成绩并返回字母等级。此代码可以使用 switch 语句重构:
function checkGrade(grade) {
switch (true) {
case (grade >= 90):
return "A";
case (grade >= 80):
return "B";
case (grade >= 70):
return "C";
case (grade >= 60):
return "D";
default:
return "F";
}
}
如你所见,switch 语句比 if-else 语句更简洁、更易读。它还能让我们更高效地处理多种情况。
另一种去除过多 if-else 条件的方法是使用对象字面量。对象字面量是 JavaScript 中定义键值对的一种方式。
为了向你展示我的意思:
function getAnimalSound(animal) {
if (animal === "dog") {
return "woof";
} else if (animal === "cat") {
return "meow";
} else if (animal === "cow") {
return "moo";
} else {
return "unknown";
}
}
在上面的例子中,我们使用了 if-else 语句来返回动物的声音。这段代码可以用对象字面量来重构:
function getAnimalSound(animal) {
const animalSounds = {
dog: "woof",
cat: "meow",
cow: "moo"
}
return animalSounds[animal] || "unknown";
}
如你所见,对象字面量方法比 if-else 和 switch 语句更简洁易读。它还允许我们以更灵活的方式定义键值对。
冒险结束
代码重构就是突破现有代码的局限性,并努力打造更优秀、更高效的版本。这可能是一项艰巨的任务,但只要拥有正确的思维方式、工具和最佳实践,你就能将代码提升到一个新的高度。
代码重构不仅能提升代码质量,还能提升应用的整体用户体验。通过努力重构代码,您可以打造一个响应更快、更直观、更引人入胜的应用,让用户爱不释手。
所以,不要害怕突破。拥抱代码重构的过程,见证你的应用走向新的高度。
动机
🍀支持
请考虑关注并支持我们,订阅我们的频道。非常感谢您的支持,这将帮助我们继续创作您喜爱的内容。提前感谢您的支持!
