译:别这么做(除非你想要糟糕的前端代码)

37 阅读8分钟

别这么做(除非你想要糟糕的前端代码)

前端代码很容易变得杂乱无章。

在这篇文章中,如果你想让前端代码保持整洁且易于维护,我将向你展示需要避免的 17 件事

让我们开始吧👇

错误 #1:使用过多全局变量

全局变量在应用的任何地方都可以访问,这使得它们存在风险。

如果你在某个随机的地方修改了一个全局变量,可能会无意中破坏其他地方的功能,这调试起来简直是噩梦。

它们还会让测试变得更加困难,因为函数的行为可能会根据全局状态而改变。

请使用函数参数或作用域内的变量来代替。

Bad:

let userRole = 'guest';

function canEditPost() {
  return userRole === 'admin';
}

userRole = 'admin'; // 意外地改变了应用程序的行为

Better:

function canEditPost(userRole) {
  return userRole === 'admin';
}

const role = 'admin';
canEditPost(role);

错误 #2:糟糕的变量名和函数名

使用像 xdatahandleStuff 这样含糊不清的名称会拖慢每个人的速度。

读者(包括未来的你)将需要猜测每个东西的作用 —— 更糟糕的是,还得四处滚动代码去查找。

一个很好的经验法则是:作用域越大,名称就应该越具描述性。

短循环?用 i 没问题。

作用域较长?使用能完整说明情况的名称。

Bad:

function d(a, b) {
  return a - b;
}

const x = d(100, 50);

Better:

function calculateDiscount(originalPrice, discountAmount) {
  return originalPrice - discountAmount;
}

const finalPrice = calculateDiscount(100, 50);

错误 #3:“万能”函数

这些函数试图在一个地方完成所有事情 —— 验证输入、更新用户界面、获取数据、处理错误等。

结果呢?函数变得冗长、杂乱,测试和调试起来十分痛苦。

相反,应该将逻辑拆分成更小、更专注的函数

这样做提高了代码的可读性,使编写单元测试变得更容易,还能帮助你在其他地方复用逻辑。

Bad:

function handleFormSubmit(event) {
  event.preventDefault();

  const name = event.target.name.value;
  const email = event.target.email.value;

  if (!name || !email.includes("@")) {
    alert("Invalid form");
    return;
  }

  setLoading(true);

  fetch("/api/submit", {
    method: "POST",
    body: JSON.stringify({ name, email }),
  })
    .then(res => res.json())
    .then(data => {
      setUser(data.user);
      navigate("/dashboard");
    })
    .catch(() => {
      alert("Something went wrong");
    })
    .finally(() => setLoading(false));
}

Better:

function handleFormSubmit(event) {
  event.preventDefault();

  const formData = getFormData(event);

  if (!isValidForm(formData)) {
    alert("Invalid form");
    return;
  }

  submitForm(formData);
}

function getFormData(event) {
  return {
    name: event.target.name.value,
    email: event.target.email.value,
  };
}

function isValidForm({ name, email }) {
  return name && email.includes("@");
}

async function submitForm(data) {
  try {
    setLoading(true);
    const response = await fetch("/api/submit", {
      method: "POST",
      body: JSON.stringify(data),
    });

    const result = await response.json();
    setUser(result.user);
    navigate("/dashboard");
  } catch (e) {
    alert("Something went wrong");
  } finally {
    setLoading(false);
  }
}

错误 #4:带有多个同类型参数的函数

当一个函数有多个相同类型的参数(如布尔值或字符串)时,很容易将它们混淆。

相反,传递一个带有命名键的对象

这样一来,每个值的用途就很清晰了,而且你也无需跳转到函数定义处去理解发生了什么。

Bad:

createUser("Alice", true, false, true);

Better:

createUser({ name: "Alice", isAdmin: true, isVerified: false, isActive: true });

错误 #5:未清理资源

每当你添加事件监听器、创建定时器、设置超时函数,或者在 React 中使用 useEffect 时,一旦这些资源不再需要,就应该清理它们。

如果你不这样做,可能会导致内存泄漏堆叠的事件监听器幽灵网络请求

对于长时间运行的前端应用(如仪表盘或管理面板),这些问题可能会显著影响性能,甚至导致整个应用崩溃。

Bad (React):

useEffect(() => {   window.addEventListener('resize', handleResize); }, []);

Better:

useEffect(() => {
  window.addEventListener('resize', handleResize);
  return () => window.removeEventListener('resize', handleResize);
}, []);

错误 #6:无单位的变量

如果一个值有单位(如毫秒、像素或兆字节),请在变量名中包含该单位

否则,其他开发者(或者未来的你)将不得不猜测这个数字的含义。

Bad:

const timeout = 3000; 

Better:

const timeoutMs = 3000;

错误 #7:条件语句嵌套超过 3 层

当你的逻辑嵌套超过 2 或 3 层时,代码会变得难以阅读和理解,尤其是当你有很多 if/else 块时。

相反,尝试**“扁平化”你的代码**:

  • 提前返回
  • 反转条件
  • 将逻辑拆分成更小的函数

这样可以使每个代码块更易于阅读和调试。

Bad:

if (user) {
  if (user.isAdmin) {
    if (user.hasAccess) {
      // do stuff
    }
  }
}

Better:

if (!user || !user.isAdmin || !user.hasAccess) return;

// do stuff

错误 #8:在代码中硬编码 URL 或配置

像 URL、API 密钥或特定环境配置这类内容进行硬编码是个陷阱。

一旦这些值发生变化,你就得在整个代码库中四处查找修改。

相反,应将配置值存储在常量或环境变量中,然后在需要的地方导入使用

这样你就能避免出现错误、数据不同步的问题,还能节省大量的时间。

Bad:

fetch('https://api.example.com/posts');

Better:

const API_BASE_URL = process.env.API_BASE_URL;
fetch(`${API_BASE_URL}/posts`);

错误 #9:保留无用代码 “以防万一”

我们都做过这样的事 —— 注释掉一些代码,然后就把它留在那里 “以防万一”。

但事实是:99% 的情况下,没人会用到这些代码 😅。而且也没人想在滚动代码时看到它们。

Bad:

// old validation logic (maybe needed later)
// function validateInput(input) { ... }

更好的做法:直接删除它。

错误 #10:庞大的 “utils” 文件

经典的 utils.js 文件一开始往往很单纯,然后慢慢地就变成了各种随机函数的大杂烩。

不知不觉间,它就有几百行代码了,让人无从下手。

相反,应将工具函数放在使用它们的代码附近

或者,将它们拆分成专注特定功能的文件,比如 arrayUtils.jsdateUtils.js 等等。

Bad:

// utils.js
export function doStuff() {}

Better:

// listUtils.js (next to List component)
export function getVisibleItems(items, filter) {
  return items.filter(item => item.visible && item.category === filter);
}

错误 #11:忽略错误

如果你的代码可能会抛出错误,不要直接忽略它。

被忽略的错误调试起来就像噩梦一般,尤其是在生产环境中。

即使只是简单地使用 console.error 也能节省数小时的猜测时间。

而且,如果用户受到影响,在用户界面中显示提示,让他们知道出了问题(而不是显示空白屏幕)。

Bad:

try {
  riskyFunction();
} catch (e) {
  // nothing
}

✅ Better:

try {
  riskyFunction();
} catch (e) {
  console.error("Error in riskyFunction:", e);
  showToast("TODO: Useful error message + actions");
}

错误 #12:一个巨大的文件

当所有内容 —— 组件、逻辑、样式和状态 —— 都包含在一个文件中时,这个文件会迅速膨胀。

最终你会得到一个超过 800 行代码的“滚动地狱”🔥。

相反,你应该:

  • 拆分内容。
  • 将相关代码分组到不同的文件夹中,并为每个文件赋予明确的职责。

这样做可以降低认知负担,并让新开发人员的入职变得更加容易。

Bad:

// App.js — 1000+ lines of code

Better: Split it up into folders:

/components
/context
/pages

错误 #13:未使用代码检查工具

像 ESLint 这样的代码检查工具能在问题进入生产环境 之前 就发现它们,从简单的 bug 到 useEffect 中缺失的 React 依赖项都能检测到。

它们还能帮助团队统一代码风格。

请确保你阅读并处理这些警告:不要到处都加上 // eslint-disable 哦 😉。

Bad:

useEffect(() => {
  fetchData();
}, []); // missing fetchData in deps

更好的做法:使用 ESLint + React hooks 插件。它会发出如下警告:

React Hook useEffect 缺少依赖项:'fetchData'。

错误 #14:混乱的文件夹结构

你的项目文件夹不应像个杂物抽屉。

当文件随机散落时,很难找到所需内容,而且在扩展应用时,这种困难会加剧。

相反,应使用基于功能或领域的结构

将相关文件分组,这样新开发人员就能快速了解各项内容的用途。

Bad:

/src
  App.js
  helpers.js
  styles.css
  randomStuff.js

Better:

/src
  /components
  /services
  App.js

错误 #15:为显而易见的代码添加注释

你无需解释 let count = 0 是什么意思。

相信你的读者 😉。

如果有代码让人困惑,试着简化逻辑或者使用更清晰的命名

注释应该解释为什么要这样做,而不是解释某行代码的作用。

注释应该解释 为什么 要这样做,而不是解释某行代码 做了什么

Bad:

// Set count to 0
let count = 0;

Better:

let itemCount = 0;

错误 #16:对怪异逻辑未添加注释

有时你需要添加一个变通方案或支持遗留情况。

这没问题 —— 但要 解释 原因

如果没有注释,你的队友(或者未来的你)将会浪费时间去试图理解 magicFix42(data) 是什么意思。

留一个简短的注释吧。你之后会感谢自己的。

Bad:

const result = magicFix42(data);

Better:

// Applies patch for backend bug #321. Read ticket for more context 
const result = magicFix42(data);

错误 #17:重复造轮子

不要重新发明轮子。

有许多由比我们更聪明的人编写的小型可靠库。

如果你能用一个值得信赖的包解决问题,那就使用它。

🟠 Ok:

function debounce(fn, delay) {
  let timer;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

✅ Easier:

import { debounce} from 'lodash-es';

const debouncedFn = debounce(myFn, 300);

总结

前端代码很快就会变得杂乱无章。

避免这 17 个错误,你的代码将更易于阅读、调试和维护。

未来的你——还有你的队友——都会感谢你的。

英文原文