从零构建react+vite6+ts+prettier+stylelint+husky+Lint-staged+Commitlint(2025版✨)

1,145 阅读11分钟

一、环境准备

  • Node v20.18.0
  • Pnpm v9.12.3
  • Vite v6.0.3
  • VS Code 插件下载安装
    • ESLint
    • Stylelint
    • Prettier - Code formatter

github 代码仓库: github.com/lbbbboom/re…

二、Vite

vite 版本:6.0.3

(一) 使用 Vite 脚手架搭建项目

pnpm create vite react-code-lint --template react-ts
  • 根目录下创建.vscode文件夹

    • extensions.json - 用于放vscode扩展插件
    • settings.json - 用于设置vscode的基本配置(项目配置会覆盖本地)
  • 删除.gitignore里的.vscode/*!.vscode/extensions.json

    • 目的:做到同一项目相同配置,保证风格统一

(二) 添加.vscode 文件夹内容

1. extensions.json

在.vscode 文件夹下创建extensions.json 文件,添加以下代码

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

2. settings.json

在.vscode 文件夹下创建 settings.json 文件,添加以下代码

{
  "editor.codeActionsOnSave": {
    "source.fixAll": "explicit",
    "source.fixAll.eslint": "explicit",
    "source.fixAll.stylelint": "explicit"
  },
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  // 配置stylelint检查的文件类型范围
  "stylelint.validate": ["css", "scss", "sass"],
  "stylelint.enable": true,
  "css.validate": false,
  "scss.validate": false,
  "[scss]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[css]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}

三、 Eslint

版本:9.16.0 ➡️

Tip:v9 版本后使用的 eslint 文件命名为eslint.config.js,文件配置项也与之前有所不同

// ...
"scripts": {
    // ...
    "lint": "eslint .", // 检测eslint
    "lint:fix": "eslint . --fix" // 根据eslint规则修复
}
// ...

尝试输入命令:

  • pnpm lint检查规则
  • pnpm lint:fix修复规则

(一)新增排序插件

  • 安装依赖
pnpm add eslint-plugin-import eslint-import-resolver-alias -D

eslint-plugin-import:此插件旨在支持 ES2015+ (ES6+) 导入/导出语法的 linting,并防止文件路径和导入名称拼写错误的问题。ES2015+ 静态模块语法旨在提供的所有优点均已在编辑器中标记出来。

  • eslint.config.js文件中写入(eslint-plugin-import)
// ...
rules: {
  // ...
    "import/order": [
      "error",
      {
        // 对导入模块进行分组,分组排序规则如下
        groups: [
          "builtin", // 内置模块
          "external", // 外部模块
          "parent", // 父节点依赖
          "sibling", // 兄弟依赖
          "internal", // 内部引用
          "index", // index文件
          "type", //类型文件
          "unknown", // 未知依赖
        ],
        //通过路径自定义分组
        pathGroups: [
          {
            pattern: "@/**", // 把@开头的应用放在external分组后面
            group: "external",
            position: "after", // 定义组的位置,after、before
          },

          {
            pattern: "react*", // 在规定的组中选其一,index、sibling、parent、internal、external、builtin、object、type、unknown
            group: "builtin",
            position: "before",
          },
        ],
        // 是否开启独特组,用于区分自定义规则分组和其他规则分组
        distinctGroup: true,
        // 每个分组之间换行
        "newlines-between": "always",
        // 相同分组排列规则 按字母升序排序
        alphabetize: { order: "asc",caseInsensitive: true },
      },
    ],
},

eslint-import-resolver-alias:这是一个简单的 Node.js 模块导入解析插件eslint-plugin-import,支持原生 Node.js 模块解析、模块别名/映射和自定义文件扩展名。

  • eslint.config.js文件中写入(eslint-import-resolver-alias)
// ...
settings: {
  "import/resolver": {
    // eslint-import-resolver-alias 可以解决绝对路径的问题
    alias: {
      map: [
        ["", "./public"], // <-- this line
        ["@", "./src"], // <-- this line
      ],
      extensions: [".js", ".jsx", ".ts", ".tsx", ".svg"],
    },
  },
},
// ...

四、Prettier

版本:3.4.2 ➡️

Prettier:是一个代码格式化工具,支持多种编程语言。它可以自动格式化代码,使其符合一致的风格标准

  • 安装依赖
pnpm add prettier eslint-config-prettier eslint-plugin-prettier -D
  • 添加.prettierrc.cjs文件,输入以下代码配置
/** .prettierrc.js
 * 在VSCode中安装prettier插件 打开插件配置填写`.prettierrc.js` 将本文件作为其代码格式化规范
 * 在本文件中修改格式化规则,不会同时触发改变ESLint代码检查,所以每次修改本文件需要重启VSCode,ESLint检查才能同步代码格式化
 * 需要相应的代码格式化规范请自行查阅配置,下面为默认项目配置
 */
