5小时搭建Vue/React项目TypeScript+ESLint+Vitest工程化底座
2026/9/8 21:28:18 网站建设 项目流程

你是不是也遇到过这样的场景?

接手一个新项目,代码里一半是any,另一半是@ts-ignore;想加个新功能,却发现类型定义一团糟,IDE 的智能提示形同虚设。好不容易下定决心要重构,面对tsconfig.json、ESLint 配置、单元测试框架选型,又是一头雾水。网上教程要么太浅,只讲interfacetype的区别,要么太散,配置了半天还是跑不通。

问题不在于你不会 TypeScript 语法,而在于如何将 TypeScript、代码规范检查和单元测试这三者,系统地、可维护地整合进你的前端工程化体系里。这恰恰是区分“会用语法”和“能用于生产”的关键。

这篇文章不会教你stringString的区别,那是语法手册的事。我们要解决的是一个更实际的问题:如何用 5 小时,为一个现有或新起的 Vue/React 项目,搭建一套坚实、高效、团队协作友好的 TypeScript 工程化底座。

这个底座包含三个核心支柱:

  1. TypeScript:提供静态类型安全,这是地基。
  2. ESLint:统一代码风格和发现潜在问题,这是承重墙。
  3. Vitest:保障代码重构和功能迭代时的信心,这是质检系统。

三者环环相扣:TypeScript 定义了“什么是对的”,ESLint 检查“怎么写更好”,Vitest 验证“改了之后还对不对”。本文将带你从零开始,一步步搭建这套体系,并深入每个环节的配置“深水区”,告诉你那些官方文档里没明说,但实际项目中一定会踩的坑。

1. 为什么是 TypeScript + ESLint + Vitest?

在深入配置之前,我们必须先达成一个共识:单纯引入 TypeScript,对项目质量的提升是有限的。它只是一个工具,用得好是神兵利器,用不好反而会成为负担(比如满屏的any)。

真正的提升来自于“类型安全 + 代码规范 + 自动化测试”形成的工程化闭环。

  • TypeScript 的局限:它能检查类型错误,但管不了代码风格(单引号还是双引号?)、潜在的逻辑错误(未使用的变量?)、或更佳实践(是否该用===?)。它告诉你“类型不对”,但不会告诉你“代码写得丑”。
  • ESLint 的补位:ESLint 专门负责代码质量和风格的一致性。通过集成@typescript-eslint插件,ESLint 可以理解 TypeScript 语法,从而在 TypeScript 编译器检查之前或之后,执行更丰富的规则检查。它们是协作关系,而非替代关系。
  • Vitest 的价值:当你基于类型和规范重构代码后,如何确保功能没被破坏?单元测试是唯一的答案。Vitest 作为一个与 Vite 高度兼容、速度极快的测试框架,完美匹配现代前端开发流程。它为你的类型安全和代码规范提供了“运行时验证”。

所以,我们的目标不是单独配置三个工具,而是让它们1+1+1 > 3。接下来,我们就从项目初始化开始。

2. 项目初始化与核心依赖安装

假设我们从一个全新的 Vite + TypeScript 项目开始。这是目前最主流、最快速的起点。

# 使用 npm 7+, yarn, pnpm 都可以,这里以 pnpm 为例(推荐,速度更快) pnpm create vite my-ts-project -- --template vue-ts # 或 react-ts # pnpm create vite my-ts-project -- --template react-ts cd my-ts-project

安装核心依赖。我们将一次性安装 TypeScript、ESLint 及其 TypeScript 插件、以及 Vitest。

pnpm add -D typescript pnpm add -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin pnpm add -D vitest @vue/test-utils jsdom # 如果是 Vue 项目 # 如果是 React 项目:pnpm add -D vitest @testing-library/react @testing-library/jest-dom jsdom pnpm add -D @vitest/ui # 可选,用于测试 UI

