侧边栏壁纸
博主头像
shunlin的小站 博主等级

劳动光荣,偷懒聪明

  • 累计撰写 2 篇文章
  • 累计创建 5 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

基于Vite搭建一个React+ts前端工程

shunlin
2025-01-31 / 0 评论 / 0 点赞 / 28 阅读 / 0 字

前言

搭建一次前端工程可以用好久,但是每次搭建前端工程都要找各种资料从头开始,特别费力气,所以借着本次又搭建了一次工程的契机把自己的各种操作做个记录。

初始化

npm create vite 创建新项目,选择react+ts模板即可。

Eslint配置

eslint.config.js整体配置如下:

import js from '@eslint/js';
import globals from 'globals';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import tseslint from 'typescript-eslint';
import sonarjs from 'eslint-plugin-sonarjs';
import ImportPlugin from 'eslint-plugin-import';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import react from 'eslint-plugin-react';
import unicorn from 'eslint-plugin-unicorn';

export default tseslint.config(
  { ignores: ['dist'] },
  {
    extends: [js.configs.recommended, ...tseslint.configs.recommended],
    files: ['**/*.{ts,tsx}'],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser
    },
    plugins: {
      'react-hooks': reactHooks,
      'react-refresh': reactRefresh,
      import: ImportPlugin,
      react,
      sonarjs,
      unicorn
    },
    rules: {
      ...reactHooks.configs.recommended.rules,
      'react-refresh/only-export-components': [
        'warn',
        { allowConstantExport: true }
      ],
      'react/self-closing-comp': [
        'error',
        {
          component: true,
          html: true
        }
      ],
      '@typescript-eslint/no-unused-vars': [
        'error',
        {
          args: 'all',
          argsIgnorePattern: '^_',
          caughtErrors: 'all',
          caughtErrorsIgnorePattern: '^_',
          destructuredArrayIgnorePattern: '^_',
          varsIgnorePattern: '^_',
          ignoreRestSiblings: true
        }
      ],
      // 默认文件名规则为小驼峰
      'unicorn/filename-case': [
        'error',
        {
          cases: {
            camelCase: true,
            pascalCase: true
          },
          ignore: ['vite-env.d.ts']
        }
      ],
      'sonarjs/no-identical-functions': 'error',
      'max-params': ['error', 5], // 规定函数的参数数量不能超过5个
      'max-depth': ['error', 3], // 限制嵌套深度为 3
      complexity: ['error', { max: 5 }], // 限制函数的复杂度为 5
      'max-lines': ['error', { max: 800 }], // 规定文件的行数不能超过800行
      'max-len': [
        'error',
        {
          code: 300, // 规定一行代码的长度不能超过300个字符
          ignoreComments: true, // 忽略注释的长度
          ignoreUrls: true // 忽略URL的长度
        }
      ],
      // 分号
      semi: ['error', 'always'],
      '@typescript-eslint/naming-convention': [
        'error',
        // interface 大驼峰
        {
          selector: 'interface',
          format: ['PascalCase']
        },
        // class 大驼峰
        {
          selector: 'class',
          format: ['PascalCase']
        },
        // 枚举大驼峰
        {
          selector: 'enum',
          format: ['PascalCase']
        },
        // 枚举值命名使用大写+下划线
        {
          selector: 'enumMember',
          format: ['UPPER_CASE']
        },
        // 变量允许大小驼峰和纯大写
        {
          selector: 'variable',
          format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
          leadingUnderscore: 'allow' // 允许前导下划线
        },
        // 函数允许大小驼峰
        {
          selector: 'function',
          format: ['camelCase', 'PascalCase']
        },
        // 允许参数名使用小驼峰
        {
          selector: 'parameter',
          format: ['camelCase', 'PascalCase'],
          leadingUnderscore: 'allow' // 允许前导下划线
        },
        // 允许类成员变量使用小驼峰和全大写+下划线
        {
          selector: 'classProperty',
          format: ['camelCase', 'UPPER_CASE', 'PascalCase']
        }
      ],
      'no-magic-numbers': [
        'warn', // 仅提示警告
        {
          ignore: [0, 1, -1], // 忽略常见数值
          ignoreArrayIndexes: true, // 忽略数组索引
          enforceConst: true, // 强制要求将魔法值定义为常量
          detectObjects: false, // 不检查对象属性中的数字
          ignoreDefaultValues: true, // 忽略函数参数默认值中的魔法值
          ignoreClassFieldInitialValues: true // 忽略class里的初始值
        }
      ],
      // 导入模块的顺序
      'import/order': [
        'error',
        {
          // 对导入模块进行分组,分组排序规则如下
          groups: [
            'builtin', // 内置模块
            'external', // 外部模块
            'parent', // 父节点依赖
            'sibling', // 兄弟依赖
            'internal', // 内部引用
            'index', // index文件
            'type', //类型文件
            'unknown' // 未知依赖
          ],
          // 是否开启独特组,用于区分自定义规则分组和其他规则分组
          distinctGroup: true,
          // 每个分组之间换行
          'newlines-between': 'always',
          // 相同分组排列规则 按字母升序排序
          alphabetize: { order: 'asc', caseInsensitive: true }
        }
      ]
    }
  },
  eslintPluginPrettierRecommended
);