module.exports = {
  // plugins: ["prettier-plugin-tailwindcss"],
  // 一行最多多少个字符
  printWidth: 90,
  // 指定每个缩进级别的空格数
  tabWidth: 2,
  // 使用制表符而不是空格缩进行
  useTabs: false,
  // 在语句末尾是否需要分号
  semi: true,
  // 是否使用单引号
  singleQuote: false,
  // 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
  quoteProps: "as-needed",
  // 在JSX中使用单引号而不是双引号
  jsxSingleQuote: false,
  // 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
  trailingComma: "es5",
  // 在对象文字中的括号之间打印空格
  bracketSpacing: true,
  // jsx 标签的反尖括号需要换行
  jsxBracketSameLine: false,
  // 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x
  arrowParens: "always",
  // 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
  rangeStart: 0,
  rangeEnd: Infinity,
  // 指定要使用的解析器,不需要写文件开头的 @prettier
  requirePragma: false,
  // 不需要自动在文件开头插入 @prettier
  insertPragma: false,
  // 使用默认的折行标准 always\never\preserve
  proseWrap: "preserve",
  // 指定HTML文件的全局空格敏感度 css\strict\ignore
  htmlWhitespaceSensitivity: "css",
  // Vue文件脚本和样式标签缩进
  vueIndentScriptAndStyle: false,
  //在 windows 操作系统中换行符通常是回车 (CR) 加换行分隔符 (LF),也就是回车换行(CRLF),
  //然而在 Linux 和 Unix 中只使用简单的换行分隔符 (LF)。
  //对应的控制字符为 "\n" (LF) 和 "\r\n"(CRLF)。auto意为保持现有的行尾
  // 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
  endOfLine: "auto",
};

Tip:.prettierrc.cjs修改了相关格式化配置规则,ESLint代码不会立即去检查报红,Ctrl+Shift+P 打开命令面板,输入Restart Extension Host重启扩展宿主,等待加载一会儿,就能同步更新了。也可以暴力直接重启打开vscode软件。

eslint-config-prettier:这个配置是为了让 ESLint 和 Prettier 更好地协作。它关闭了所有可能与 Prettier 冲突的 ESLint 规则。使用这个配置可以确保 Prettier 的格式化规则优先于 ESLint 的风格规则,从而避免两者之间的冲突。

eslint-plugin-prettier:

这个插件将 Prettier 作为一个 ESLint 规则来运行。这样,你可以在 ESLint 的输出中看到 Prettier 的格式化问题。通过这种方式,你可以在使用 ESLint 时同时检查代码的格式化问题,并在开发过程中及时修复。

(一) 添加eslint-plugin-prettier 配置

eslint.config.js文件下添加以下代码,这样就会将prettier 与 eslint 融合在一起了

import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";

export default tseslint.config(
  { ignores: ["dist"] },
  {
    // ...
  }
  eslintPluginPrettierRecommended
);
  • 这时候使用 eslint 修复的时候会读取 prettier 的配置进行修复代码

五、Stylelint

版本:16.11.0 ➡️

  • 安装相关依赖(使用Scss样式预处理器)