关键点解释:

  • @typescript-eslint/parser:允许 ESLint 解析 TypeScript 代码。
  • @typescript-eslint/eslint-plugin:提供了一系列针对 TypeScript 的 ESLint 规则。
  • jsdom:为 Vitest 提供一个浏览器环境的模拟,用于测试涉及 DOM 的代码。

3. TypeScript 配置 (tsconfig.json):不只是开启strict

Vite 模板生成的tsconfig.json通常是一个好的起点,但生产项目需要更细致的控制。我们重点关注几个容易混淆或至关重要的配置项。

// tsconfig.json { "compilerOptions": { "target": "ES2020", // 编译目标语法,现代浏览器支持良好 "useDefineForClassFields": true, "lib": ["ES2020", "DOM", "DOM.Iterable"], // 包含 DOM 类型定义 "module": "ESNext", "skipLibCheck": true, // 跳过库文件的类型检查,加快编译 /* 模块解析 */ "moduleResolution": "bundler", // 与 Vite/Rollup 等打包器配合更好 "allowImportingTsExtensions": true, // 允许导入 .ts 扩展名 "resolveJsonModule": true, "isolatedModules": true, // 确保每个文件可独立编译,对打包和测试必需 "noEmit": true, // Vite 负责构建,tsc 只做类型检查 /* 类型检查的严格模式 - 这是核心! */ "strict": true, // 启用所有严格类型检查选项 "noUnusedLocals": true, // 报告未使用的局部变量 "noUnusedParameters": true, // 报告未使用的函数参数 "noFallthroughCasesInSwitch": true, // 防止 switch case 穿透 /* 路径别名 - 提升开发体验 */ "baseUrl": ".", "paths": { "@/*": ["src/*"] } }, "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], // 包含的文件 "references": [{ "path": "./tsconfig.node.json" }] // 如果有 Node 端配置 }

必须理解的几个“坑”:

  1. strict: true:这是最重要的开关。它一次性开启了约 8 项严格检查(如strictNullChecks,strictFunctionTypes等)。强烈建议从一开始就开启,虽然初期会报很多错,但这能从根本上杜绝一大类运行时错误。如果历史项目迁移困难,可以逐一开启子选项。
  2. moduleResolution:旧项目或某些教程可能使用node。对于 Vite 项目,使用bundlernode16/nodenext是更正确的选择,能更好地处理 ESM 模块。
  3. isolatedModules: true:当使用 Vitest 或 SWC 等非tsc的编译器时,此选项必须为true,确保每个文件是有效的独立模块。
  4. noEmit: true:在 Vite 项目中,我们通常用vite buildtsc --noEmit只进行类型检查,而不输出 JS 文件。构建由 Vite 完成。

4. ESLint 配置:统一代码风格的“宪法”

ESLint 的配置是团队协作的基石。我们将创建一个同时处理.js,.ts,.vue文件的配置。

首先,初始化 ESLint 配置。你可以使用npx eslint --init交互式生成,但为了更清晰,我们手动创建.eslintrc.cjs(CommonJS 格式,因为 ESLint 内部使用)。

// .eslintrc.cjs module.exports = { root: true, // 表明这是根配置文件,ESLint 将停止在父级目录中查找 env: { browser: true, es2020: true, node: true, }, extends: [ 'eslint:recommended', // ESLint 内置推荐规则 'plugin:@typescript-eslint/recommended', // TS 插件推荐规则 'plugin:@typescript-eslint/recommended-requiring-type-checking', // 需要类型信息的更严格规则 ], parser: '@typescript-eslint/parser', // 指定 TS 解析器 parserOptions: { ecmaVersion: 'latest', sourceType: 'module', project: './tsconfig.json', // 告诉 ESLint tsconfig 的位置,这对需要类型信息的规则至关重要 tsconfigRootDir: __dirname, }, plugins: ['@typescript-eslint'], rules: { // 在这里覆盖或添加自定义规则 // 示例:强制使用单引号 'quotes': ['error', 'single'], // 示例:禁止使用 console.log (生产代码中) 'no-console': ['warn', { allow: ['warn', 'error'] }], // 关闭 @typescript-eslint 的特定规则 '@typescript-eslint/no-explicit-any': 'warn', // 允许 any,但给出警告 '@typescript-eslint/no-unused-vars': ['error', { 'argsIgnorePattern': '^_' }], // 忽略以下划线开头的未使用参数 }, overrides: [ // 针对特定文件覆盖配置 { files: ['*.vue'], extends: ['plugin:vue/vue3-recommended'], // 使用 Vue 3 推荐规则 parser: 'vue-eslint-parser', parserOptions: { parser: '@typescript-eslint/parser', // 在 Vue 文件中解析 `<script lang="ts">` }, }, ], };

