好用的前端工具链
Deep Dive into My Favorite Toolchain: Pitfalls, Highlights, and Unresolved Obsessions.
这篇文章不会写工具如何用,怎么用可以去看官网。这里只有我亲测好用的私藏宝贝、我在折腾它们时的挖坑填坑和让我兴奋的技术延伸。
Vite+
全局安装,详见Vite+官网
为什么我不改package.json 中的命令,本地的vite+也能自动识别vp转化为npm run?
vp dev 并不是被“转化”为 npm run dev,而是 Vite+ 直接“绕过”了 package.json 中的脚本,去调用了底层真正的命令。
对比三种“启动方式”的区别:
| 命令 | 实际执行的动作 | 是否依赖 package.json 的 scripts |
|---|---|---|
npm run dev / pnpm dev | 读取 package.json 中的 "dev" 脚本,执行其中的字符串命令 | 完全依赖 |
vp dev | 忽略 package.json,直接在 node_modules 中找 vite 二进制文件并执行。 | 完全不依赖(只依赖项目是否安装了 vite 包) |
vp 的好用快捷键速记
press r + enter重启开发服务器press u + enter重新显示服务器地址press o + enter自动打开浏览器press c + enter清空终端屏幕press q + enter退出并终止开发服务器
运行 Vite+ 的环境条件
| 条件维度 | 具体要求 | 扩展问题 |
|---|---|---|
| 操作系统 | macOS(10.15+)、Linux、Windows(WSL2 或 PowerShell) | 🤔为什么强调 macOS10.15+? |
| Shell 环境 | Bash、Zsh、Fish 或 PowerShell | - |
| Node.js | Node.js 18.0.0 或更高版本(底层 Vite 的要求) | 🤔为什么要求Node18+? |
| 网络 | 能访问 npm 官方仓库(或配置了国内镜像 | 🤔vp 命令启动是否需要网络? |
INFO
🤔为什么强调 macOS10.15+?
因为从 macOS Catalina(10.15)开始,苹果就把默认 Shell 从 Bash 换成了 Zsh。Vite+ 依赖 Zsh 的一些现代特性(如更完善的 glob 模式、数组操作)来优化脚本执行,因此官方建议 10.15+。实际上,如果你手动装了新版 Bash,在旧版 macOS 上也能跑,但官方不再为此做兼容测试。
😈 额外问题:
🤔为什么要求Node18+? 
🤔vp 命令启动是否需要网络? 
Vite+ 命令运行条件
不是所有项目都能用 Vite+ 的所有命令,这里只讨论 Vite 构建的项目。
| 命令 | 项目必须具备的条件 |
|---|---|
vp dev / vp build | 1. package.json中必须安装 vite 作为依赖。2. 项目根目录必须有 vite.config.js 或 vite.config.ts。3. 必须有一个 index.html 作为入口(标准 SPA)。 |
vp check(代码检查) | 项目中有 .eslintrc 或 oxlintrc.json 配置文件。 |
vp test | 项目中安装了vitest依赖 |
vp pack(打包) | 项目有 src/index.ts 等入口文件,用于发布 npm 包。 |
个人使用vp如何在团队开发中避免污染仓库?
千万不要在 CI/CD(如 GitHub Actions、Jenkins)里用 vp(其实就是不要在本地项目的
yml文件里写vp)全局安装时出现提示
vp env on选择no。如果选了yes,这里Vite+ 会生成 node 替身文件,可能会和公司的 .nvmrc 文件或 nvm 产生“谁说了算”的冲突。
WARNING
我在团队开发拉取代码的时候会发现别人的.DS_Store,这是Mac 系统在每个文件夹下自动生成的隐藏文件,记录图标位置。
解决方法,在项目根目录的.gitignore写:
# macOS
.DS_Store
Thumbs.db
# 编辑器/临时文件
.vscode/
.idea/
*.local
Oh My Zsh
为什么z file不能快速切换文件夹了?
因为z file名,不是“全局搜索工具”,它只记得你去过的地方。
敲 z 加文件夹名字就能瞬间飞过去,这其实是 Oh My Zsh 自带的一个超级插件,叫做z,它能把项目路径极其复杂的简化。启用方式:检查你的 ~/.zshrc 配置文件中的 plugins=(git z),确保 z 在里面。重启终端后,你就可以享受这种“记忆力”了。
硬核功能
除了美化控制台里的命令行、跳转路径外,还有很多其他功能,这里写下来是因为我发现我平时都没物尽其用。
git 插件缩写:gst → git status,gco → git checkout,gcb → git checkout -b, 目测能省下不少键盘寿命。
history 搜索:
输入 r 加关键字,快速找回之前敲过的长命令(比如 r vite 就能翻出 pnpm create vite@latest)
TypeScript
对文档内容的思考和延伸
"TypeScript是带有编译时类型检查器的JavaScript的运行时。"
我的理解是 tsc 是在 js 的基础上多了一个类型检查器的功能。
"通过感知JavaScript的工作原理,TypeScript可以构建一个接受JavaScript代码但具有类型的类型系统。"
let helloWorld = "Hello World";//这样赋值无需声明类型,tsc是知道helloWorld的值是字符串类型的 let helloWorld: string🤔这我想起了几年前写的
vue + ts的项目,是let helloWorld: string = ''的写法,这里我有个疑问,既然tsc直接认识,为什么还要写类型强调?可能是设置了eslint检查,tsc规定,只有当声明变量但不赋值(或赋值为 null/any)时,才必须亲手写类型注解。在TypeScript 官方团队与 ESLint 团队合作维护的typescript-eslint工具链中,有一个配置no-inferrable-types,就是为了禁止tsc能轻易判断出的基本类型进行多余的显示注解。
⚠️,该规则默认不检查函数参数和属性。另外,这个规则也是为了去掉一眼就看得出的废话,维护团队代码一致性。
在Vue项目中推荐开启,可以针对
<script setup>中的普通变量约束,对defineProp、defineEmits里的使用的类型字面量无效果,对ref、computed灵活处理。"如果将代码从 JavaScript 迁移到 TypeScript ,即使 TypeScript 认为代码有类型错误,也可以 保证 以相同的方式运行。"
这句话让我产生疑惑🤔,因为开发时项目中ts爆红,
npm run就跑不动。原因在于实际开发中,前端工程化的工具如Vue/ Vite / Webpack / Nuxt等在调用底层TS时,通常会强制开启类型检查作为构建的前置关卡。所以我对tsc的刻板印象不好用、严格、麻烦并不是tsc本身的原因。
文档的这句话,强调的是逻辑层面的不变性,而不是编译通过性。 如下:
// 在 JS 中 let result = 1 / 0; // 结果是 Infinity // 迁移到 TS 后(即使你写错了类型) let result: number = 1 / 0; // 或者你甚至写 let result: string = 1 / 0;(类型报错) // 但编译出的 JS 依然是 let result = 1 / 0; // 在浏览器里跑,结果依然是 Infinity,而不会像 Java 那样抛出一个 ArithmeticException。"对函数的类型定义"link 由文档衍生的疑问:对错的规则究竟是什么?
interface User { name: string; id: number; } // ---分割线--- const user2 = { name: 'A', id: 1, age: 18 }; function getAdminUser(): User { return user2; // ✅ 通过!鸭式类型判定有效 } getAdminUser({name: 'A', id: 1, age: 18});//❌ 报错 function deleteUser(user: User) { // ... } const extraUser = { name: 'A', id: 1, age: 18 }; deleteUser(extraUser); //✅ 通过 deleteUser({ name: 'A', id: 1, age: 18 }); //❌ 报错一个坑点:只要用字面量,返回值和参数都会严格报错;只要用变量,返回值和参数都宽松放行。
鸭式类型
源自 计算机科学
Computer Science下的子学科 —— 编程语言理论Programming Language Theory, PLT和 类型理论Type Theory。“鸭式类型
Duck Typing”这个词的灵感来自于美国诗人 James Whitcomb Riley 的一句民间谚语:“If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.”(如果它看起来像鸭子,游泳像鸭子,叫起来像鸭子,那它就是鸭子。)
在编程领域,这个概念最早主要用于
Python、Ruby 等动态语言(运行时检查),后来TypeScript将其引入到静态类型检查中,形成了“结构化类型系统”。即:不关心这个对象的“真实身份(类名)”,只关心它“长什么样(拥有哪些属性/方法)”。“TypeScript 提供了检查你在赋值时与类型是否匹配的功能。”
疑问:🤔既然 TS 能自动识别(类型推断),为什么还要用 interface?
interface User { name: string; id: number; } const user: User = { username: "Hayes", // ❌ Object literal may only specify known properties, and 'username' does not exist in type 'User'. id: 0, };很有趣的问题,但答案AI告诉了我更有趣的类比。tsc的自动识别(类型判断)可以类比验钞机,interface类比验钞模版(规定钱/代码长什么样),你这个疑问就是既然验钞机能识别真钱,为什么还要验钞模版?
验钞模版能在钱生产的源头就避免错误。tsc中的自动识别是用,interface接口定义了一个开发者需要遵守的、公开的、稳定的契约。
延伸:🤔tsc中简单类型会用
type而非interface,tsc对简单类型的定义场景?type和interface的不同?`type`&`interface`
核心区别:

实际开发中的使用场景划分:

Everyday Types
JavaScript 对原始类型可以通过typeof提前判断类型,但对于函数,并没有对应的机制去计算类型。JavaScript 只提供了动态类型 —— 执行代码,然后才能知道会发生什么事。
所以typeScript采用了一种静态的类型系统,在代码实际执行前预测代码的行为。
静态类型系统描述了程序运行时指的结构和行为。
为什么静态类型系统描述了程序运行时指的结构和行为?
我看到这句话的疑问点在于程序运行时。因为在我的理解中,JS通常需要等到运行时才能暴露类型错误,比如JS调用函数,往往是运行之后才知道。那为什么在这里又描述成程序运行时的?
AI给出的解释是:TS的静态类型检查器在代码执行前工作,但它检查的依据——类型——描述的是代码执行时那些值会具有的结构和行为。
所以“程序运行时”指的是类型所描述的对象,而不是类型检查发生的时刻。两者并不冲突。
另外需要注意的点是,TS类型在运行时会被擦除,TS的类型只存在于编译时,当编译成JS后,所有的类型标注都会消失:
// 编译前(TS)
function greet(name: string) { ... }
// 编译后(JS)
function greet(name) { ... }
所以这进一步说明:描述程序运行时的结构和行为:在编译时,用类型给运行时的值建立模型,并检查代码是否与模型冲突。