Loading...

文章背景图

Vue3 工程化脚手架配置(ESLint 等)

2026-05-29
0
- 字
- 分钟
|

Vue 3 + TypeScript + Vite 项目模板,集成 ESLint、Stylelint、Prettier、Commitlint、Husky。

cover: 附件/Halo封面/design.jpg

快速开始

pnpm create vite  # vue ts
cd <your project>
git init
pnpm config set registry https://registry.npmmirror.com

cover: 附件/Halo封面/design.jpg

1. 安装运行时依赖

pnpm add pinia pinia-plugin-persistedstate vue-router
pnpm add -D vite-plugin-vue-devtools

编辑 src/main.ts

import { createPinia } from "pinia";

import "@/style.css";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
import { createApp } from "vue";

import App from "@/App.vue";
import router from "@/router";

const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);

createApp(App).use(pinia).use(router).mount("#app");

创建 src/router/index.ts

import { createRouter, createWebHistory } from "vue-router";

import HelloWorld from "@/components/HelloWorld.vue";

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      component: HelloWorld,
      name: "home",
      path: "/",
    },
  ],
});

export default router;

编辑 vite.config.ts

import vue from "@vitejs/plugin-vue";
import { defineConfig } from "vite";
import VueDevTools from "vite-plugin-vue-devtools";

export default defineConfig({
  plugins: [VueDevTools(), vue()],
  resolve: {
    tsconfigPaths: true,
  },
});

编辑 tsconfig.app.json