配置核心解析:

  1. extends顺序:后面的配置会覆盖前面的。我们首先继承 ESLint 基础规则,然后是 TS 通用规则,最后是需要类型检查的严格规则。这个顺序很重要。
  2. parserOptions.project这是连接 ESLint 和 TypeScript 类型系统的关键!没有它,@typescript-eslint/recommended-requiring-type-checking里的许多高级规则(如正确识别 Promise 返回类型)将无法工作。务必确保路径正确。
  3. overrides:用于对特定文件类型(如.vue)应用不同的解析器和规则集。这是处理 Vue SFC 或 React + TSX 文件的标准做法。
  4. 规则定制rules对象是你的主战场。建议团队共同讨论并确定规则。可以从较宽松开始('warn'),逐步收紧到'error'

5. 集成 Prettier:处理格式,让 ESLint 专注代码质量

ESLint 既能检查代码质量,也能(通过插件)检查代码格式。但 Prettier 在代码格式化上更专业、更固执。最佳实践是让它们各司其职:

  • Prettier:负责所有格式化规则(缩进、分号、换行、引号等)。
  • ESLint:负责所有代码质量规则(未使用的变量、错误的类型使用等)。

首先安装依赖并解决潜在的规则冲突:

pnpm add -D prettier eslint-config-prettier eslint-plugin-prettier
  • eslint-config-prettier关闭所有与 Prettier 冲突的 ESLint 规则。
  • eslint-plugin-prettier将 Prettier 作为 ESLint 规则来运行,这样你可以在 ESLint 的输出中看到格式问题。

更新.eslintrc.cjs

// .eslintrc.cjs module.exports = { // ... 其他配置保持不变 extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:@typescript-eslint/recommended-requiring-type-checking', 'prettier', // 必须放在最后!用来关闭冲突规则 ], plugins: [ '@typescript-eslint', 'prettier', // 添加 prettier 插件 ], rules: { // ... 其他规则 'prettier/prettier': 'error', // 将 Prettier 的格式化问题标记为错误 }, };

创建.prettierrc配置文件:

