首页 / 文章 / 当人工智能为你编写React应用却忽视代码整洁性原则时

当人工智能为你编写React应用却忽视代码整洁性原则时

学习七种良好的代码编写习惯——DRY原则、单一职责原则、保护性条款等——以及AI生成的React代码为何常常违反这些习惯,还有相应的修正方法。

2427 词

最近有一位客户交给我一个开发披萨订购网站的项目。

起初看来这个项目的范围还算可控。

一个首页。

一个菜单区域。

不同的披萨类别。

每个产品的单独页面。

一个购物车功能。

一个结账流程。

再加上一个用于管理后台各项事务的管理员面板。

人们的自然想法是:

“这应该很快就能完成。”

随后他们决定借助人工智能编程助手来完成工作。

从这时起事情变得有趣起来。

让人工智能承担大部分编码工作

该项目是用React开发的。

人们没有手动输入每一行代码,而是向人工智能发送指令来生成各部分内容。

比如这样的指令:

“创建披萨卡片组件。”

立刻就能生成对应的组件。

接下来是:

“构建购物车。”

已处理。

接着:

“构建结账页面。”

也已处理。

然后:

“创建一个能显示统计数据、订单信息及客户信息的管理员控制面板。”

已完成。

速度实在令人印象深刻。

通常需要数小时才能完成的工作几分钟内就做好了。

最终成果包括:

  • React组件
  • 表单
  • 数据表格
  • 控制面板小部件
  • API集成
  • 加载指示器
  • 错误处理机制
  • 能自适应屏幕尺寸的布局

而且该应用程序确实可以正常运行。

一切看起来都很不错。

似乎已经找到了理想的开发流程:

想法 → 提示词 → 代码 → 上线

但还缺少一个关键步骤。

审查已构建的内容。

客户从未见过代码本身

从客户的角度来看,一切都很完美。

网站运行流畅。

控制面板功能正常。

菜单显示正确。

订单呈现情况与预期一致。

从外观上看,它十分精致。

那么问题出在哪里呢?

问题从外部是无法察觉的。

实际上,代码库正在悄悄变成一个维护难题。

起初并不明显。

但随着更多功能的添加,某种模式开始显现。

一个念头不断浮现:

“等等……难道没有类似的东西已经被做出来了吗?”

就在那时,一个重要的认识出现了:

AI确实擅长生成可运行的代码。

但它并不会自动产出整洁的代码。

问题一:到处都是重复逻辑

几乎完全相同的逻辑散布在应用程序的各个部分。

例如,有几个独立的函数都在分别计算总数。

其中一个函数的代码如下:

const total = price * quantity;

在其他地方,也有几乎一模一样的代码:

const orderTotal = price * quantity;

而在另一个文件中则是:

const cartAmount = price * quantity;

这三个函数本质上都在执行相同的计算。

从技术层面来看并没有任何问题。

但一旦定价规则需要更改,就必须逐一找到并更新所有这些代码副本。

正是在这时,整洁代码的第一个原则变得清晰起来。

1. DRY原则——避免重复代码

每当出现重复的逻辑时,值得思考的问题是:

能否将其整合为单一来源?

例如:

const calculateItemTotal = (price, quantity) => {
  return price * quantity;
};

从那以后,应用程序的每个部分都可以调用同一个共享函数。

目标并非强制在所有地方实现代码复用。

只是为了消除毫无意义的重复代码

问题#2:单个组件承担了所有功能

有一次,我们打开了其中一个生成的React文件仔细查看。

它的代码量不断增加。

一行又一行,持续膨胀。