pnpm add stylelint stylelint-scss stylelint-config-recommended-scss stylelint-config-standard stylelint-config-standard-scss stylelint-order -D
  1. stylelint(16.11.0):
    Stylelint 是一个强大的现代 CSS 样式检查工具。它可以帮助你检测和修复 CSS、SCSS 和其他样式语言中的错误和不一致。
    它提供了丰富的规则集,可以自定义以满足项目的特定需求。
  2. stylelint-scss(6.10.0):
    这个插件为 Stylelint 提供了对 SCSS 语法的支持。它增加了许多专门用于 SCSS 的规则,帮助你保持 SCSS 代码的质量和一致性。
  3. stylelint-config-recommended-scss(14.1.0):
    这是一个 Stylelint 的配置包,提供了一组推荐的 SCSS 规则。它是一个基础配置,帮助你快速开始使用 Stylelint 检查 SCSS 代码。
  4. stylelint-config-standard(36.0.1):
    这个配置包提供了一组标准的 CSS 规则,适用于大多数项目。它帮助你确保 CSS 代码符合行业标准的最佳实践。
  5. stylelint-config-standard-scss(14.0.0):
    这是 stylelint-config-standard 的扩展,专门为 SCSS 提供标准规则。它结合了标准 CSS 和 SCSS 的最佳实践。
  6. stylelint-order(6.0.4):
    这个插件为 Stylelint 提供了 CSS 属性排序的功能。它允许你定义属性的排序规则,帮助你保持样式代码的可读性和一致性。
  • 在根目录新增文件 .stylelintignore
# .stylelintignore
# 旧的不需打包的样式库
*.min.css
 
# 其他类型文件
*.js
*.jpg
*.png
*.eot
*.ttf
*.woff
*.json
 
# 测试和打包目录
/test/
/dist/
/node_modules/
/lib/
  • 在根目录新增文件 .stylelintrc.cjs