{ "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "es5", "printWidth": 100, "endOfLine": "lf" }

关键点:extends数组中的'prettier'必须放在最后,以确保它能正确覆盖之前所有配置中可能与 Prettier 冲突的格式规则。

6. Vitest 配置:极速单元测试

Vitest 的优势在于它与 Vite 共享配置,无需额外配置即可处理 TypeScript、路径别名等。基本配置非常简单。

首先,在package.json中添加测试脚本:

// package.json { "scripts": { "dev": "vite", "build": "vue-tsc && vite build", // 先进行类型检查再构建 "preview": "vite preview", "test": "vitest", "test:ui": "vitest --ui", // 打开测试 UI "lint": "eslint . --ext .ts,.vue --fix", // ESLint 检查并自动修复 "format": "prettier --write ." // Prettier 格式化 } }

创建vitest.config.ts

// vitest.config.ts import { defineConfig } from 'vitest/config'; import vue from '@vitejs/plugin-vue'; // 如果是 Vue 项目 // 如果是 React 项目,则不需要 vue 插件,可能需要 @vitejs/plugin-react export default defineConfig({ plugins: [vue()], // Vue 项目需要,React 项目不需要或使用 react() test: { globals: true, // 是否提供全局的 describe, it, expect 等 API environment: 'jsdom', // 测试环境,模拟浏览器 // 设置别名,与 tsconfig.json 中的 paths 对齐 alias: { '@': '/src', }, // 覆盖率报告 coverage: { provider: 'istanbul', // 或 'c8' reporter: ['text', 'json', 'html'], }, }, });

现在,让我们编写第一个测试。创建一个简单的工具函数及其测试:

// src/utils/math.ts export function add(a: number, b: number): number { return a + b; } export function divide(a: number, b: number): number { if (b === 0) { throw new Error('Division by zero'); } return a / b; }
// src/utils/math.test.ts import { describe, it, expect } from 'vitest'; // 如果 globals: true,则无需导入 import { add, divide } from './math'; describe('math utilities', () => { describe('add', () => { it('should add two numbers correctly', () => { expect(add(1, 2)).toBe(3); expect(add(-1, 5)).toBe(4); }); }); describe('divide', () => { it('should divide two numbers correctly', () => { expect(divide(6, 2)).toBe(3); }); it('should throw an error when dividing by zero', () => { expect(() => divide(5, 0)).toThrowError('Division by zero'); }); }); });

运行测试:

pnpm test

你会看到 Vitest 快速启动并运行测试。如果一切正常,你将看到通过的测试用例。

7. 自动化工作流:在提交代码前自动检查

手动运行linttest命令很容易被忘记。我们需要将其自动化,集成到 Git 工作流中。使用lint-stagedhusky是行业标准。

pnpm add -D husky lint-staged

初始化 Husky:

npx husky init

这会在项目根目录创建.husky文件夹,并添加pre-commit钩子示例。编辑.husky/pre-commit文件:

#!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" npx lint-staged

然后,在package.json中配置lint-staged

// package.json { // ... 其他配置 "lint-staged": { "*.{js,ts,vue}": [ "eslint --fix", // 自动修复 ESLint 问题 "prettier --write" // 自动格式化 ], "*.{json,md,css,scss}": [ "prettier --write" // 格式化其他文件 ] } }

现在,每次执行git commit时,lint-staged会自动对你本次提交所修改的文件运行 ESLint 修复和 Prettier 格式化。如果 ESLint 有无法自动修复的错误,提交将会被阻止。

更进一步:你还可以添加commit-msg钩子来规范提交信息格式,或添加pre-push钩子来运行完整的测试套件。

8. 常见问题与排查思路

在整合这套体系时,你几乎一定会遇到下面这些问题。

问题现象可能原因排查方式解决方案
ESLint 报错:Parsing error: ...1. 解析器未正确配置。
2. 文件扩展名未包含在检查范围。
1. 检查.eslintrc.cjs中的parseroverrides配置。
2. 检查运行命令eslint . --ext .ts,.vue中的--ext参数。
1. 确保对.vue文件使用了vue-eslint-parser,并在其parserOptions中指定@typescript-eslint/parser
2. 确保命令包含了所有需要检查的文件类型。
@typescript-eslint规则不生效或报类型错误parserOptions.project未配置或路径错误。检查.eslintrc.cjs中的parserOptions.project路径,确保其指向正确的tsconfig.json确保路径正确。对于 monorepo 或特殊结构,可能需要配置tsconfigRootDirproject: [‘./tsconfig.json’, ‘./packages/*/tsconfig.json’]
Vitest 无法识别路径别名@/Vitest 配置中的alias未设置,或与 Vite 配置不一致。1. 检查vitest.config.ts中的alias配置。
2. 检查vite.config.ts中的resolve.alias配置。
vitest.config.tstest.alias中设置与 Vite 一致的别名。如果 Vitest 和 Vite 配置合并,可以继承。
Prettier 和 ESLint 规则冲突(如引号格式)eslint-config-prettier未正确配置或顺序不对。检查.eslintrc.cjsextends数组,确保'prettier'在最后。'prettier'置于extends数组末尾。确保已安装eslint-config-prettier
husky钩子不执行1..husky目录无执行权限。
2. 项目未初始化 git。
1. 运行chmod +x .husky/*(Unix)。
2. 运行git init
1. 赋予钩子脚本执行权限。
2. 确保项目是 git 仓库。
TypeScript 类型在.vue文件中不生效Vue 文件中的<script>标签未设置lang="ts"检查 Vue 单文件组件。确保<script>标签为<script setup lang="ts"><script lang="ts">

9. 最佳实践与工程建议

  1. 渐进式采用:对于老项目,不要试图一次性开启所有严格规则。可以:

    • 先配置好基础设施(TS, ESLint, Prettier)。
    • strict设为false,然后逐步开启子选项如strictNullChecks
    • 将关键的 ESLint 规则设为'warn',待团队适应后再改为'error'
    • 使用// eslint-disable-next-line注释临时禁用某些行的规则,但要有记录并后续清理。
  2. 团队统一配置:将最终的.eslintrc.cjs,.prettierrc,tsconfig.json等配置文件纳入版本控制。新成员克隆项目后,安装依赖即可获得完全一致的开发环境。

  3. IDE/编辑器集成

    • VSCode:安装 ESLint、Prettier、Volar (Vue) / TypeScript Vue Plugin 等扩展。在项目根目录创建.vscode/settings.json,启用保存时自动格式化与修复:
      { "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[vue]": { "editor.defaultFormatter": "esbenp.prettier-vscode" } }
  4. 测试策略

    • 单元测试:用 Vitest 测试纯函数、工具类、Composable/自定义 Hook。
    • 组件测试:使用@vue/test-utils@testing-library/react测试组件的交互和渲染输出。
    • 快照测试:谨慎使用,适用于不希望意外改变的 UI 结构。
    • 测试覆盖率:将其作为质量参考,而非绝对目标。关注核心业务逻辑的覆盖。
  5. CI/CD 集成:在 GitHub Actions、GitLab CI 等流水线中,加入以下步骤:

    # 示例 GitHub Actions 步骤 - name: Install dependencies run: pnpm install - name: Lint run: pnpm lint - name: Type Check run: pnpm type-check # 需要在 package.json 中添加 "type-check": "vue-tsc --noEmit" - name: Test run: pnpm test --run

    确保合并到主分支的代码都通过了类型检查、代码规范检查和单元测试。

  6. 定期更新依赖:使用pnpm outdatednpm-check-updates定期检查并更新 TypeScript、ESLint 插件、Vitest 等依赖,以获取性能改进、新特性支持和安全修复。

10. 总结:从工具到习惯

搭建 TypeScript + ESLint + Vitest 的工程化体系,远不止是安装几个包和复制粘贴配置。其核心价值在于“约束”“反馈”

  • TypeScript 提供编译时的类型约束,让错误在代码运行前暴露。
  • ESLint 提供编码时的风格和质量约束,让团队代码像一个人写出来的。
  • Prettier 提供自动化的格式约束,终结无意义的缩进争论。
  • Vitest 提供变更后的逻辑约束,确保重构不会引入回归缺陷。
  • Husky 和 lint-staged 提供流程约束,让质量检查成为提交代码前的强制动作。

这套体系的最终目的,是让这些“约束”从令人厌烦的条条框框,变成开发者肌肉记忆般的“习惯”。当你在写代码时,IDE 已经实时提示了类型错误和风格问题;当你提交代码时,自动化流程已经帮你做好了检查和格式化;当你修改一个核心函数时,旁边的测试用例会给你重构的信心。

投入最初的 5 小时来搭建这套体系,换来的是项目长期的可维护性、团队协作的顺畅度,以及作为开发者每天被“工具链”默默协助的安心感。这可能是你为项目所做的,性价比最高的投资之一。

建议将本文的最终配置文件收藏或纳入你的项目模板。下次启动新项目时,你将能从容地从一个坚实、现代化的工程化基础开始,专注于业务逻辑的创新,而非环境的折腾。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询