要点说明:

  1. 使用sonarjs插件来控制功能相同的函数

  2. 使用了import插件的order规则,配置导入顺序

  3. @typescript-eslint/no-unused-vars限制未使用的变量,放行_符号

  4. react/self-closing-comp自闭和插件,用于使用eslint --fix的时候能够自动修复标签闭合

  5. @typescript-eslint/naming-convention配置了一些书写习惯,具体的配置可以参考注释

  6. no-magic-numbers限制魔法值的使用

  7. eslintPluginPrettierRecommended,整合prettier与eslint。使用格式化修复的时候直接使用eslint --fix修复即可,这样可以防止与prettier冲突

添加工程指令

    "lint": "eslint .",
    "lint:fix": "eslint --fix .",

Prettier

.prettierrc.cjs中配置prettier

配置如下:

module.exports = {
  printWidth: 80, // 定义每行代码的最大长度为80个字符,有助于提高代码的可读性
  tabWidth: 2, // 设置缩进为2个空格,使代码结构清晰
  semi: true, // 要求在语句末尾添加分号,避免JavaScript的自动分号插入(ASI)可能导致的问题
  singleQuote: true, // 使用单引号,使代码风格更统一
  trailingComma: 'none', // 在多行结构(如数组和对象)的最后一个元素后面添加逗号,有助于版本控制系统的diff更清晰
  bracketSpacing: true, // 在对象的括号之间添加空格,提高代码的可读性
  jsxBracketSameLine: true, // 将多行JSX元素的关闭标签放在最后一行的末尾,使代码风格更统一
  proseWrap: 'preserve', // 当超出print width时,不对markdown文本进行换行,使得markdown文本的格式更一致
  arrowParens: 'always', // 在单参数箭头函数中始终包含括号,避免添加或删除参数时需要修改箭头函数的问题
  closeEmptyJsxElements: true, // 自动关闭空的JSX元素,使代码看起来更整洁
  closeEmptyJsxTags: true, // 自动关闭空的JSX标签,使代码看起来更整洁
  jsxSelfClosing: true, // 自动关闭JSX标签,使代码看起来更整洁
  endOfLine: 'lf' // 使用LF作为换行符,避免不同操作系统下换行符不一致的问题
};

EditorConfig

.editorconfig是对编辑器的配置,而prettier是对代码格式的限制。在

# EditorConfig 文件示例
# 表示这是项目根目录下的顶级 .editorconfig 文件,编辑器在查找配置时会停止向上查找
root = true
# 匹配所有文件
[*]
# 使用 UTF-8 编码
charset = utf-8
# 使用 Unix 风格的换行符
end_of_line = lf
# # 文件末尾会插入一个空行
# insert_final_newline = true
# 使用空格缩进符
indent_style = space
# 缩进大小为 2
indent_size = 2

Commitlint

commitlint.config.js配置commit message限制。

提交的message需要满足<type>(<scope>): <subject>

例如:

feat(login): add password reset functionality
fix(api): handle null response in getUser endpoint
docs(readme): update installation instructions
chore: update eslint configuration
export default {
  extends: ['@commitlint/config-conventional'],
  rules: {
    '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',
        'types',
        'release'
      ]
    ]
  }
};

StyleLint

配置对于样式文件的限制。

安装stylelint和相关插件

