mcpField
F
frontend-js-best-practices

frontend-js-best-practices

github.com/sergiodxa开发92333 次安装39 周前更新
代码仓库

关于 frontend-js-best-practices

面向 JavaScript 和 TypeScript 开发的性能优化与代码风格指南,适用于编写和审查代码的开发者。内容由 17 条规则组成,覆盖数据结构、缓存、迭代和 DOM 操作等主题。

主要功能

  1. 01提供 17 条编码规则
  2. 02优化 Map、Set 和数组查找
  3. 03减少重复迭代与计算
  4. 04使用缓存降低重复访问
  5. 05规范 DOM 操作与代码结构

适用场景

  1. 01编写 JavaScript 或 TypeScript 代码
  2. 02优化循环和热点代码路径
  3. 03审查数据结构与缓存使用
  4. 04统一项目代码风格

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/sergiodxa/agent-skills --skill frontend-js-best-practices

安装后在 Claude Code 中输入/frontend-js-best-practices确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

rules/
batch-dom-css.md
cache-function-results.md
cache-property-access.md
cache-storage.md
combine-iterations.md
comments-meaningful-only.md
const-let-usage.md
early-exit.md
function-declarations.md
hoist-regexp.md
index-maps.md
length-check-first.md
min-max-loop.md
no-as-type-casts.md
no-default-exports.md
result-type.md
set-map-lookups.md
tosorted-immutable.md

详细介绍

JavaScript 最佳实践

适用于 JavaScript 和 TypeScript 代码的性能优化与代码风格模式。包含 17 条规则,重点在于减少不必要的计算、优化数据结构并保持一致的约定。

适用场景

在以下情况下,请参考这些指南:

  • 编写循环或数组操作
  • 使用数据结构(Map、Set、数组)
  • 直接操作 DOM
  • 缓存值或函数结果
  • 优化热点代码路径
  • 声明变量或函数

规则摘要

const-let-usage (MEDIUM) — @rules/const-let-usage.md

在模块级作用域使用 const,在函数内部使用 let。

// Module level: const with UPPER_SNAKE_CASE for primitives
const MAX_RETRIES = 3;
const userCache = new Map<string, User>();

// Inside functions: always let
function process(items: Item[]) {
  let total = 0;
  let result = [];
  for (let item of items) {
    total += item.price;
  }
  return { total, result };
}
function-declarations (MEDIUM) — @rules/function-declarations.md

命名函数优先使用函数声明,而不是箭头函数。

// Good: function declaration
function calculateTotal(items: Item[]): number {
  let total = 0;
  for (let item of items) {
    total += item.price;
  }
  return total;
}

// Good: arrow for inline callbacks
let active = users.filter((u) => u.isActive);

// Good: arrow when type requires it
const handler: ActionFunction = async ({ request }) => {
  // ...
};
no-default-exports (MEDIUM) — @rules/no-default-exports.md

使用具名导出。避免默认导出(Remix 路由组件除外)。

// Bad: default export
export default function formatCurrency(amount: number) { ... }

// Good: named export
export function formatCurrency(amount: number) { ... }

// Exception: Remix routes use default export named "Component"
export default function Component() { ... }
no-as-type-casts (HIGH) — @rules/no-as-type-casts.md

避免使用 as Type 类型断言。改用类型守卫或 Zod 验证。

// Bad: type assertion
let user = response.data as User;

// Good: Zod validation
let user = UserSchema.parse(response.data);

// Good: type guard
if (isUser(response.data)) {
  let user = response.data;
}
comments-meaningful-only (MEDIUM) — @rules/comments-meaningful-only.md

仅在需要补充代码无法表达的信息时添加注释。

// Bad: restates the code
// Set the user's name
let userName = user.name;

// Good: explains business rule
// Transactions under $250 don't require written acknowledgment per policy
if (transaction.amount < 250) {
  return { requiresAcknowledgment: false };
}
set-map-lookups (LOW-MEDIUM) — @rules/set-map-lookups.md

使用 Set/Map 实现 O(1) 查找,而不是使用数组方法。

// Bad: O(n) per check
const allowedIds = ["a", "b", "c"];
items.filter((item) => allowedIds.includes(item.id));

// Good: O(1) per check
const allowedIds = new Set(["a", "b", "c"]);
items.filter((item) => allowedIds.has(item.id));
index-maps (LOW-MEDIUM) — @rules/index-maps.md

重复查找时,只构建一次 Map。

// Bad: O(n) per lookup = O(n*m) total
orders.map((order) => ({
  ...order,
  user: users.find((u) => u.id === order.userId),
}));

