Vue3集成Eslint保存时代码缩进自动还原问题求助
解决Vue3项目中ESLint缩进异常问题
问题现象
- .ts文件和Vue文件的style部分格式正常,但template中的组件props、整个script部分存在缩进问题
- 保存文件时,代码会先被格式化为预期的缩进样式,随即又还原为无缩进状态
示例代码(script部分)
初始状态:
type MessagesType = { title: string; message: string; type?: string; };
预期状态:
type MessagesType = { title: string; message: string; type?: string; };
无论初始是哪种状态,保存后都会还原为无缩进的初始状态。
项目配置文件
.eslintrc.js
module.exports = { root: true, env: { commonjs: true, jest: true, es2022: true, }, parser: 'vue-eslint-parser', parserOptions: { parser: '@typescript-eslint/parser', }, plugins: ['@typescript-eslint', 'import'], extends: [ 'eslint:recommended', 'plugin:import/recommended', 'plugin:import/typescript', 'plugin:vue/vue3-recommended', 'plugin:@typescript-eslint/recommended', ], rules: { // global rules: 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'warn', 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'warn', 'no-async-promise-executor': 'warn', 'no-trailing-spaces': 'warn', 'no-template-curly-in-string': 'warn', 'no-multi-spaces': 'warn', // typescript rules: '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-this-alias': 'off', '@typescript-eslint/member-delimiter-style': 'warn', '@typescript-eslint/type-annotation-spacing': 'warn', '@typescript-eslint/consistent-type-definitions': ['warn', 'type'], '@typescript-eslint/no-require-imports': 'warn', '@typescript-eslint/prefer-for-of': 'warn', '@typescript-eslint/ban-ts-comment': 'warn', 'semi': 'off', '@typescript-eslint/semi': 'warn', 'comma-dangle': 'off', '@typescript-eslint/comma-dangle': ['warn', 'always-multiline'], 'indent': 'off', '@typescript-eslint/indent': ['warn', 2], 'object-curly-spacing': 'off', '@typescript-eslint/object-curly-spacing': ['error', 'always'], 'quotes': 'off', '@typescript-eslint/quotes': ['warn', 'single'], 'comma-spacing': 'off', '@typescript-eslint/comma-spacing': 'warn', 'block-spacing': 'off', '@typescript-eslint/block-spacing': 'warn', 'func-call-spacing': 'off', '@typescript-eslint/func-call-spacing': 'warn', 'key-spacing': 'off', '@typescript-eslint/key-spacing': 'warn', 'keyword-spacing': 'off', '@typescript-eslint/keyword-spacing': 'warn', 'space-before-blocks': 'off', '@typescript-eslint/space-before-blocks': 'warn', 'space-before-function-paren': 'off', '@typescript-eslint/space-before-function-paren': ['warn', { 'anonymous': 'always', 'named': 'never', 'asyncArrow': 'always', }], 'space-infix-ops': 'off', '@typescript-eslint/space-infix-ops': 'warn', 'no-extra-semi': 'off', '@typescript-eslint/no-extra-semi': 'warn', 'no-unused-expressions': 'off', '@typescript-eslint/no-unused-expressions': 'warn', '@typescript-eslint/consistent-type-imports': ['warn', { prefer: 'type-imports', disallowTypeAnnotations: true, fixStyle: 'inline-type-imports', }], // Vue specific rules: 'vue/attribute-hyphenation': 'off', 'vue/v-on-event-hyphenation': 'off', 'vue/script-indent': ['warn', 2, { 'baseIndent': 1 }], 'vue/max-attributes-per-line': ['warn', { 'singleline': { 'max': 4, }, 'multiline': { 'max': 1, }, }], 'vue/no-unused-components': 'warn', 'vue/no-unused-vars': 'warn', 'vue/component-name-in-template-casing': 'warn', 'vue/component-options-name-casing': 'warn', 'vue/custom-event-name-casing': 'warn', 'vue/define-macros-order': 'warn', 'vue/define-props-declaration': ['error', 'runtime'], 'vue/no-unused-refs': 'warn', 'vue/padding-line-between-blocks': 'warn', 'vue/prefer-prop-type-boolean-first': 'warn', // import rules: 'import/no-unresolved': 'warn', 'import/order': ['warn', { 'groups': [ ['builtin', 'external'], 'internal', ['sibling', 'parent', 'index'], // 'type', ], 'newlines-between': 'always', 'warnOnUnassignedImports': true, }, ], 'import/consistent-type-specifier-style': ['warn', 'prefer-inline'], 'import/no-duplicates': ['warn', { 'prefer-inline': true }], }, settings: { 'import/parsers': { '@typescript-eslint/parser': ['.ts', '.tsx'], }, 'import/resolver': { 'typescript': { 'alwaysTryTypes': true, }, }, }, overrides: [ { // disable original/ts indent rule for .vue files, to allow vue/script-indent handle them (otherwise both will) files: ['*.vue'], rules: { 'indent': 'off', '@typescript-eslint/indent': 'off', }, }, { files: [ '*.spec.{j,t}s', ], rules: { 'no-unused-expressions': 'off', }, }, { files: [ '**/__tests__/*.{j,t}s?(x)', '**/tests/unit/**/*.spec.{j,t}s?(x)', ], env: { jest: true, }, }, ], };
package.json
{ "name": "login", "version": "3.3.3", "private": true, "scripts": { "start": "vite --port=8082", "build": "vite build", "lint:production": "" }, "dependencies": { "@quasar/extras": "^1.16.6", "axios": "^1.5.0", "node": "^16.20.2", "pinia": "^2.1.6", "quasar": "^2.12.7", "vue": "^3.3.4", "vue-router": "^4.2.4" }, "devDependencies": { "@quasar/vite-plugin": "^1.5.0", "@types/node": "^20.8.2", "@typescript-eslint/eslint-plugin": "^5.59.9", "@typescript-eslint/parser": "^5.62.0", "@vitejs/plugin-vue": "^4.2.3", "eslint": "^8.51.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-import": "^2.28.1", "eslint-plugin-vue": "^9.17.0", "sass": "^1.32.12", "typescript": "^5.0.2", "vite": "^4.4.5", "vue-tsc": "^1.8.5" }, "pre-commit": [ "lint:production" ] }
解决步骤
1. 排查编辑器格式化工具冲突
- 若使用VSCode,检查是否同时启用了ESLint和其他格式化工具(如内置TypeScript格式化、Prettier),规则不一致会导致保存时互相覆盖。
- 在VSCode的
settings.json中配置,指定ESLint为Vue和TypeScript文件的默认格式化工具:"[vue]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, "[typescript]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, "editor.codeActionsOnSave": { "source.fixAll.eslint": true }
2. 调整ESLint缩进规则
- 针对Vue文件的script部分,完善
vue/script-indent规则配置,确保和TypeScript缩进规则对齐:'vue/script-indent': ['warn', 2, { baseIndent: 1, switchCase: 1, ignores: [] }] - 确认
@typescript-eslint/indent规则的缩进值为2,与Vue规则保持一致。
3. 同步TypeScript配置
- 检查
tsconfig.json中的格式化相关配置(如indentSize),确保和ESLint的缩进规则匹配。 - 若项目使用
.editorconfig文件,同步其中的indent_size设置为2。
4. 清除缓存并重启服务
- 执行命令清除ESLint缓存:
npx eslint --cache --clear - 重启VSCode和Vite开发服务,避免缓存导致规则不生效。
内容的提问来源于stack exchange,提问作者Narok
相关产品推荐
相关产品推荐

