JS/TS 编码规范实战:Vue 场景变量 / 函数 / 类型标注避坑|编码语法规范篇

【Vue/JS/TS】+【前端日常开发】:从【变量声明/函数写法/类型标注】到【落地实操】,彻底搞懂【前端可维护代码】的最佳写法,避开团队协作高频坑!

📑 文章目录

  • [一、先定一个总原则:让代码回答 3 个问题](#一、先定一个总原则:让代码回答 3 个问题)
  • [二、变量声明:const 优先,let 必要;别用 var](#二、变量声明:const 优先,let 必要;别用 var)
    • [2.1 规则一:默认用 const,能不变就不变](#2.1 规则一:默认用 const,能不变就不变)
    • [2.2 规则二:对象/数组"内容可变"要自觉](#2.2 规则二:对象/数组“内容可变”要自觉)
    • [2.3 规则三:作用域要清楚,尽量避免"跨块复用同名"](#2.3 规则三:作用域要清楚,尽量避免“跨块复用同名”)
    • [2.4 变量命名(你会在 Vue 里反复用到)](#2.4 变量命名(你会在 Vue 里反复用到))
  • [三、函数写法:优先"清晰 + 可复用";区分声明与箭头](#三、函数写法:优先“清晰 + 可复用”;区分声明与箭头)
    • [3.1 规则一:函数做工具就写"函数声明",做回调就写"箭头函数"](#3.1 规则一:函数做工具就写“函数声明”,做回调就写“箭头函数”)
    • [3.2 规则二:箭头函数逻辑简单就用隐式返回;复杂就用大括号 + 显式 return](#3.2 规则二:箭头函数逻辑简单就用隐式返回;复杂就用大括号 + 显式 return)
    • [3.3 规则三:参数处理清晰,默认值优先,避免"神秘 undefined"](#3.3 规则三:参数处理清晰,默认值优先,避免“神秘 undefined”)
    • [3.4 规则四:尽量"早返回",减少深层嵌套](#3.4 规则四:尽量“早返回”,减少深层嵌套)
    • [3.5 规则五:纯函数优先,副作用要"隔离"](#3.5 规则五:纯函数优先,副作用要“隔离”)
  • [四、TS 类型标注:标注边界,能推断就推断;别滥用 any](#四、TS 类型标注:标注边界,能推断就推断;别滥用 any)
    • [4.1 规则一:不要到处写类型;优先让 TS 推断简单情况](#4.1 规则一:不要到处写类型;优先让 TS 推断简单情况)
    • [4.2 规则二:公开函数/工具函数,建议给入参和返回值](#4.2 规则二:公开函数/工具函数,建议给入参和返回值)
    • [4.3 规则三:类型别写成"装饰品",要覆盖真实约束](#4.3 规则三:类型别写成“装饰品”,要覆盖真实约束)
    • [4.4 规则四:联合类型要配合"判别方式"或类型保护(type guard)](#4.4 规则四:联合类型要配合“判别方式”或类型保护(type guard))
    • [4.5 规则五:空值处理要"可读",优先 ?? / 可选链 ?.](#4.5 规则五:空值处理要“可读”,优先 ?? / 可选链 ?.)
    • [4.6 规则六:尽量少用类型断言(as),它只能当"最后一层保险"](#4.6 规则六:尽量少用类型断言(as),它只能当“最后一层保险”)
  • [五、一套完整实战示例:Vue 场景下的变量/函数/类型怎么选](#五、一套完整实战示例:Vue 场景下的变量/函数/类型怎么选)
    • [5.1 TS 版本:推荐写法(简洁又规范)](#5.1 TS 版本:推荐写法(简洁又规范))
    • [5.2 反例:同样的需求,用 JS/TS 常见写法会怎么"变味"](#5.2 反例:同样的需求,用 JS/TS 常见写法会怎么“变味”)
  • [六、日常落地清单:写完代码前你可以自检这 6 条](#六、日常落地清单:写完代码前你可以自检这 6 条)
  • [🔍 系列模块导航](#🔍 系列模块导航)

同学们好,我是 Eugene(尤金),一名多年中后台前端开发工程师。

(Eugene 发音 /juːˈdʒiːn/,大家怎么顺口怎么叫就好)

很多前端开发者都会遇到一个瓶颈:

代码能跑,但不够规范;功能能实现,但维护起来特别痛苦;一个人写没问题,一到团队协作就各种混乱、踩坑、返工。

想写出干净、优雅、可维护 的专业代码,靠的不是天赋,而是体系化的规范 + 真实实战经验

这一系列《前端规范实战》,我会用大白话 + 真实业务场景,不讲玄学、不堆理论,只分享能直接落地的规范、标准与避坑指南。

帮你从「会写代码」真正升级为「会写优质、可维护、团队级别的代码」。


做了 7 年前端后我越来越确定:规范不是"为了显得严谨",而是为了在你每天写的代码里,减少歧义、降低出错率、让团队更快读懂 。尤其是 Vue 项目里,代码往往会被拆到很多文件、很多 watch/effect/computed 里,变量声明、函数写法、类型标注只要稍微"不自洽",后面就会反复踩同一类坑。

这篇文章不讲玄学底层原理,只讲"日常写代码到底怎么选、为什么这么选、踩坑会踩在哪",并且每个规则都给出小白也能照着用的完整示例。你可以直接把它当成"编码时的选择题"。

一、先定一个总原则:让代码回答 3 个问题

写任何一段 JS/TS 代码,都尽量让读者(包括未来的你)在 3 秒内看懂:

  1. 这段代码的"东西"是什么(变量/参数/返回值的语义)
  2. 这段代码会不会变化(变量是否可变、函数是否有副作用)
  3. 类型上有哪些约束(TS 能推断就推断,推断不了就明确标注边界)

接下来我们按你关心的 3 个核心语法部分来落地:变量声明 / 函数写法 / 类型标注


二、变量声明:const 优先,let 必要;别用 var

2.1 规则一:默认用 const,能不变就不变

在 JS 里,变量声明最容易带来的 bug 是"以为不会变,结果被改了",然后你在别处使用了旧值,出现错位、渲染异常、计算结果不对。

推荐:

  • "只赋值一次"的变量:用 const
  • "需要重新赋值"的变量:用 let
  • **不使用 ** var(作用域、提升行为更容易造成误解)
反例(可读性差 + 未来容易被改)
js 复制代码
let total = 0;

for (const p of products) {
  total += p.price;
}

total = total * 1.1; // 未来改动时很容易忽略"total"本来不该变
正例(更清晰:total 的含义更稳定)
js 复制代码
const total = products.reduce((sum, p) => sum + p.price, 0);
const discountTotal = total * 1.1;

[⬆ 返回目录](#⬆ 返回目录)

2.2 规则二:对象/数组"内容可变"要自觉

很多人以为 const obj = {} 就代表"不能改",这不成立。const 只是保证变量绑定不被重新指向,但对象内部仍然可以被改。

你可以这样理解:

  • const:变量名别换指向(不要再 obj = xxx
  • 对象内部修改:要么明确接受可变,要么尽量用不可变写法(更适合状态管理)
反例(隐蔽的可变)
js 复制代码
const user = { name: 'Alice', age: 20 };

user.age = 21; // 读者可能以为你在做不可变更新,但你在原地修改
正例(不可变更新:更利于追踪变更)
js 复制代码
const user = { name: 'Alice', age: 20 };
const updatedUser = { ...user, age: 21 };

[⬆ 返回目录](#⬆ 返回目录)

2.3 规则三:作用域要清楚,尽量避免"跨块复用同名"

let/const 都是块级作用域(block scope)。同一个块里避免反复声明、避免在不同层级用同名变量,会让读者在脑内"跳转"。

反例(同名遮蔽,踩坑常见)
js 复制代码
let keyword = 'Vue';

if (search) {
  let keyword = search; // 遮蔽了外层 keyword,后续你可能以为 keyword 一直是同一个
}
正例(用更明确的变量名)
js 复制代码
let keyword = 'Vue';

if (search) {
  keyword = search;
}

[⬆ 返回目录](#⬆ 返回目录)

2.4 变量命名(你会在 Vue 里反复用到)

下面是我建议的"能直接用"的命名习惯:

  1. 普通值:camelCase,语义清晰(例如 userNametotalPrice
  2. 布尔值:优先加语义前缀 is/has/can/should(例如 isLoadinghasMore
  3. 数组:复数名 itemsusersproducts
  4. 事件/回调:onXxx(例如 onSubmitonClose
  5. 临时变量:尽量短但别乱缩写(例如 tmp 可以,但别变成 tx1 这种不可读)

[⬆ 返回目录](#⬆ 返回目录)


三、函数写法:优先"清晰 + 可复用";区分声明与箭头

3.1 规则一:函数做工具就写"函数声明",做回调就写"箭头函数"

这不是绝对,但能让团队形成一致的阅读节奏:

  • 作为"可复用工具函数"(文件顶部、export、util):倾向 function xxx(...) {}
  • 作为"回调"(例如 map/filter/watch 里的一次性逻辑):倾向 const xxx = () => {} 或内联箭头
例子:工具函数用声明
js 复制代码
function formatMoney(cents) {
  return (cents / 100).toFixed(2);
}
例子:回调用箭头
js 复制代码
const total = items.reduce((sum, item) => sum + item.price, 0);

[⬆ 返回目录](#⬆ 返回目录)

3.2 规则二:箭头函数逻辑简单就用隐式返回;复杂就用大括号 + 显式 return

简单表达式:

js 复制代码
const isHot = (p) => p.score >= 90;

复杂逻辑:

js 复制代码
const normalizeProduct = (p) => {
  const price = p.priceCents ?? 0;
  return { ...p, price };
};

这样做的好处是:读者不用猜你的函数到底会不会做额外工作。

[⬆ 返回目录](#⬆ 返回目录)

3.3 规则三:参数处理清晰,默认值优先,避免"神秘 undefined"

很多坑来自这种写法:你在函数里不断判断 x && ...,结果逻辑变得很难读。

反例(读者要脑内推导大量情况)
js 复制代码
function calc(min, max) {
  if (min && max) return max - min;
  if (min) return min;
  return 0;
}
正例(用默认值 + 明确语义)
js 复制代码
function calc(min = 0, max = 0) {
  if (max > 0) return max - min;
  return min;
}

[⬆ 返回目录](#⬆ 返回目录)

3.4 规则四:尽量"早返回",减少深层嵌套

Vue 写 watch 或处理筛选时,最常见的代码风格问题就是深层 if/else

反例(嵌套过深)
js 复制代码
function filterProducts(products, keyword) {
  const res = [];

  for (const p of products) {
    if (keyword) {
      if (p.name.includes(keyword)) {
        res.push(p);
      }
    } else {
      res.push(p);
    }
  }

  return res;
}
正例(早返回,逻辑更顺)
js 复制代码
function filterProducts(products, keyword) {
  if (!keyword) return products;

  return products.filter((p) => p.name.includes(keyword));
}

[⬆ 返回目录](#⬆ 返回目录)

3.5 规则五:纯函数优先,副作用要"隔离"

在 Vue 里:

  • 计算数据(computed)尽量纯:同样输入得到同样输出

  • 请求/日志/写状态这类副作用放到合适的地方:actionswatch 回调内部等

纯函数示例:

js 复制代码
function calcCartTotal(items) {
  return items.reduce((sum, it) => sum + it.quantity * it.price, 0);
}

副作用示例:

js 复制代码
async function loadProducts(api) {
  const res = await api.getProducts();
  // 这里才开始处理"请求结果到状态"的逻辑
  return res;
}

[⬆ 返回目录](#⬆ 返回目录)


四、TS 类型标注:标注边界,能推断就推断;别滥用 any

TS 的核心目标不是"把代码变长",而是让你在最该出错的地方更早发现问题。所以类型标注要遵循一个非常实用的策略:

规则总纲:在"边界处"标注类型,在"内部"尽量让 TS 推断。

边界通常是:

  • 函数参数 / 返回值(尤其是对外暴露的函数)
  • API 请求数据的入口
  • 对象结构很复杂、很容易写错的地方

4.1 规则一:不要到处写类型;优先让 TS 推断简单情况

反例(冗长但没带来额外安全)
ts 复制代码
const keyword: string = 'Vue';
const page: number = 1;
正例(短且不牺牲安全)
ts 复制代码
const keyword = 'Vue';
const page = 1;

[⬆ 返回目录](#⬆ 返回目录)

4.2 规则二:公开函数/工具函数,建议给入参和返回值

尤其是你写到 util、通用函数、团队会复用的逻辑里。

ts 复制代码
type MoneyCents = number;

function formatMoney(cents: MoneyCents): string {
  return (cents / 100).toFixed(2);
}

[⬆ 返回目录](#⬆ 返回目录)

4.3 规则三:类型别写成"装饰品",要覆盖真实约束

你要的是"约束",不是"占位"。例如 API 数据通常是最危险的边界。

场景:商品列表接口

假设接口返回结构如下:

  • id 必须存在且是字符串
  • priceCents 可能是数字
  • tags 可能为空

我们在 TS 里可以这样建模:

ts 复制代码
type Product = {
  id: string;
  name: string;
  priceCents: number;
  tags?: string[];
};

type Filters = {
  keyword?: string;
  minPriceCents?: number;
  maxPriceCents?: number;
};

如果你在后续筛选里把 priceCents 当成字符串用,TS 就能在你写代码时提醒你。

[⬆ 返回目录](#⬆ 返回目录)

4.4 规则四:联合类型要配合"判别方式"或类型保护(type guard)

反例(看起来能编过,但可能埋雷)
ts 复制代码
function getLabel(v: string | number) {
  // 你没有区分类型就直接当字符串用
  return v.toUpperCase(); // number 没有 toUpperCase
}
正例(判别联合 + 分支清晰)
ts 复制代码
function getLabel(v: string | number) {
  if (typeof v === 'string') return v.toUpperCase();
  return String(v);
}

[⬆ 返回目录](#⬆ 返回目录)

4.5 规则五:空值处理要"可读",优先 ?? / 可选链 ?.

很多 Vue/TS 项目坑来自 null/undefined 混用。建议你在严格模式下保持习惯:

  • 有默认值:优先用 ??
  • 可能为 null:优先用 ?.
  • 避免到处写强行断言 as
ts 复制代码
const priceCents = product.priceCents ?? 0;
const firstTag = product.tags?.[0] ?? '';

[⬆ 返回目录](#⬆ 返回目录)

4.6 规则六:尽量少用类型断言(as),它只能当"最后一层保险"

as 可以让 TS 假装你说的是对的,但如果你其实判断错了,就会把运行时风险带回来。

更推荐写"类型保护":

ts 复制代码
function isProduct(value: any): value is Product {
  return (
    value &&
    typeof value.id === 'string' &&
    typeof value.name === 'string' &&
    typeof value.priceCents === 'number'
  );
}

[⬆ 返回目录](#⬆ 返回目录)


五、一套完整实战示例:Vue 场景下的变量/函数/类型怎么选

下面这个示例我会做成"你可以直接套进项目的写法"。场景:商品列表页需要:

  1. 获取商品(边界)

  2. 根据筛选条件计算展示列表(纯函数优先)

  3. 格式化金额(工具函数)

  4. 更新 UI 所需的数据(状态层处理副作用)

5.1 TS 版本:推荐写法(简洁又规范)

ts 复制代码
type Product = {
  id: string;
  name: string;
  priceCents: number;
  tags?: string[];
};

type Filters = {
  keyword?: string;
  minPriceCents?: number;
  maxPriceCents?: number;
};

function formatMoney(cents: number): string {
  return (cents / 100).toFixed(2);
}

function matchesFilters(p: Product, filters: Filters): boolean {
  const keyword = filters.keyword ?? '';
  const minPrice = filters.minPriceCents ?? 0;
  const maxPrice = filters.maxPriceCents ?? Number.POSITIVE_INFINITY;

  const byKeyword = keyword ? p.name.includes(keyword) : true;
  const byMin = p.priceCents >= minPrice;
  const byMax = p.priceCents <= maxPrice;

  return byKeyword && byMin && byMax;
}

function buildDisplayList(products: Product[], filters: Filters) {
  // 纯函数:同样输入得到同样输出
  return products
    .filter((p) => matchesFilters(p, filters))
    .map((p) => ({
      id: p.id,
      title: p.name,
      priceText: formatMoney(p.priceCents),
      tags: p.tags ?? [],
    }));
}

你可以注意到这些"规范点"在一起工作时的效果:

  • 变量:默认用 const,只有需要重新赋值才用 let(此例基本不用 let

  • 函数:工具函数用声明,回调用箭头;逻辑清晰

  • 类型:只在关键边界处建模(Product / Filters),内部让 TS 推断

[⬆ 返回目录](#⬆ 返回目录)

5.2 反例:同样的需求,用 JS/TS 常见写法会怎么"变味"

ts 复制代码
// 反例:不建模,滥用 any,导致可读性和安全性都下降
function buildDisplayList(products: any[], filters: any) {
  const res = [];

  for (const p of products) {
    // p.priceCents 可能不是 number,你没约束也没处理
    if (filters.keyword && !p.name.includes(filters.keyword)) continue;

    const priceText = (p.priceCents / 100).toFixed(2); // 运行时才知道会不会炸
    res.push({ id: p.id, title: p.name, priceText, tags: p.tags });
  }

  return res;
}

这个反例会带来三个典型问题:

  • 可读性:any 让读者无法判断结构是什么

  • 可维护性:未来接口一变,你不会在编译期得到提醒

  • 风险滞后:错误往往到运行时才暴露

[⬆ 返回目录](#⬆ 返回目录)


六、日常落地清单:写完代码前你可以自检这 6 条

你不需要背很多条,只要养成"写完快速自检"的习惯:

  1. 这段代码里我有没有"随便用 let/var",而其实只需要 const

  2. 有无对象内部被我悄悄改了,导致外部状态不可追踪?

  3. 函数是否逻辑被我写得很深?有没有可以拆成纯函数/工具函数?

  4. 回调里我是否写成了"既做判断又做副作用"的大杂烩?

  5. TS 里我有没有把关键边界参数/返回值用 any 或缺失类型,导致约束失效?

  6. null/undefined 我有没有用清晰的 ?? / ?. 处理?有没有靠"运气"写断言?

[⬆ 返回目录](#⬆ 返回目录)


总结:简洁不是随便,规范是让你更快更稳

  • const 优先:减少"变量会变"的认知成本,让代码更稳定

  • 函数写法要遵循"清晰表达":工具函数清晰可复用,回调函数紧凑好读

  • TS 类型标注要守边界:能推断就推断,推断不了就在关键位置补上约束

  • 大多数线上坑不是你不会写,而是你"写了但读不懂 / 约束没生效 / 风险滞后到运行时"

[⬆ 返回目录](#⬆ 返回目录)

🔍 系列模块导航

📝 编码语法规范

这是前端规范实战系列中第二个模块,当编码语法规范模块更新完成之后会附上此模块的跳转链接,方便同学们阅读学习。

更新中,敬请期待~

👉 跟着系列慢慢学,把技术功底扎扎实实地打牢~

📚 系列总览

前端规范实战系列 」正在持续更新中,后续会整理一篇《前端规范实战系列全系列目录导航》,包含每篇文章简介 + 直达链接,方便大家按顺序、体系化学习。

更新中,敬请期待~

[⬆ 返回目录](#⬆ 返回目录)


技术成长,从来不是比谁写得快,而是比谁写得稳、规范、可维护

哪怕每次只吃透一条规范,长期下来,差距会非常明显。

后续我会持续更新前端规范、工程化、可维护代码相关实战干货,帮你告别面条代码、维护噩梦,在开发与面试中更有底气。

觉得有用欢迎 点赞 + 收藏 + 关注,不错过每一篇实战内容。

我是 Eugene,与你一起写规范、写优质代码,我们下篇干货见~

相关推荐
暮冬-  Gentle°2 小时前
C++中的工厂方法模式
开发语言·c++·算法
FlyWIHTSKY2 小时前
vue3中const的使用和定义
前端·javascript·vue.js
Chengbei112 小时前
Chrome浏览器渗透利器支持原生扫描!JS 端点 + 敏感目录 + 原型污染自动化检测|VulnRadar
javascript·chrome·安全·web安全·网络安全·自动化·系统安全
gongzemin2 小时前
怎么在VS Code 调试vue3 源码
前端·vue.js
乱世军军2 小时前
把 Python 3.13 降级到 3.11
开发语言·python
本喵是FW2 小时前
C语言手记2
c语言·开发语言
fy121632 小时前
GO 快速升级Go版本
开发语言·redis·golang
共享家95272 小时前
Java入门(String类)
java·开发语言
0xDevNull2 小时前
Spring Boot 循环依赖解决方案完全指南
java·开发语言·spring