// Good: O(1) per lookup = O(n+m) total
const userById = new Map(users.map((u) => [u.id, u]));
orders.map((order) => ({
  ...order,
  user: userById.get(order.userId),
}));
tosorted-immutable (MEDIUM-HIGH) — @rules/tosorted-immutable.md

使用 toSorted() 而不是 sort(),以避免修改原数据。

// Bad: mutates original array
const sorted = users.sort((a, b) => a.name.localeCompare(b.name));

// Good: creates new sorted array
const sorted = users.toSorted((a, b) => a.name.localeCompare(b.name));
combine-iterations (LOW-MEDIUM) — @rules/combine-iterations.md

将多个 filter/map 操作合并到一个循环中。

// Bad: 3 iterations
const admins = users.filter((u) => u.isAdmin);
const testers = users.filter((u) => u.isTester);
const inactive = users.filter((u) => !u.isActive);

// Good: 1 iteration
const admins: User[] = [],
  testers: User[] = [],
  inactive: User[] = [];
for (const user of users) {
  if (user.isAdmin) admins.push(user);
  if (user.isTester) testers.push(user);
  if (!user.isActive) inactive.push(user);
}
cache-property-access (LOW-MEDIUM) — @rules/cache-property-access.md

在循环中缓存对象属性。

// Bad: repeated lookups
for (let i = 0; i < arr.length; i++) {
  process(obj.config.settings.value);
}

// Good: cached lookup
const value = obj.config.settings.value;
const len = arr.length;
for (let i = 0; i < len; i++) {
  process(value);
}
cache-function-results (MEDIUM) — @rules/cache-function-results.md

在模块级 Map 中缓存开销较大的函数结果。

const slugifyCache = new Map<string, string>();

function cachedSlugify(text: string): string {
  if (!slugifyCache.has(text)) {
    slugifyCache.set(text, slugify(text));
  }
  return slugifyCache.get(text)!;
}
cache-storage (LOW-MEDIUM) — @rules/cache-storage.md

在内存中缓存 localStorage/sessionStorage 的读取结果。

const storageCache = new Map<string, string | null>();

function getLocalStorage(key: string) {
  if (!storageCache.has(key)) {
    storageCache.set(key, localStorage.getItem(key));
  }
  return storageCache.get(key);
}
early-exit (LOW-MEDIUM) — @rules/early-exit.md

结果确定后立即返回。

// Bad: continues after finding error
function validate(users: User[]) {
  let error = "";
  for (const user of users) {
    if (!user.email) error = "Email required";
  }
  return error ? { error } : { valid: true };
}

// Good: returns immediately
function validate(users: User[]) {
  for (const user of users) {
    if (!user.email) return { error: "Email required" };
  }
  return { valid: true };
}
length-check-first (MEDIUM-HIGH) — @rules/length-check-first.md

进行开销较大的比较前,先检查数组长度。

// Bad: always sorts even when lengths differ
function hasChanges(a: string[], b: string[]) {
  return a.sort().join() !== b.sort().join();
}

// Good: early return if lengths differ
function hasChanges(a: string[], b: string[]) {
  if (a.length !== b.length) return true;
  let aSorted = a.toSorted();
  let bSorted = b.toSorted();
  return aSorted.some((v, i) => v !== bSorted[i]);
}
min-max-loop (LOW) — @rules/min-max-loop.md

使用循环求最小值/最大值,而不是排序。

// Bad: O(n log n)
const latest = [...projects].sort((a, b) => b.updatedAt - a.updatedAt)[0];

// Good: O(n)
let latest = projects[0];
for (const p of projects) {
  if (p.updatedAt > latest.updatedAt) latest = p;
}
hoist-regexp (LOW-MEDIUM) — @rules/hoist-regexp.md

将 RegExp 的创建移到循环外。

// Bad: creates regex every iteration
items.forEach(item => {
  if (/pattern/.test(item.text)) { ... }
})

// Good: create once
const PATTERN = /pattern/
items.forEach(item => {
  if (PATTERN.test(item.text)) { ... }
})
batch-dom-css (MEDIUM) — @rules/batch-dom-css.md

先批量读取 DOM,再执行写入,以避免布局抖动。

// Bad: interleaved reads/writes force reflows
element.style.width = "100px";
const width = element.offsetWidth; // forces reflow
element.style.height = "200px";

// Good: batch writes, then read
element.style.width = "100px";
element.style.height = "200px";
const { width, height } = element.getBoundingClientRect();
result-type (MEDIUM) — @rules/result-type.md

使用显式的 Result 类型表示成功或失败。

let result = success(data);
if (isFailure(result)) return handleError(result.error);

评论

支持 Markdown