这篇内容是在有道云旧笔记基础上重新整理的 Vue/Vite 工程实践 笔记。原始记录偏碎片化,这里补充了使用场景、阅读顺序和落地时需要注意的边界,方便以后在项目里快速复用。

适用场景 ​

  • Vue/Vite 项目中遇到配置、组件封装、构建或运行时问题时,作为排查入口。
  • 需要把一次性调试经验沉淀成团队内可复用的前端工程实践。
  • 迁移旧项目或升级依赖时,用来对照检查环境变量、插件和组件行为差异。

核心要点 ​

  • 正文已经按 「选择缘由」、「项目配置」、「注意告警」、「解决错误」 等小节组织,阅读时可以先定位到当前问题对应的小节。
  • 保留了 39 段可直接参考的代码或命令,主要涉及 markdown、bash、javascript、json,复制前需要按当前项目路径、版本和变量名做调整。
  • 涉及环境变量的示例只保留命名和加载方式,真实密钥、token 和本地配置应放在未提交的本地文件或 CI 密钥变量中。
  • 涉及命令行操作时,建议先在测试目录或测试环境执行,确认输出符合预期后再应用到生产环境。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479

### 选择缘由
在项目中使用 ESLint 能够自动检测代码中的潜在错误和不符合规范的写法,帮助开发者及时发现问题,减少调试时间。同时,它支持自定义规则,使团队能够统一代码风格,提高团队协作效率。可以保持代码整洁、可读性强(Code Review),从而提升项目的可维护性和可扩展性。这不仅有助于减少代码中的 bug,也能促进更高效的开发流程,减少在版本管理中多人协作开发冲突。

- 选择 @antfu/eslint-config
> 开箱即用,高兼容、灵活多变的配置规则,自动格式修复(类似`Prettier`),默认遵守`.gitignore`