{
  "extends": "@vue/tsconfig/tsconfig.dom.json",
  "compilerOptions": {
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
    "types": ["vite/client"],
    "paths": { "@/*": ["./src/*"] },
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "erasableSyntaxOnly": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

cover: 附件/Halo封面/design.jpg

2. 安装 Git 提交规范工具

pnpm add -D husky lint-staged @commitlint/cli @commitlint/config-conventional czg
pnpm exec husky init

编辑 .husky/pre-commit

pnpm lint-staged

编辑 .husky/commit-msg

commitlint --edit "$1"

注意:确保文件编码为 UTF-8 无 BOM,否则 shell 无法识别命令。

创建 commitlint.config.js

// @see https://commitlint.js.org/#/reference-rules
export default {
  extends: ["@commitlint/config-conventional"],

  rules: {
    "type-enum": [
      2,
      "always",
      [
        "feat",     // ✨ 新增功能
        "fix",      // 🐛 修复缺陷
        "docs",     // 📝 文档变更
        "style",    // 🌈 代码格式
        "refactor", // 🔄 代码重构
        "perf",     // 🚀 性能优化
        "test",     // 🧪 测试改动
        "build",    // 📦️ 构建/依赖变更
        "ci",       // ⚙️ CI 配置
        "revert",   // ↩️ 回滚
        "chore",    // 🛠️ 辅助工具
        "wip",      // 🚧 开发中
      ],
    ],
    "subject-case": [0],
  },

  prompt: {
    messages: {
      type: "选择你要提交的类型:",
      scope: "选择一个提交范围(可选):",
      customScope: "请输入自定义的提交范围:",
      subject: "填写简短精炼的变更描述:\n",
      body: '填写更加详细的变更描述(可选)。使用 "|" 换行:\n',
      breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行:\n',
      footerPrefixesSelect: "选择关联 issue 前缀(可选):",
      customFooterPrefix: "输入自定义 issue 前缀:",
      footer: "列举关联 issue(可选)例如: #31, #I3244:\n",
      generatingByAI: "正在通过 AI 生成你的提交简短描述…",
      generatedSelectByAI: "选择一个 AI 生成的简短描述:",
      confirmCommit: "是否提交或修改commit?",
    },
    types: [
      { value: "feat",     name: "特性:     ✨  新增功能", emoji: ":sparkles:" },
      { value: "fix",      name: "修复:     🐛  修复缺陷", emoji: ":bug:" },
      { value: "docs",     name: "文档:     📝  文档变更", emoji: ":memo:" },
      { value: "style",    name: "格式:     🌈  代码格式", emoji: ":lipstick:" },
      { value: "refactor", name: "重构:     🔄  代码重构", emoji: ":recycle:" },
      { value: "perf",     name: "性能:     🚀  性能优化", emoji: ":zap:" },
      { value: "test",     name: "测试:     🧪  测试改动", emoji: ":white_check_mark:" },
      { value: "build",    name: "构建:     📦️  构建/依赖变更", emoji: ":package:" },
      { value: "ci",       name: "集成:     ⚙️  CI 配置", emoji: ":ferris_wheel:" },
      { value: "revert",   name: "回退:     ↩️  回滚 commit", emoji: ":rewind:" },
      { value: "chore",    name: "其他:     🛠️  辅助工具", emoji: ":hammer:" },
      { value: "wip",      name: "开发中:   🚧  临时提交", emoji: ":construction:" },
    ],
    issuePrefixes: [{ value: "closed", name: "closed:   ISSUES has been processed" }],
  },
};

编辑 package.json 添加 scripts

{
  "scripts": {
    "prepare": "husky",
    "commit": "czg",
    "preinstall": "npx only-allow pnpm"
  }
}

使用 czg 而非 git-cz:czg 是 cz-git 的原生 CLI,启动速度远快于 commitizen 适配器模式,且无需额外安装 commitizen 和 cz-git。

cover: 附件/Halo封面/design.jpg

3. 安装 Lint 工具

pnpm add -D eslint @eslint/js typescript-eslint eslint-plugin-vue prettier \
  eslint-config-prettier stylelint stylelint-config-standard-scss postcss-html \
  stylelint-config-recess-order eslint-plugin-perfectionist \
  eslint-plugin-vuejs-accessibility sass

创建 eslint.config.js

import js from "@eslint/js";
import eslintConfigPrettier from "eslint-config-prettier/flat";
import perfectionist from "eslint-plugin-perfectionist";
import pluginVue from "eslint-plugin-vue";
import vuejsAccessibility from "eslint-plugin-vuejs-accessibility";
import tseslint from "typescript-eslint";

export default tseslint.config(
  js.configs.recommended,
  ...tseslint.configs.recommended,
  ...pluginVue.configs["flat/recommended"],
  perfectionist.configs["recommended-natural"],
  ...vuejsAccessibility.configs["flat/recommended"],
  {
    files: ["**/*.ts", "**/*.vue"],
    languageOptions: {
      parserOptions: {
        parser: tseslint.parser,
        project: ["./tsconfig.app.json", "./tsconfig.node.json"],
        extraFileExtensions: [".vue"],
      },
    },
    rules: {
      "@typescript-eslint/consistent-type-imports": "error",
      "@typescript-eslint/prefer-optional-chain": "error",
      "@typescript-eslint/prefer-nullish-coalescing": "error",

      // 代码质量
      eqeqeq: "error",
      curly: "error",
      "no-var": "error",
      "prefer-const": "error",
      "no-implicit-coercion": "error",

      // TypeScript 一致性
      "@typescript-eslint/array-type": ["error", { default: "array-simple" }],
      "@typescript-eslint/prefer-for-of": "error",
      "@typescript-eslint/prefer-string-starts-ends-with": "error",
      "@typescript-eslint/prefer-includes": "error",
      "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error",
      "@typescript-eslint/switch-exhaustiveness-check": "error",
    },
  },
  { ignores: ["dist/", "node_modules/", "*.d.ts"] },
  {
    rules: {
      "vue/multi-word-component-names": "off",
      "perfectionist/sort-objects": "off",

      // Vue 一致性
      "vue/block-order": ["error", { order: ["script", "template", "style"] }],
      "vue/html-button-has-type": "error",
      "vue/require-default-prop": "error",
      "vue/require-explicit-emits": "error",
      "vue/html-self-closing": [
        "error",
        {
          html: { void: "always", normal: "always", component: "always" },
        },
      ],
    },
  },
  eslintConfigPrettier,
);

创建 stylelint.config.js

/** @type {import("stylelint").Config} */
export default {
  extends: ["stylelint-config-standard-scss", "stylelint-config-recess-order"],
  overrides: [
    {
      files: ["**/*.vue"],
      customSyntax: "postcss-html",
    },
  ],
  rules: {
    "scss/operator-no-unspaced": null,

    // Vue SFC :deep() pseudo-class
    "selector-pseudo-class-no-unknown": [
      true,
      {
        ignorePseudoClasses: ["deep", "global", "slotted"],
      },
    ],

    // 代码约定
    "declaration-no-important": true,
    "max-nesting-depth": 3,
    "color-named": "never",
    "function-url-no-scheme-relative": true,
    // 禁止 BEM __ 双下划线命名
    "selector-class-pattern": "^(?!.*__)[a-z][a-z0-9-_]*$",
    "property-no-vendor-prefix": true,
    "value-no-vendor-prefix": true,
  },
};

创建 .prettierrc.json

{
  "semi": true,
  "singleQuote": false,
  "trailingComma": "all",
  "printWidth": 100,
  "tabWidth": 2,
  "endOfLine": "lf",
  "arrowParens": "always",
  "bracketSpacing": true,
  "singleAttributePerLine": true,
  "vueIndentScriptAndStyle": false
}

创建 .prettierignore

dist/
node_modules/
pnpm-lock.yaml
.husky/
public/
*.stylelintcache
src/assets

编辑 package.json 添加 lint scripts

{
  "scripts": {
    "lint": "eslint . && stylelint \"src/**/*.{css,scss,vue}\"",
    "lint:fix": "eslint . --fix && stylelint \"src/**/*.{css,scss,vue}\" --fix",
    "format": "prettier --write --ignore-unknown .",
    "format:check": "prettier --check --ignore-unknown ."
  },
  "lint-staged": {
    "**/*.{ts,vue}": ["eslint --fix"],
    "**/*.{css,scss,vue}": ["stylelint --fix"],
    "**/*": ["prettier --write --ignore-unknown"]
  }
}

编辑 tsconfig.node.json

{
  "compilerOptions": {
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
    "target": "es2023",
    "lib": ["ES2023"],
    "types": ["node"],
    "skipLibCheck": true,
    "module": "nodenext",
    "allowImportingTsExtensions": true,
    "verbatimModuleSyntax": true,
    "moduleDetection": "force",
    "noEmit": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "erasableSyntaxOnly": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["vite.config.ts"]
}

4. 编辑器配置

编辑 .gitignore 追加

*.stylelintcache

创建 .vscode/extensions.json

{
  "recommendations": [
    "Vue.volar",
    "dbaeumer.vscode-eslint",
    "esbenp.prettier-vscode",
    "stylelint.vscode-stylelint"
  ]
}

创建 .vscode/settings.json

{
  "editor.cursorSmoothCaretAnimation": "on",
  "editor.cursorBlinking": "smooth",
  "editor.cursorStyle": "line",
  "editor.cursorWidth": 2,

  "editor.renderWhitespace": "boundary",
  "editor.renderLineHighlight": "all",
  "editor.bracketPairColorization.enabled": true,
  "editor.guides.bracketPairs": true,
  "editor.guides.indentation": true,
  "editor.smoothScrolling": true,
  "editor.wordWrap": "off",

  "editor.fontFamily": "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Consolas', 'Courier New', monospace",
  "editor.fontLigatures": true,
  "editor.fontSize": 14,
  "editor.lineHeight": 24,

  "files.trimTrailingWhitespace": true,
  "files.insertFinalNewline": true,
  "files.trimFinalNewlines": true,

  "editor.tabSize": 2,
  "editor.detectIndentation": false,
  "editor.stickyScroll.enabled": true
}
原创

Vue3 工程化脚手架配置(ESLint 等)

本文链接: Vue3 工程化脚手架配置(ESLint 等)

本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

评论交流

文章目录