module.exports = {
  extends: [
    'stylelint-config-standard',
    'stylelint-config-standard-scss',
    'stylelint-config-recommended-scss',
  ],
  plugins: ['stylelint-scss', 'stylelint-order'],
  rules: {
    'no-irregular-whitespace': true,
    // 指定样式的排序
    'order/properties-order': [
      [
        'content',
        'position',
        'top',
        'right',
        'bottom',
        'left',
        'z-index',
        'display',
        'vertical-align',
        'flex',
        'flex-grow',
        'flex-shrink',
        'flex-basis',
        'flex-direction',
        'flex-flow',
        'flex-wrap',
        'grid',
        'grid-area',
        'grid-template',
        'grid-template-areas',
        'grid-template-rows',
        'grid-template-columns',
        'grid-row',
        'grid-row-start',
        'grid-row-end',
        'grid-column',
        'grid-column-start',
        'grid-column-end',
        'grid-auto-rows',
        'grid-auto-columns',
        'grid-auto-flow',
        'grid-gap',
        'grid-row-gap',
        'grid-column-gap',
        'gap',
        'row-gap',
        'column-gap',
        'align-content',
        'align-items',
        'align-self',
        'justify-content',
        'justify-items',
        'justify-self',
        'order',
        'float',
        'clear',
        'object-fit',
        'overflow',
        'overflow-x',
        'overflow-y',
        'overflow-scrolling',
        'clip',

        //
        'box-sizing',
        'width',
        'min-width',
        'max-width',
        'height',
        'min-height',
        'max-height',
        'margin',
        'margin-top',
        'margin-right',
        'margin-bottom',
        'margin-left',
        'padding',
        'padding-top',
        'padding-right',
        'padding-bottom',
        'padding-left',
        'border',
        'border-spacing',
        'border-collapse',
        'border-width',
        'border-style',
        'border-color',
        'border-top',
        'border-top-width',
        'border-top-style',
        'border-top-color',
        'border-right',
        'border-right-width',
        'border-right-style',
        'border-right-color',
        'border-bottom',
        'border-bottom-width',
        'border-bottom-style',
        'border-bottom-color',
        'border-left',
        'border-left-width',
        'border-left-style',
        'border-left-color',
        'border-radius',
        'border-top-left-radius',
        'border-top-right-radius',
        'border-bottom-right-radius',
        'border-bottom-left-radius',
        'border-image',
        'border-image-source',
        'border-image-slice',
        'border-image-width',
        'border-image-outset',
        'border-image-repeat',
        'border-top-image',
        'border-right-image',
        'border-bottom-image',
        'border-left-image',
        'border-corner-image',
        'border-top-left-image',
        'border-top-right-image',
        'border-bottom-right-image',
        'border-bottom-left-image',

        //
        'background',
        'background-color',
        'background-image',
        'background-attachment',
        'background-position',
        'background-position-x',
        'background-position-y',
        'background-clip',
        'background-origin',
        'background-size',
        'background-repeat',
        'color',
        'box-decoration-break',
        'box-shadow',
        'outline',
        'outline-width',
        'outline-style',
        'outline-color',
        'outline-offset',
        'table-layout',
        'caption-side',
        'empty-cells',
        'list-style',
        'list-style-position',
        'list-style-type',
        'list-style-image',

        //
        'font',
        'font-weight',
        'font-style',
        'font-variant',
        'font-size-adjust',
        'font-stretch',
        'font-size',
        'font-family',
        'src',
        'line-height',
        'letter-spacing',
        'quotes',
        'counter-increment',
        'counter-reset',
        '-ms-writing-mode',
        'text-align',
        'text-align-last',
        'text-decoration',
        'text-emphasis',
        'text-emphasis-position',
        'text-emphasis-style',
        'text-emphasis-color',
        'text-indent',
        'text-justify',
        'text-outline',
        'text-transform',
        'text-wrap',
        'text-overflow',
        'text-overflow-ellipsis',
        'text-overflow-mode',
        'text-shadow',
        'white-space',
        'word-spacing',
        'word-wrap',
        'word-break',
        'overflow-wrap',
        'tab-size',
        'hyphens',
        'interpolation-mode',

        //
        'opacity',
        'visibility',
        'filter',
        'resize',
        'cursor',
        'pointer-events',
        'user-select',

        //
        'unicode-bidi',
        'direction',
        'columns',
        'column-span',
        'column-width',
        'column-count',
        'column-fill',
        'column-gap',
        'column-rule',
        'column-rule-width',
        'column-rule-style',
        'column-rule-color',
        'break-before',
        'break-inside',
        'break-after',
        'page-break-before',
        'page-break-inside',
        'page-break-after',
        'orphans',
        'widows',
        'zoom',
        'max-zoom',
        'min-zoom',
        'user-zoom',
        'orientation',
        'fill',
        'stroke',

        //
        'transition',
        'transition-delay',
        'transition-timing-function',
        'transition-duration',
        'transition-property',
        'transform',
        'transform-origin',
        'animation',
        'animation-name',
        'animation-duration',
        'animation-play-state',
        'animation-timing-function',
        'animation-delay',
        'animation-iteration-count',
        'animation-direction',
        'animation-fill-mode',
      ],
      {
        unspecified: 'bottom',
        severity: 'error',
      },
    ],
  },
}
  • 打开命令面板重启一下扩展宿主,可以看到App.css文件报红
  • 添加脚本命令行
// ...
  "scripts": {
    // ...
    "stylelint": "stylelint ./**/*.{css,scss}",
    "stylelint:fix": "stylelint ./**/*.{css,scss} --fix",
  },
// ...

六、Husky

版本:9.1.7 ➡️

Husky 是一个用于管理 Git 钩子的工具。它可以帮助你在特定的 Git 操作(如提交、推送等)之前或之后自动运行脚本,从而确保代码质量和一致性

  • 安装依赖
pnpm add husky -D
  • package.json添加脚本命令
// ...
  "scripts": {
    // ...
  "prepare": "husky install"
  },
// ...
  • 生成 husky
pnpm exec husky init
  • 更新修改 pre-commit 文件
pnpm run lint:fix && pnpm run stylelint:fix
  • 执行 Git 提交命令
git add .
git commit -m 'feat: init'
  • 可以看到提交的时候帮我们检查并修复不规范的代码

七、Commitlint

@commitlint/cli(19.6.0):

Commitlint 是一个用于检查 Git 提交信息的工具。@commitlint/cli 是其命令行接口,允许你在命令行中运行 Commitlint。

@commitlint/config-conventional(19.6.0):

这是 Commitlint 的一个配置包,提供了一组基于 Angular 提交信息规范的规则。

cz-git(1.11.0):

cz-git 是一个交互式的提交信息生成工具,基于 Commitizen 适配器。它提供了一个命令行界面,帮助开发者按照预定义的格式生成提交信息。