#### 为什么不使用Prettier
Prettier 是一个固执己见的代码格式化程序。它通过解析您的代码并使用自己的规则重新打印它来强制实施一致的样式,这些规则考虑了最大行长度,并在必要时包装代码。
> Prettier 形容它自己是 "一个固执己见的代码格式化工具"。原文:`Prettier is an opinionated code formatter.`
带给我们的困扰
- 强制换行 Prettier 有一个名为 printWidth 的概念,它限制每行适应固定的宽度(在默认设置里是80个字符),你不能彻底禁用它([#3468](https://github.com/prettier/prettier/issues/3468))。
- 让代码适合屏幕宽度,并避免水平滚动是一件很合理事情,经常`损害了代码和 git diff 的可读性`
- 在 JavaScript 中修改字符串内容时,可能会使该行字符数超过了printWidth 设置的值,就会强制将其换行。它破坏了行与行之间展示出的差异并且使得代码难以审查。想象在另外一次 pull request 中,我们可能会将字符串缩短一点,然后 Prettier 又会强制将其合并回一行。来来回回之间,它造成了很多不必要的麻烦。
- 只关心代码风格而完全不关心逻辑
- Eslint和Prettier部分功能重叠导致冲突,需要配置解决(如:在 ESLint 中使用禁用这些重叠规则)
- 无法完全控制代码风格
结合各种因素不使用`Prettier`可以避免`强制换行`导致非意愿换行导致的一系列影响(code review、git diff、git merge等),减少不必要的配置工作

特征
- 自动修复格式(旨在在没有 Prettier 的情况下独立使用)
- 合理的默认值,最佳实践,只需一行配置
- 专为 TypeScript、JSX、Vue、JSON、YAML、Toml、Markdown 等而设计。开箱即用。
- 固执己见,但非常可定制
- ESLint Flat 配置,轻松编写!
- 可选的 React、Svelte、UnoCSS、Astro、Solid 支持
- 可选的格式化程序支持格式化 CSS、HTML、XML 等。
- 样式原则:最小读取,稳定差异,一致
- 排序的导入,悬空的逗号
- 单引号,无分号
- 使用 `ESLint Stylistic`
- 默认遵守`.gitignore`
- 需要 `ESLint v9.5.0+`

### 项目配置
- 安装
```bash
# 使用 CLI 工具
npm dlx @antfu/eslint-config@latest

# 手动安装
pnpm i -D eslint @antfu/eslint-config eslint-plugin-format
```
- 使用
```javascript

// package.json
{
"scripts": {
"lint": "eslint",
"lint:fix": "eslint --fix"
}
}

// eslint.config.js
import antfu from '@antfu/eslint-config'

export default antfu({
vue: {
overrides: {
// enforce order of component top-level elements 自定义 Vue 文件中标签的顺序
'vue/block-order': ['error', {
order: ['template', 'script', 'style'],
}],
},
}, // true
typescript: true,
stylistic: {
indent: 2, // 4, or 'tab'
// 建议使用单引号,在字符串中使用双引号更方便无需转义,视觉效果更清晰,如:'Hello, "world"!', '<div class="eslint" style=""></div>'
quotes: 'single', // or 'double'
},
formatters: {
/**
* Format CSS, LESS, SCSS files, also the `<style>` blocks in Vue
* By default uses Prettier
*/
css: true,
/**
* Format HTML files
* By default uses Prettier
*/
html: true,
/**
* Format Markdown files
* Supports Prettier and dprint
* By default uses Prettier
*/
markdown: 'prettier',
},
// 如果没有限制文件类型,则会为每一个文件进行规则重写
overrides: {},

// JSON 和 YAML 的支持
// jsonc: false,
// yaml: false,

// 忽略某些文件或目录
ignores: [
'**/fixtures',
// ...globs
],
rules: {
// 在生产环境中,不允许使用 console、debugger、alert,但是是为了方便开发调试,需要在生产打包时去掉
'no-console': 'off',
'no-debugger': 'error',
'no-alert': 'error',
},
})
```
- 自动修复
- VS Code 支持

安装 [VS Code ESLint 扩展](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint "点击前往安装 VS Code ESLint 扩展")
配置文件
```json
// .vscode/settings.json
{
// Disable the default formatter, use eslint instead
"prettier.enable": false,
"editor.formatOnSave": false,

// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},

// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{ "rule": "style/*", "severity": "off", "fixable": true },
{ "rule": "format/*", "severity": "off", "fixable": true },
{ "rule": "*-indent", "severity": "off", "fixable": true },
{ "rule": "*-spacing", "severity": "off", "fixable": true },
{ "rule": "*-spaces", "severity": "off", "fixable": true },
{ "rule": "*-order", "severity": "off", "fixable": true },
{ "rule": "*-dangle", "severity": "off", "fixable": true },
{ "rule": "*-newline", "severity": "off", "fixable": true },
{ "rule": "*quotes", "severity": "off", "fixable": true },
{ "rule": "*semi", "severity": "off", "fixable": true }
],

// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"jsonc",
"yaml",
"toml",
"xml",
"gql",
"graphql",
"astro",
"svelte",
"css",
"less",
"scss",
"pcss",
"postcss"
]
}
```

### 注意告警
- 禁用 consoledebugger、alert
当前`no-console`没有配置 `error`,而是配置了`off`是因为考虑到了我们在开发模、测试式下方便调试、定位问题。
> 注意:在生产环境中需要在打包工具中配置去掉上述调试

### 解决错误
>注意:以下“错误写法”关键词不完全代表代码本质错误或否定这种书写风格,而是不符合当前`Eslint`配置规则
- 使用 === 和 !==
```javascript
# Expected '===' and instead saw '=='.eslint(eqeqeq)

# 错误写法
if (data.code == 1) {
...
}

# 推荐写法
if (data.code === 1) {
...
}
```
- 使用 const 声明那些声明后不再被修改的变量
```javascript
# 'xxxx' is never reassigned. Use 'const' instead.eslint(prefer-const)

# 错误写法
let detailData = {
patientId: 123456,
checkNo: NA123456,
}

# 推荐写法
const detailData = {
patientId: 123456,
checkNo: NA123456,
}
```
- 'xxxx'已被定义,但从未使用
```javascript
# 'xxxx' is defined but never used.eslintunused-imports/no-unused-vars

# 错误写法,在某一些情况下我们必须使用`try catch`来捕获我们的异常,但是这里没有使(引)用 error,Eslint会被报错
try {
// 正常业务逻辑 ...
} catch (error) {
// 异常业务逻辑 ...
}

# 推荐写法 A (简化写法)
try {
// 正常业务逻辑 ...
} catch {
// 异常业务逻辑 ...
}

# 推荐写法 B (引用 error)
try {
// 正常业务逻辑 ...
} catch (error) {
// 异常业务逻辑 ...
// 打印或者抛出 任选一种即可
console.log('%c [ error ]-148', error)
throw error
}
```
- 禁止'xxxx'在定义变量之前使用
```javascript
# 'xxxx' was used before it was defined.eslint(ts/no-use-before-define)
# 错误写法
async function deleteLoadingMessage(delayMs) {
await delay(delayMs)
...
}
...
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))


#建议写法
const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
...
async function deleteLoadingMessage(delayMs) {
await delay(delayMs)
...
}

```
- 禁止'xxxx'未使用的引用、申明、解构
```javascript
# 'xxxx' is assigned a value but never used. Allowed unused vars must match /^_/u.eslint(unused-imports/no-unused-vars)

# 错误写法 页面下文中没有使用 (Audios、push、replace、isPlaying)
import Audios from '@/utils/audio'
const { push, replace } = useNavigate()
const isPlaying = ref(false)
let respanse = null

# 正确写法 删除或者注释上述代码片段,建议直接删除
```
- 要求构造函数首字母大写
``` javascript
# A constructor name should not start with a lowercase letter.eslint(new-cap)

# 错误写法
# 这种情况下并非我们书写不规范导致,而是引用第三方类库代码规范、风格与我们不一致导致,这时候我们必须解决这类的`error`,不然不符合我们使用`Eslint`的初衷。
const { jsPDF } = await import('jspdf')
const pdf = new jsPDF('p', 'mm', 'a4')

# 推荐写法
const { jsPDF: JsPDF } = await import('jspdf')
const pdf = new jsPDF('p', 'mm', 'a4')
```
### 跳过检查
- 虽然 // eslint-disable-next-line 很方便,但应谨慎使用。频繁地禁用规则可能会导致代码质量下降,建议仅在确实必要的情况下使用。
- 这条指令可以指定特定的规则进行禁用,比如 // eslint-disable-next-line no-alert,也可以不指定规则,这样就会禁用所有的 ESLint 检查。

- 禁用单行检查
```javascript
// eslint-disable-next-line
```
- 禁用整行检查
```javascript
// eslint-disable-line
```
- 禁用特定规则
```javascript
// eslint-disable-next-line no-alert
```
- 禁用整个文件的检查
```javascript
/* eslint-disable */
```
- 禁用整个文件的检查
```javascript
/* eslint-disable */
```


### 暂存检查
在每次提交代码前应用`lint``auto-fix`,首先需要配置然后在安装:
```javascript

// package.json

{
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged"
},
"lint-staged": {
"*": "eslint --fix"
}
}

pnpm i -D lint-staged simple-git-hooks

// to active the hooks
npx simple-git-hooks


```

### 提交校验

```bash
pnpm i -D @commitlint/cli @commitlint/config-conventional customizable cz-customizable

```
```javascript

{
// ...
"scripts": {
"lint": "eslint . --fix",
"commit": "cz-customizable/standalone.js",
"prepare": "simple-git-hooks",
"postinstall": "simple-git-hooks"
},
"config": {
"commitizen": {
"path": "cz-customizable"
},
"cz-customizable": {
"config": ".cz-config.cjs"
}
},
"simple-git-hooks": {
"pre-commit": "npx lint-staged",
"commit-msg": "npx commitlint --edit $1"
},
"lint-staged": {
"*": "npx lint"
},
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
},
// ...
}

```

> 注意如果在`package.json`中配置了上文的`commitlint`,就会导致`commitlint.config.cjs`配置默认不会被加载
```javascript
// commitlint.config.cjs

module.exports = {
// 继承的规则
extends: ['@commitlint/config-conventional'],
// 定义规则类型
rules: {
// type 类型定义,表示 git 提交的 type 必须在以下类型范围内
'type-enum': [
2,
'always',
[
'feat', // 新功能 feature
'fix', // 修复 bug
'docs', // 文档注释
'style', // 代码格式(不影响代码运行的变动)
'refactor', // 重构(既不增加新功能,也不是修复bug)
'perf', // 性能优化
'test', // 增加测试
'chore', // 构建过程或辅助工具的变动
'revert', // 回退
'build', // 打包
],
],
// subject 大小写不做校验
'subject-case': [0],
},
}
```
```javascript

// 自定义票据,可关联于其它产品如:Jira等
// .cz-config.cjs

module.exports = {
// 可选类型
types: [
{ value: 'feat', name: 'feat: 新功能' },
{ value: 'fix', name: 'fix: 修复' },
{ value: 'docs', name: 'docs: 文档变更' },
{ value: 'style', name: 'style: 代码格式(不影响代码运行的变动)' },
{
value: 'refactor',
name: 'refactor: 重构(既不是增加feature,也不是修复bug)',
},
{ value: 'perf', name: 'perf: 性能优化' },
{ value: 'test', name: 'test: 增加测试' },
{ value: 'chore', name: 'chore: 构建过程或辅助工具的变动' },
{ value: 'revert', name: 'revert: 回退' },
{ value: 'build', name: 'build: 打包' },
],
messages: {
type: '请选择提交类型:',
customScope: '请输入修改范围(可选):',
subject: '请简要描述提交(必填):',
body: '请输入详细描述(可选):',
ticketNumber: '请输入要关联的的Jira票据编号(可选):',
confirmCommit: '确认使用以上信息提交?(y/n/e/h)',
},
skipQuestions: ['body', 'footer'],
ticketNumberPrefix: 'LAI-',
allowTicketNumber: true,
}


// commitlint.config.cjs

const czConfig = require('./.cz-config.cjs')

const { types, messages } = czConfig
module.exports = {
// 继承的规则
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
types?.map(t => t.value),
],
'subject-case': [0],
'jira-issue': [2, 'always'],
},
plugins: [
{
rules: {
'jira-issue': (content) => {
const { subject } = content
const regexTicket = /LAI-\d+(?=\s|$)/g

const isEmptySubject = subject?.replace(regexTicket, '').trim().length === 0

const isRequiredTicket = messages.ticketNumber?.includes('必填') || process.env.JIRA_REQUIRE
const isHasTicket = regexTicket.test(subject)
if (!isHasTicket && isRequiredTicket) {
return [false, '提交信息中需要包含 Jira 票据号']
}
if (isEmptySubject) {
return [false, '提交信息中描述不能为空']
}
return [true]
},
},
},
],
}

```

实践检查清单 &#8203;

  • 先确认 Vue、Vite、Node、包管理器版本,再判断示例是否需要按当前工程结构调整。
  • 涉及构建或插件配置时,建议同时验证开发环境和生产构建结果。
  • 不要把真实 token、密码、私钥或本地 .env.local 提交到仓库。
  • 涉及 UI 或浏览器行为时,至少在桌面端和移动端各验证一次关键路径。

复盘小结 &#8203;

这篇笔记更适合作为问题排查或方案落地前的速查材料。后续如果在真实项目中再次遇到同类问题,可以继续把具体版本、报错信息、最终取舍和验证结果补到对应小节里,让它从代码片段逐步沉淀成完整方案。