那个单一组件负责:

  • API请求
  • 表单状态管理
  • 验证逻辑
  • 模态框的显示/隐藏
  • 表格渲染
  • 结果筛选
  • 分页功能
  • 通知系统
  • 简而言之,有一个文件几乎独自承担了整个应用程序一半的功能。

    显而易见的结论是:

    “维护这个结构会非常麻烦。”

    于是该组件被拆分成了更小的部分。

    不再保持原有的结构:

    Orders.jsx
       ↓
    800+ lines
    

    而是被重构为类似这样的形式:

    Orders
     ├── OrderFilters
     ├── OrderTable
     ├── OrderRow
     ├── OrderModal
     └── useOrders
    

    这一转变直接引出了下一个原则。

    2. 单一职责原则

    一个组件或函数应当只承担一项明确的任务。

    如果无法用一句话描述某个组件的功能,那很可能是它承担了过多的职责。

    例如:

    OrderTable 用于显示订单列表。

    那是一个简洁明了的描述。

    与之相比:

    OrderTable 负责获取订单、检查用户权限、计算总计、触发通知,并将所有信息以表格形式展示。

    那样的描述可是个危险信号。

    问题#3:嵌套的if语句让代码变得混乱

    项目中的某些逻辑曾是这样的:

    if (user) {
      if (user.isActive) {
        if (user.isVerified) {
          // create order
        }
      }
    }
    

    它确实能正常运行。

    但阅读起来十分费力。

    于是它被重写为:

    if (!user) return;
    if (!user.isActive) return;
    if (!user.isVerified) return;
    
    // create order
    

    那个版本一眼就能看懂。

    这时就需要用到下一个习惯:

    3. 提前返回/保护性语句

    与其将核心逻辑埋在多层嵌套的条件中,不如尽早处理无效情况并提前退出。

    Invalid?
       ↓
    Return
    
    Invalid?
       ↓
    ReturnEverything okay?
       ↓
    Do the actual work
    

    这样一来,主逻辑依然保持简洁、易读,且没有不必要的缩进。

    问题 #4:AI为已存在的功能建议了新组件

    这个错误有点尴尬。

    给AI的提示是:

    “创建一个用于删除订单的确认模态框。”

    AI的回应是生成了一个全新的组件。

    它几乎在未经质疑的情况下就被接受了。

    随后对项目进行快速搜索后发现了重要线索:

    其实已经存在模态框组件了。

    本可以直接重用它。

    那一刻让人明白了一个道理:

    AI并不知道某个代码库中已经存在什么。

    即便系统确实知晓,它仍可能选择创建新内容而非重用现有资源。

    更好的做法是给出如下提示:

    “在创建新组件之前,先检查现有代码库中是否有可重用的部分。”

    这一经验引出了另一条规则:

    4. 先重用再创建

    在添加以下内容之前:

    • 新组件
    • 新工具函数
    • 新自定义钩子
    • 新API辅助工具

    应先询问:

    “项目中已经存在这样的内容了吗?”

    优秀的代码库并非由大量可重用组件构成,而是体现在开发者们会重用现有资源,而非默默复制它们。

    问题#5:变量名有时糟糕透顶

    人工智能生成的代码速度极快。

    人们很容易接受这样的变量名:

    const data = ...
    const result = ...
    const x = ...
    const temp = ...
    

    这些名称可以正常编译且不会引发错误。

    但几个月后,data实际上指的是什么?

    是与披萨相关的数据吗?

    还是订单信息?

    客户记录呢?

    或是与控制面板相关的内容?

    因此,随着项目的发展,变量命名应当更加有针对性。

    与其这样写:

    const data = await getOrders();
    

    不如使用更清晰的版本:

    const orders = await getOrders();
    

    再比如:

    const x = users.filter(...);
    

    这样写会清晰得多:

    const activeUsers = users.filter(...);
    

    由此又得出一条简单的规则:

    5. 使用能解释代码含义的名称

    具有描述性的命名往往能让注释完全变得多余。

    读到类似这样的内容时:

    const activeUsers = users.filter(
      (user) => user.isActive
    );
    

    已经清楚地说明了正在发生什么。

    无需再添加:

    // Filter users who are currently active
    

    代码本身就已经说明了一切。

    问题 #6:对本无需优化的内容进行优化

    在这样的项目中,很容易开始产生这样的想法:

    “这段代码需要进一步优化。”

    这往往会导致人们去研究诸如:

    useMemo()
    useCallback()
    

    以及一些其他的性能提升技巧。

    但在这里值得暂停一下。

    优化并不一定是好事。

    以一个简单的计算为例:

    const total = price * quantity;
    

    仅仅因为存在相应的工具,就毫无理由将其包装在复杂的优化模式中。

    这样做只会让代码更难理解。

    这又引出了另一个教训:

    6. 只有在存在真正问题时才进行优化

    不要仅仅因为以下原因就着手优化:

    “网上有人说这种技术更快。”

    相反,应先找出真正的瓶颈。

    某个组件是否频繁重新渲染?

    API调用是否缓慢?

    某项计算是否确实耗时?

    代码包的大小是否过大?

    数据库查询效率是否低下?

    在采取任何措施之前,先确定真正的问题所在。

    只有到那时才应该进行优化。

    干净的代码加上不必要的优化只会带来多余的复杂性。

    问题#7:不再要求AI“解决所有问题”

    这或许是这类项目中最重要的一条经验。

    有时人们会忍不住直接说:

    “帮我把整个项目整理一下。”

    但最好还是先不要这么做。

    在那种情况下,“整理”到底意味着什么?

    人工智能可以这样回应:

    • 重命名变量和文件
    • 重新组织文件夹结构
    • 引入新的抽象概念
    • 合并相关的函数
    • 删除它认为不必要的代码
    • 重构整体架构

    其中一些改动确实能起到帮助作用。

    另一些则可能毫无意义。

    还有一些甚至可能会悄悄引入错误。

    采用更缓慢、分阶段的办法效果更好。

    首先可以这样问:

    “看看这个项目,暂时先不要做任何改动。”

    然后:

    “指出所有重复的逻辑部分。”

    接着:

    “告诉我这些重复内容中哪些真的值得修改。”

    只有在那之后才应该对建议进行审查。

    然后应用一项更改。

    接着进行测试。

    这就成了第七条规则:

    7. 重构前先理解

    绝不要让人工智能在未完全理解代码的情况下对其进行修改。

    先从分析开始。

    确保理解其逻辑。

    再决定该怎么做。

    然后进行更改。

    最后通过测试来验证。

    一个披萨网站所体现的道理

    有趣的是,客户很少会问:

    “你的代码整洁吗?”

    他们通常会问更简单的问题:

    “网站能正常使用吗?”

    而大多数情况下答案确实是肯定的。

    不过,开发者承担的责任远不止第一个可用的版本。

    软件一旦发布就很少会保持不变。

    最终客户会提出这样的要求:

    "我们能添加配送追踪功能吗?"

    接着是:

    "让我们加上折扣码吧。"

    然后是:

    "我们需要支持多个分支。"

    再然后是:

    "为餐厅员工添加账户功能。"

    最后是:

    "我们希望有一些报表功能。"

    没过多久,那个小小的披萨网站就发展成了一个功能完备的系统。

    正是在这个时候,整洁的代码结构才真正体现出其价值。

    错不在人工智能

    在这里应该做到公平对待。

    人工智能并非罪魁祸首。

    在这样的开发过程中,它确实具有极高的价值。

    它能帮助实现:

    • 提升工作效率
    • 尝试不同的方法
    • 处理重复性的编码任务
    • 定位错误
    • 构建组件框架
    • 理性思考解决方案

    真正的问题从来不是人工智能在生成代码。

    问题在于有时生成的代码在没有经过充分审查的情况下就被接受了。

    这是两个截然不同的问题。

    结合人工智能的改进型编码工作流程

    完成这样的项目后,调整工作方式是很有必要的。

    整个流程大致如下:

    Understand the requirement
              ↓
    Explore existing code
              ↓
    Plan the solution
              ↓
    Ask AI for implementation
              ↓
    Review the generated code
              ↓
    Simplify
              ↓
    Test
              ↓
    Refactor if necessary
    

    人工智能仍然承担着大部分的实际编码工作。

    但更多的思考任务则回归到开发者身上。

    这种平衡似乎正是整个软件开发领域的发展方向。

    AI负责编写代码,但代码库仍属于你

    这就是整个体验带来的核心启示。

    一旦AI开始为你生成代码,人们很容易产生这样的想法:

    “这是AI写的,它肯定明白自己在做什么。”

    但这种想法并不成立。

    代码库的所有权始终在你手中。

    未来需要持续维护它的人是你。

    出现漏洞时必须去排查问题的人也是你。

    需要向他人解释其运作原理的人还是你。

    数月后再次回顾时仍需理解其逻辑的人也是你。

    说不定将来会有其他开发者打开这个项目并疑惑:

    “采用这种方法的理由是什么?”

    理想情况下,代码本身就应该让这种逻辑清晰可见。

    七项值得坚持的优质代码习惯

    总结整个经历后,以下内容让我印象深刻:

    1. DRY原则

    除非有充分理由,否则避免在多个地方重复相同的逻辑。

    2. 单一职责原则

    确保每个函数或组件的功能都明确且单一。

    3>尽早返回原则

    减少嵌套结构,使主要逻辑路径更易于理解。

    4>先复用再创建原则

    在开发新内容之前,先在代码库中寻找现有解决方案。

    5>良好的命名原则

    选择能够清晰表达变量或函数含义的名称。

    6>有依据地优化原则

    除非存在可量化的性能问题,否则不要随意增加复杂性。

    7>重构前先理解原则

    AI可以提出修改建议,但哪些建议值得采纳由您决定。

    总结

    在开始这个披萨网站项目时,我原以为AI辅助的主要好处会是:

    极高的速度。

    现在回想起来,我发现还有更大的好处。

    AI让开发者有更多精力去处理那些真正需要判断力的开发工作。

    我们不必把时间浪费在重复性的基础工作中,而是可以将精力投入到更有价值的问题上:

    这真的是正确的做法吗?

    能否让它变得更简单?

    项目中已经存在类似的功能了吗?

    当新的功能加入时,这个设计还能适用吗?

    其他开发者也能理解并遵循这个设计吗?

    这正是使用人工智能辅助开发时面临的真正难题。

    人工智能几乎可以瞬间生成数百行代码。

    而确认这些代码确实有存在的必要仍需由我们来完成。

    既然人工智能已融入开发流程,或许这就是“整洁代码”的新定义:

    让代码运行起来并非终点,能够后续维护它才是。

    相关阅读

  • 三种能优化 React 应用架构的 TypeScript 模式 — 了解 Repository、Observer 和 Builder 模式如何利用 TypeScript 的类型系统来打造更简洁、更易维护的 React 和 Next.js 代码库。