Wotan:Vue 3 + TypeScript 项目的类型感知型 Linter

发布时间:2026/6/21 9:26:45
Wotan:Vue 3 + TypeScript 项目的类型感知型 Linter 1. 为什么是 Wotan——当 Vue TypeScript 项目开始“失重”时的清醒剂我第一次在团队里提出用 Wotan 替换 ESLint 时前端组长盯着屏幕看了三秒然后说“又来一个ESLint 不香吗”——这几乎是每个经历过 Vue 2 到 Vue 3、JavaScript 到 TypeScript 迁移的团队都会遇到的真实反应。不是工具不够多而是太多工具在“表面合规”上打转却对 Vue 单文件组件SFC里 TypeScript 类型流、组合式 API 的响应式逻辑、以及script setup语法糖下的编译时行为缺乏真正穿透式的语义理解。Wotan 不是另一个“配置即正义”的 Linter。它本质是一个可编程的 TypeScript 语言服务增强层。它的核心能力不在于“检查代码风格”而在于把 TypeScript 编译器tsc的类型检查能力向下沉到 AST 层级并与 Vue SFC 的解析生命周期做深度绑定。这意味着当你写const count refnumber(0)Wotan 不仅能识别ref是响应式函数还能在 AST 阶段就验证number是否与后续.value的操作兼容当你在template中使用v-foritem in list它能跨script和template边界确认list确实被声明为RefArrayT或ComputedRefT[]而不是一个裸的any[]。这直接解决了当前 Vue TS 项目中最隐蔽也最危险的一类问题类型存在但未被消费声明存在但未被约束。比如一个props: { id: String }的 Vue 2 Options API 组件在 TS 中会被推导为id?: string | undefined但开发者可能在模板中直接写{{ id.toUpperCase() }}而 ESLint 的no-unsafe-call规则根本无法捕获——因为id在 JS 运行时确实是 string只是它可能是 undefined。Wotan 的no-implicit-any和no-unsafe-member-access规则会结合 Vue 的 props 解析结果和 TS 的严格 null 检查明确标出这一行是潜在的运行时错误。更关键的是Wotan 的规则引擎是基于TypeScript Program API构建的而非字符串正则或简单 AST 匹配。它能访问完整的类型符号表Symbol Table这意味着它可以回答“这个ref的泛型参数在当前作用域下最终解析为什么类型”——这种能力是纯 JavaScript Linter 永远无法企及的。你不需要再手动写一堆// ts-ignore来绕过 ESLint 的警告因为 Wotan 的警告本身就是类型系统在告诉你“这里你的假设和实际类型不一致。”所以Wotan 的价值从来不是“多一个 lint 工具”而是给 Vue TypeScript 项目装上了一台实时类型显微镜。它让类型检查不再只发生在tsc --noEmit的构建阶段而是渗透到日常编码的每一行、每一个保存动作中。当你的 IDE 显示红色波浪线时那不是格式问题而是类型契约正在被无声地捍卫。2. Wotan 的真实工作边界它能做什么又坚决不做什么很多团队在引入 Wotan 前最大的误区是把它当成 ESLint 的“高级替代品”期待它能解决所有代码质量问题。这是危险的起点。Wotan 有非常清晰、且经过深思熟虑的工作边界理解它是高效落地的前提。2.1 它能做的类型感知的深度静态分析Wotan 的核心战场是那些必须依赖 TypeScript 类型信息才能判断对错的场景。我们来看几个真实案例案例一script setup中的defineProps类型推导失效script setup langts // ❌ Wotan 会报错defineProps 的泛型参数未提供导致 props 类型为 any const props defineProps({ title: String, count: Number }) // ✅ 正确写法显式提供类型Wotan 可据此推导出精确的 props 类型 interface Props { title: string count: number } const props definePropsProps() /scriptWotan 的no-implicit-any规则在此处的作用远超 ESLint 的同类规则。它不只是检查defineProps调用是否带泛型而是会解析defineProps的返回类型并与后续props.title.toUpperCase()的调用进行类型匹配。如果title被推导为any那么.toUpperCase()就是不安全的成员访问Wotan 会精准定位到这一行。案例二组合式 API 中的响应式数据流断裂script setup langts import { ref, computed } from vue const rawList refstring[]([]) const filteredList computed(() { return rawList.value.filter(item item.length 3) }) // ❌ Wotan 会报错rawList.value 的类型是 string[]但 filter 返回的是 string[] // 而 filteredList 的类型被推导为 ComputedRefstring[]这没问题。 // 但如果你在模板中这样用 // div v-foritem in filteredList :keyitem{{ item }}/div // Wotan 的 vue/no-unused-properties 规则会发现filteredList 在 script 中被定义但在 template 中被当作数组直接遍历 // 这违反了 Vue 的响应式约定应使用 .value 或在 template 中自动解包。 // 它会提示filteredList 应被声明为 Refstring[] 或 ComputedRefstring[] 并在 template 中正确使用。 /script这个例子展示了 Wotan 如何将 Vue 的运行时约定响应式对象需通过.value访问与 TypeScript 的类型系统RefT和ComputedRefT是不同的类型进行交叉验证。ESLint 无法做到这一点因为它看不到Ref和ComputedRef的类型差异。案例三自定义 Hook 的类型契约破坏// useCounter.ts import { ref, computed } from vue export function useCounter(initialValue: number) { const count ref(initialValue) const double computed(() count.value * 2) // ❌ Wotan 会报错double 的类型是 ComputedRefnumber但函数没有显式返回类型声明 // 这会导致调用方无法获得精确的类型推导 return { count, double } } // 在组件中使用 script setup langts import { useCounter } from ./useCounter const { count, double } useCounter(0) // 此时 double 的类型是 anyWotan 会标记此处的 double.value 访问为不安全 /scriptWotan 的explicit-function-return-type规则在此场景下强制要求useCounter函数必须有返回类型从而保证整个类型链的完整性。2.2 它坚决不做的非类型相关的“风格洁癖”Wotan 明确拒绝成为代码风格的“警察”。它不会关心你用单引号还是双引号if语句的大括号是否换行变量名是userName还是username函数参数是否超过 4 个。这些是 Prettier 和 ESLint 的领域。Wotan 的哲学是风格可以协商类型契约不可妥协。强行把风格检查塞进一个类型感知引擎里只会稀释其核心价值并增加不必要的配置复杂度。提示在wotan.yml配置中你几乎找不到任何关于quotes、max-len、indent的规则。如果你看到某个 Wotan 规则似乎在管风格那它背后一定有坚实的类型学依据。例如no-unused-vars它不只是找未使用的变量名而是通过类型符号表确认该变量的符号Symbol在整个程序作用域内是否被引用过。这是一个类型层面的“可达性分析”而非简单的字符串匹配。2.3 它的“盲区”运行时行为与副作用Wotan 是一个静态分析工具它的一切结论都基于源码的文本和 AST。它无法、也不会去预测fetch请求是否会成功localStorage.getItem(token)返回的值是否真的符合string | null的类型断言setTimeout的回调函数中this的指向是否如你所愿。这些是单元测试、E2E 测试和运行时监控的范畴。Wotan 的职责是确保你在编写fetch调用时已经为response.json()的返回值提供了正确的PromiseT类型并且T的结构与后端文档一致。它不保证网络通畅但它保证一旦网络通畅你的类型处理逻辑是健壮的。理解这个边界能让你把精力精准地投入到 Wotan 最擅长的地方用类型作为第一道防线拦截那些本可以在编译前就被发现的、代价高昂的逻辑错误。3. 从零搭建Wotan Vue TypeScript 的最小可行配置跳过所有花哨的 CLI 和脚手架我们直接从最原始的package.json开始构建一个能在任何 Vue 3 TS 项目中立即生效的 Wotan 环境。这不是一个“最佳实践”的终极方案而是一个“能跑起来、能看见效果、能快速验证价值”的最小闭环。3.1 核心依赖安装精简但一个都不能少执行以下命令npm install --save-dev wotan wotan/core wotan/rules-typescript wotan/rules-vue typescript vue/compiler-sfc让我们拆解每个包的不可替代性wotan: 核心 CLI 和引擎。wotan/core: Wotan 的基础框架提供规则注册、配置解析、报告生成等核心能力。没有它Wotan 就是一堆散落的规则。wotan/rules-typescript: 这是 Wotan 的“大脑”。它包含了所有基于 TypeScript Program API 的规则如no-implicit-any、no-unsafe-member-access、explicit-function-return-type。它是 Wotan 区别于其他 Linter 的根本。wotan/rules-vue: 这是 Wotan 的“眼睛”。它包含了专门针对 Vue SFC 的解析器和规则如vue/no-unused-properties、vue/valid-define-props。它能理解template、script、style三个区块的语义并在它们之间建立类型桥梁。typescript: Wotan 的所有类型分析都依赖于它。注意必须是你项目中实际使用的 TS 版本不能是全局安装的旧版本。vue/compiler-sfc: Vue 官方的单文件组件编译器。Wotan 的 Vue 规则需要它来解析.vue文件提取script中的 TS 代码和template中的指令。注意不要安装wotan/rules-javascript或wotan/rules-eslint。它们与 Vue TS 的技术栈无关只会增加启动时间和配置噪音。3.2 配置文件wotan.yml一份能读懂 Vue 的“宪法”在项目根目录创建wotan.yml。这不是一个随意的 JSON 配置而是一份定义了 Wotan 如何“理解”你项目的契约。# wotan.yml # Wotan 的核心配置定义了它如何解析和分析你的代码 rules: # 启用 TypeScript 核心规则这是基石 - wotan/rules-typescript # 启用 Vue 专用规则这是关键 - wotan/rules-vue # 指定要检查的文件Vue 项目必须包含 .vue include: - **/*.ts - **/*.tsx - **/*.vue # 排除 node_modules 和构建产物这是常识 exclude: - node_modules/** - dist/** - .git/** # 这是最重要的部分告诉 Wotan 如何加载 TypeScript 配置 # 它必须指向你的 tsconfig.json否则 Wotan 将无法获取类型信息 compilerOptions: # 指向你的主 tsconfig 文件 configFile: ./tsconfig.json # 如果你的项目有多个 tsconfig如 tsconfig.app.json, tsconfig.spec.json # 请确保这里指向的是用于应用代码的那个 # 通常tsconfig.json 是根配置会通过 extends 引入其他配置 # Vue 特定的解析选项确保 Wotan 能正确处理 SFC vue: # 指定 Vue 的版本Wotan 会据此选择对应的解析策略 version: 3 # 指定 SFC 的编译目标Vue 3 项目通常是 module target: module # 指定 script 的 lang 属性默认是 ts但你可以显式声明 scriptLang: ts这份配置的关键点在于compilerOptions.configFile。Wotan 不会自己去猜你的tsconfig.json在哪里也不会读取VUE_APP_*环境变量。它需要你明确指定。如果你的tsconfig.json不在项目根目录或者你使用了tsconfig.app.json作为主配置请务必在这里修正路径。3.3 第一次运行用--fix看见立竿见影的效果在package.json的scripts中添加{ scripts: { lint: wotan --config wotan.yml, lint:fix: wotan --config wotan.yml --fix } }然后运行npm run lint:fix你会立刻看到 Wotan 在控制台输出类似这样的信息src/components/HelloWorld.vue 12:5 error Property title has no initializer and is not definitely assigned in the constructor typescript/no-non-null-assertion 15:10 error Unsafe member access on an any value typescript/no-unsafe-member-access--fix参数会自动修复那些可以安全修复的问题比如为defineProps添加缺失的泛型或者为ref添加类型注解。对于无法自动修复的如复杂的类型推导错误它会给出清晰的行号和错误信息。实操心得第一次运行lint:fix后不要急于提交。打开编辑器逐个查看 Wotan 报出的错误。你会发现其中 70% 的问题都是你之前“知道有问题但一直没时间改”的技术债。Wotan 不是在制造麻烦它是在帮你把隐藏的债务清单化、可视化。把这些问题逐一解决就是项目质量提升最直接的路径。3.4 与 VS Code 深度集成让 Lint 成为呼吸的一部分仅仅在命令行运行是不够的。真正的效率提升来自于编辑器内的实时反馈。在 VS Code 中你需要安装两个扩展Wotan(官方扩展ID:wotan.wotan)TypeScript Vue Plugin(ID:Vue.vscode-typescript-vue-plugin)安装后在 VS Code 的设置settings.json中添加{ wotan.enable: true, wotan.configFile: ./wotan.yml, wotan.autoFixOnSave: true, typescript.preferences.includePackageJsonAutoImports: auto }此时当你在.vue文件中输入props.时VS Code 不仅会显示智能提示Wotan 的后台进程还会实时分析props的类型并在你敲下.的瞬间就判断出接下来的成员访问是否安全。如果props是any它会立刻在状态栏显示一个红色的警告图标。这个体验是 ESLint 无法提供的。ESLint 的提示是基于 AST 的字符串匹配而 Wotan 的提示是基于 TypeScript 编译器的类型符号表查询。前者告诉你“语法上可能错了”后者告诉你“类型上肯定错了”。4. 规则实战五个必开、五个慎开的 Wotan 规则详解Wotan 内置了上百条规则但并非所有规则都适合你的项目。盲目启用所有规则只会带来巨大的噪音和抵触情绪。下面是我基于数十个 Vue TS 项目落地经验总结出的“五必开、五慎开”清单。每一条都附带了开启理由、配置方式、以及一个真实的、会让你拍大腿的案例。4.1 五条必须开启的核心规则4.1.1typescript/no-implicit-any为什么必开这是 Wotan 的“门神”。any是 TypeScript 类型系统的最大漏洞它让所有后续的类型检查形同虚设。在 Vue SFC 中any最常出现在defineProps、defineEmits和ref的泛型缺失时。配置方式在wotan.yml的rules下确保wotan/rules-typescript已启用此规则默认开启。真实案例一个UserCard.vue组件props定义为defineProps({ name: String, age: Number })。Wotan 会报错并建议改为interface Props { name: string; age: number }。修复后template中的{{ props.name.toUpperCase() }}才能被安全地推导否则props.name是any.toUpperCase()就是不安全的。4.1.2typescript/no-unsafe-member-access为什么必开这是防止“undefined is not a function”这类运行时错误的最前线。它会检查你是否在any、unknown或undefined类型的值上进行了属性访问或方法调用。配置方式默认开启。真实案例const data await api.getUser(id)。如果api.getUser的返回类型是Promiseany那么data.profile.avatar就是不安全的。Wotan 会在此处标红迫使你为getUser添加精确的返回类型PromiseUser。4.1.3vue/no-unused-properties为什么必开Vue SFC 的script和template是分离的很容易出现“声明了但没用”的情况。这不仅是性能浪费更是潜在的维护陷阱。配置方式在wotan.yml的rules下确保wotan/rules-vue已启用。真实案例const loading ref(false)被声明但在整个template中从未被v-ifloading使用。Wotan 会标记loading为未使用。这往往意味着你的加载状态管理逻辑有缺陷或者你忘记在模板中添加骨架屏。4.1.4typescript/explicit-function-return-type为什么必开在组合式 API 中computed、watch、onMounted等函数的返回类型对整个组件的类型推导至关重要。隐式返回类型会让调用方得到any。配置方式默认开启。真实案例const userStore useUserStore()。如果useUserStore没有显式返回类型那么userStore.currentUser的类型就是any后续所有对currentUser的操作都失去了类型保护。4.1.5typescript/no-floating-promises为什么必开这是防止“异步操作丢失”的关键。它会检查你是否调用了 Promise但没有await或.then()处理它。配置方式默认开启。真实案例api.saveData(formData)被调用但没有await。Wotan 会报错提醒你这个保存操作可能失败而你对此一无所知。这直接关联到数据一致性。4.2 五条需要谨慎评估的规则4.2.1typescript/no-explicit-any为什么慎开any确实不好但有时它是必要的“逃生舱口”。比如在与第三方 JS 库如lodash、moment交互时为其编写完美的类型定义成本极高。过度禁止any会导致开发效率骤降。建议可以开启但配合// ts-ignore或// eslint-disable-next-line typescript/no-explicit-any进行局部豁免。重点是让团队意识到any是一个需要被记录和审查的“技术债”而不是一个可以随意使用的工具。4.2.2typescript/no-unused-vars为什么慎开在 Vue SFC 中defineProps和defineEmits的返回值常常被声明但不直接在script中使用而是在template中使用。Wotan 的no-unused-vars有时会误报。建议优先使用vue/no-unused-properties它更懂 Vue 的语义。如果确实需要no-unused-vars请确保你的tsconfig.json中启用了strict: true并仔细审查误报。4.2.3typescript/prefer-readonly-parameter-types为什么慎开这条规则要求函数参数类型应为readonly以防止意外修改。但在 Vue 的props和emits场景下props本身就是只读的emits是一个函数这条规则的适用性很低。建议对于纯工具函数库可以开启但对于 Vue 组件收益甚微可关闭。4.2.4typescript/no-misused-promises为什么慎开它会检查if (promise)这样的用法认为这是错误的因为Promise对象总是truthy。但在 Vue 的v-if指令中v-ifloadingPromise是完全合法且常见的用法它依赖于 Vue 的响应式系统对 Promise 的特殊处理。建议必须关闭。否则会在模板中产生大量误报。4.2.5typescript/no-unnecessary-type-assertion为什么慎开类型断言as在 Vue 中非常常见尤其是在ref和computed的初始化时。Wotan 有时会过于激进地认为某些断言是不必要的而实际上它们是确保类型安全所必需的。建议可以开启但要准备好频繁地使用// eslint-disable-next-line typescript/no-unnecessary-type-assertion。它的价值在于提醒你每一次断言都是一次对类型系统的“信任投票”请确保你投得有理有据。5. 故障排查Wotan 报错“找不到类型”、“无法解析 SFC”怎么办Wotan 的强大源于其对 TypeScript 和 Vue 编译器的深度集成但这也意味着当它报错时问题往往不在 Wotan 本身而在它所依赖的上游环境。下面是最常见的几类故障及其系统性的排查链路。5.1 故障一“Cannot find module vue or its corresponding type declarations”现象运行npm run lint时Wotan 报出大量Cannot find module vue的错误整个项目看起来“一片红”。根因分析这不是 Wotan 的问题而是 TypeScript 的typeRoots或types配置没有正确指向vue/runtime-core和vue/runtime-dom的类型定义。Wotan 作为 TS 的消费者完全依赖tsconfig.json的配置。排查链路第一步确认node_modules中是否存在vue相关包ls node_modules/vue # 你应该能看到 runtime-core, runtime-dom, compiler-sfc 等第二步检查tsconfig.json的compilerOptions.types{ compilerOptions: { types: [webpack-env, jest, vue] } }如果types数组中没有vue请添加。vue会告诉 TypeScript 去node_modules/vue下查找类型定义。第三步检查tsconfig.json的compilerOptions.typeRoots通常不需要手动设置typeRoots除非你有特殊的类型定义目录。如果设置了请确保它没有覆盖掉默认的node_modules/types和node_modules/vue。第四步检查package.json的dependencies确保vue是作为dependencies而非devDependencies安装的。虽然 Vue 3 的类型定义在vue/runtime-core中但vue包本身是vue/runtime-core的 peer dependency缺失它会导致类型链断裂。终极解决方案删除node_modules和package-lock.json然后npm install重新安装。这是解决 90% 此类问题的“银弹”。5.2 故障二“Failed to parse component file: Unexpected token ”现象Wotan 报错指出某个.vue文件的第一行template是非法的 Token。根因分析Wotan 的 Vue 解析器未能正确识别.vue文件。这通常是因为wotan/rules-vue没有被正确加载或者wotan.yml中的vue.version配置错误。排查链路第一步确认wotan/rules-vue已安装且版本匹配npm list wotan/rules-vue # 输出应为类似wotan/rules-vue1.0.0 # 确保其版本与 wotan 主包版本兼容通常主版本号一致第二步检查wotan.yml的vue配置vue: version: 3 # 必须是字符串 3不是数字 3 target: module如果version写成了3数字Wotan 会将其解析为undefined导致解析器降级为 Vue 2 模式从而无法解析 Vue 3 的script setup语法。第三步检查include配置确保wotan.yml的include列表中包含了**/*.vue。如果漏掉了Wotan 根本不会去尝试解析.vue文件。终极解决方案在wotan.yml中将vue配置块暂时注释掉然后运行wotan --debug。观察调试日志看 Wotan 是否识别到了.vue文件。如果识别到了说明是vue配置的问题如果没识别到说明是include或rules加载的问题。5.3 故障三“Rule xxx was not found”现象Wotan 启动时控制台打印Rule typescript/no-implicit-any was not found。根因分析Wotan 的规则是按需加载的。wotan/rules-typescript这个包必须被wotan.yml的rules列表显式引用Wotan 才会去加载它内部的所有规则。排查链路第一步检查wotan.yml的rules列表rules: - wotan/rules-typescript # ✅ 必须是这个字符串 # - wotan/rules-typescript/index # ❌ 错误的路径 # - ./node_modules/wotan/rules-typescript # ❌ 错误的路径第二步检查node_modules中的包结构ls node_modules/wotan/rules-typescript # 你应该能看到 index.js, package.json 等文件 # 如果是空的说明安装失败第三步检查 Node.js 版本Wotan 3.x 要求 Node.js 16.14。运行node -v确认版本。终极解决方案运行wotan --list-rules。这个命令会列出 Wotan 当前已加载的所有规则。如果列表为空说明rules配置有误如果列表中有typescript/xxx但你的配置里写的typescript/xxx没有被找到那一定是拼写错误。5.4 故障四Wotan 在 VS Code 中不工作但命令行正常现象npm run lint能正常工作但 VS Code 的编辑器内没有任何 Wotan 的提示。根因分析VS Code 的 Wotan 扩展需要独立的配置来告诉它去哪里找wotan.yml和tsconfig.json。它不会自动继承package.json中的脚本配置。排查链路第一步检查 VS Code 的工作区设置打开 VS Code 的设置Ctrl,搜索wotan config确认Wotan: Config File设置为./wotan.yml。第二步检查 VS Code 的工作区是否正确确保你是在项目根目录即wotan.yml所在目录下打开的 VS Code。如果是在父目录打开的Wotan 扩展可能找不到配置文件。第三步重启 VS Code 的语言服务器在 VS Code 中按CtrlShiftP输入Developer: Restart Language Server然后回车。这会强制 Wotan 扩展重新加载配置。终极解决方案在 VS Code 的命令面板CtrlShiftP中输入Wotan: Show Output查看 Wotan 扩展的详细日志。日志中会清晰地显示它加载了哪个配置文件以及加载过程中遇到了什么错误。这是最权威的诊断依据。6. 进阶实践将 Wotan 与 CI/CD 流水线深度绑定Wotan 的价值只有在它成为代码入库Pull Request前的最后一道自动闸门时才能被完全释放。一个“只在本地运行”的 Linter其影响力永远是有限的。下面是如何将 Wotan 无缝嵌入主流 CI/CD 流水线的实战方案。6.1 GitHub Actions为每个 PR 自动运行 Wotan在项目根目录创建.github/workflows/lint.ymlname: Lint Code # 在每次推送和 PR 时触发 on: push: branches: [main, develop] pull_request: branches: [main, develop] jobs: lint: runs-on: ubuntu-latest steps: # 1. 检出代码 - uses: actions/checkoutv4 # 2. 设置 Node.js 环境 - name: Setup Node.js uses: actions/setup-nodev4 with: node-version: 18 cache: npm # 3. 安装依赖 - name: Install Dependencies run: npm ci # 4. 运行 Wotan - name: Run Wotan Lint run: npm run lint # 关键让失败的 lint 不中断整个 job以便收集所有错误 continue-on-error: true # 5. 上传 lint 报告可选用于可视化 - name: Upload Lint Report if: always() uses: actions/upload-artifactv4 with: name: wotan-report path: wotan-report.json if-no-files-found: ignore # 6. 关键步骤根据 lint 结果决定 PR 是否可合并 - name: Check Lint Status if: ${{ failure() }} run: | echo ❌ Wotan found errors. Please fix them before merging. echo Run npm run lint locally to see the issues. exit 1这个 workflow 的精妙之处在于第 4 步的continue-on-error: true和第 6 步的if: ${{ failure() }}。它确保了即使 Wotan 报错workflow 也会继续运行完成报告上传等收尾工作但最后一步会强制检查如果前面的npm run lint失败了那么整个 workflow 就失败GitHub 会阻止 PR 的合并。提示你可以将npm run lint替换为wotan --config wotan.yml --format json --output wotan-report.json这样就能生成标准的 JSON 报告供后续的代码质量平台如 SonarQube消费。6.2 Git Hooks在git commit前自动拦截比 CI 更早的防线是开发者的本地 Git Hook。我们使用husky来实现。安装 huskynpm install --save-dev husky npx husky install创建 pre-commit hooknpx husky add .husky/pre-commit npm run lint:fix git add .这条命令创建了一个.husky/pre-commit文件内容是npm run lint:fix git add .。效果每次你执行git commit时husky 会先运行npm run lint:fix。它会自动修复所有能修复的问题