czg(1.11.0):

czg 是 cz-git 的一个命令行工具,提供了更友好的用户界面和功能,帮助开发者生成符合规范的提交信息。

  • 安装依赖
pnpm add @commitlint/cli @commitlint/config-conventional cz-git czg -D
  • 在 package.json 文件中添加
  "commitizen": {
    "path": "node_modules/cz-git"
  }
  • 项目根目录添加commitlint.config.cjs文件夹
/** @type {import('cz-git').UserConfig} */
module.exports = {
  ignores: [(commit) => commit.includes("init")],
  extends: ["@commitlint/config-conventional"],
  rules: {
    "body-leading-blank": [2, "always"],
    "footer-leading-blank": [1, "always"],
    "header-max-length": [2, "always", 108],
    "subject-empty": [2, "never"],
    "type-empty": [2, "never"],
    "subject-case": [0],
    "type-enum": [
      2,
      "always",
      [
        "feat",
        "fix",
        "docs",
        "style",
        "refactor",
        "perf",
        "test",
        "build",
        "ci",
        "chore",
        "revert",
        "wip",
        "workflow",
        "types",
        "release",
      ],
    ],
  },
  prompt: {
    messages: {
      type: "选择你要提交的类型 :",
      scope: "选择一个提交范围[可选]:",
      customScope: "请输入自定义的提交范围 :",
      subject: "请简要描述提交 :\n",
      body: '填写更加详细的变更描述[可选]。使用 "|" 换行 :\n',
      breaking: '列举非兼容性重大的变更[可选]。使用 "|" 换行 :\n',
      footerPrefixsSelect: "选择关联issue前缀[可选]:",
      customFooterPrefixs: "输入自定义issue前缀 :",
      footer: "列举关联issue [可选] 例如: #31, #I3244 :\n",
      confirmCommit: "是否提交或修改commit ?",
    },
    types: [
      { value: "feat", name: "feat:   ✨  新增功能", emoji: "✨" },
      { value: "fix", name: "fix:   🐛  修复缺陷", emoji: "🐛" },
      { value: "docs", name: "docs:   📝  文档变更", emoji: "📝" },
      {
        value: "style",
        name: "style:   💄  代码格式",
        emoji: "💄",
      },
      {
        value: "refactor",
        name: "refactor:   🔨  代码重构",
        emoji: "🔨",
      },
      { value: "perf", name: "perf:    ⚡️  性能优化", emoji: "⚡️" },
      {
        value: "test",
        name: "test:   ✅  测试",
        emoji: "✅",
      },
      {
        value: "build",
        name: "build:   📦️  打包构建",
        emoji: "📦️",
      },
      { value: "ci", name: "ci:   👷  CI 配置变更", emoji: "👷" },
      { value: "revert", name: "revert:   ⏪️  代码回退", emoji: "⏪️" },
      {
        value: "chore",
        name: "chore:   🚀  构建/工程依赖/工具",
        emoji: "🚀",
      },
      { value: "wip", name: "wip:   🚧  正在开发中", emoji: "🚧" },
      { value: "workflow", name: "workflow:   🎯  工作流程改进", emoji: "🎯" },
    ],
    useEmoji: true,
    scopes: [],
    customScopesAlign: "bottom",
    emptyScopesAlias: "empty",
    customScopesAlias: "custom",
    allowBreakingChanges: ["feat", "fix"],
    skipQuestions: ["scope", "body", "breaking", "footerPrefix", "footer"], // 自定义选择指定的问题不显示
  },
};
  • package.json 中添加脚本
  "scripts": {
    // ...
    "commit": "czg"
  },
  • .husky 中新增 commit-msg 文件
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no -- commitlint --edit $1
  • 输入命令
pnpm commit

八、lint-staged

15.2.10

lint-staged 是一个工具,用于在 Git 暂存文件上运行 linters(代码检查工具)。它的主要作用是提高代码质量和开发效率。以下是 lint-staged 的一些关键功能和作用:

  • 安装依赖
pnpm add lint-staged -D
  • 修改pre-commit文件

npx lint-staged

九、执行

git add .

pnpm commit
  • 完成

十、参考资料