pnpm add stylelint stylelint-config-recommended-less stylelint-config-standard stylelint-less stylelint-order stylelint-prettier stylelint-selector-bem-pattern

.stylelintrc.cjs中配置

module.exports = {
  root: true,
  /* 继承其他规则, 用来扩展配置 */
  extends: [
    'stylelint-config-standard',
    'stylelint-config-recommended-less',
    'stylelint-prettier/recommended'
  ],
  /* 排序和bem规则插件 */
  plugins: [
    'stylelint-order',
    'stylelint-selector-bem-pattern',
    'stylelint-prettier'
  ],
  /* 忽略文件 */
  ignoreFiles: [
    '**/*.js',
    '**/*.jsx',
    '**/*.tsx',
    '**/*.t
  1. 启用了bem规则,并且配置了命名遵守bem规则

  2. 配置了样式的排序顺序

添加工程指令

"stylelint": "stylelint 'src/**/*.{css,less}'",
"stylelint:fix": "stylelint --fix 'src/**/*.{css,less}'",

Husky配置

  1. 安装husky

pnpm add husky -D

  1. 添加npm的生命周期脚本

prepare会在会在pnpm install前运行,husky会往.git/hooks目录下面添加相关的钩子

// ...
  "scripts": {
    // ...
  "prepare": "husky"
  },
// ...

  1. 初始化husky

pnpm exec husky init

LintStaged

安装lint-staged

pnpm add lint-staged -D

这是一个用于检查git暂存区文件的工具,防止每次lint时扫描所有工程文件,提高效率。

在husky生成的.husky目录下的pre-commit写入pnpm run lint-staged --allow-empty

接着到package.json配置lint-stage的操作, 对于代码片段我们使用eslint修复它们,对于样式文件我们使用stylelint来修复:

{
  // ...
  "lint-staged": {
    "*.{js,ts,jsx,tsx}": [
      "eslint --fix"
    ],
    "*.{less,css}": [
      "stylelint --fix --allow-empty-input"
    ]
  //...
  },
}

.vscode目录配置

在工程目录下新建.vscode目录

  1. 新建extensions.json,写入这个项目建议使用的插件

{
  "recommendations": [
    "dbaeumer.vscode-eslint",  //eslint  
    "esbenp.prettier-vscode",  // prettier
    "stylelint.vscode-stylelint", // stylelint
    "editorconfig.editorconfig",  // editorconfig
    "christian-kohler.path-intellisense",  // 文件路径智能提示
    "kushagra-aa.wrap-it",  // 标签快速包裹
    "aaron-bond.better-comments",  // 更好的代码注释插件
    "streetsidesoftware.code-spell-checker"  // 拼写检查
  ]
}
  1. 新建settings.json写入与当前工程相关的vscode配置

{
  "editor.codeActionsOnSave": {  //保存时的操作
    "source.fixAll.eslint": "always",  // 保存时总是修复eslint
    "source.fixAll.stylelint": "explicit"  // 显式触发修复,也就是手动触发修复
  },
  "editor.formatOnSave": true,  // 保存时格式化代码
  "editor.defaultFormatter": "esbenp.prettier-vscode",  // 默认的格式化工具
  "stylelint.enable": true,  // 启动stylelint插件
  "stylelint.validate": ["css", "less", "html"]  // stylelint检查的文件类型
}

  1. 还可以再.vscode里写入一些像目录常用的代码片段,比如新建一个typescript.code-snippets,我们添加一个新增zuztand store的代码片段:

// typescript 代码片段
{
  "Create Zustand Store": {
    "prefix": "zus",
    "body": [
      "import { create } from 'zustand';",
      "",
      "interface ${TM_FILENAME_BASE/^(use)(.*)/$2/}State {",
      "  count: number;",
      "  setCount: (count: number) => void;",
      "}",
      "",
      "const use${TM_FILENAME_BASE/^(use)(.*)/$2/}Store = create<${TM_FILENAME_BASE/^(use)(.*)/$2/}State>((set) => ({",
      "  count: 0,",
      "  setCount: (count) => set({ count })",
      "}));",
      "",
      "export default use${TM_FILENAME_BASE/^(use)(.*)/$2/}Store;",
      ""
    ],
    "description": "创建一个 Zustand Store 模板"
  }
}

附录

参考文档:

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

0

评论区