2024 企业级工程化(Vite+Vue3+Eslint+Prettier+Stylelint+Husky)

611 阅读13分钟

前言

在前端行业摸爬滚打多年,兜兜转转只在小公司待过,前段时间有幸当了一次正规军,是做低代码平台的,结果入职不到一个月,被全退了,原因是我的思路有问题,不了解业务,效率太慢。受此打击,想搭建一个企业级别的后台管理系统,所以项目中难免遇到工程化的一些东西,在此写一下心得,也算一个记录了。

这套项目相当于一个模板,可以用于企业级别的多人协作,主要是配置了vite5.x、eslint、prettier、stylelint、husky、commitizen、cz-git。

注:我个人是比较反感typescript的,开发成本太大了,我之前被劝退的那家公司,前端9个人,后端15个人,这种情况下也没有用ts,由此可见,typescript也并不是大项目中必须配置的。

环境

node:18.20.5
yarn:1.22.22

我这里用的是yarn的镜像源,设置yarn镜像源,可以参考这篇文章

ksh7.com/posts/npm-r…

查看本地镜像源
yarn config get registry

国内 淘宝 镜像源
yarn config set registry https://registry.npmmirror.com/

官方镜像源
yarn config set registry https://registry.yarnpkg.com/

第三方库

{ 
  "name": "vite-project",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build:prod": "vite build",
    "build:stage": "vite build --mode staging",
    "preview": "vite preview",
    "stylelint:fix": "stylelint \"src/**/*.{html,vue,css,scss}\" --fix",
    "stylelint:out": "stylelint \"src/**/*.{html,vue,css,scss}\" -o .stylelint-output.txt -f string",
    "prettier": "prettier --check  \"src/**/*.{js,ts,json,css,less,scss,vue,html,md}\"",
    "prettier:fix": "prettier --log-level error --write \"src/**/*.{js,ts,json,css,less,scss,vue,html,md}\"",
    "eslint": "eslint --ext .js,.vue ./src vite.config.js ",
    "eslint:fix": "eslint --ext .js,.vue --fix ./src vite.config.js",
    "eslint:out": "eslint --ext .js,.vue -o .eslint-output.html -f html ./src vite.config.js",
    "eslint:fixout": "eslint --ext .js,.vue --fix -o .eslint-output.html -f html ./src vite.config.js",
    "lint-staged": "lint-staged",
    "cm": "git-cz",
    "commitlint": "commitlint -e -V",
    "prepare": "husky"
  },
  "dependencies": {
    "axios": "^1.7.9",
    "big.js": "^6.2.2",
    "echarts": "^5.5.1",
    "element-plus": "^2.9.1",
    "pinia": "^2.3.0",
    "pinia-plugin-persistedstate": "^4.2.0",
    "vue": "^3.5.13",
    "vue-router": "^4.5.0"
  },
  "devDependencies": {
    "@commitlint/cli": "^19.6.1",
    "@commitlint/config-conventional": "^19.6.0",
    "@vitejs/plugin-vue": "^5.2.1",
    "@vitejs/plugin-vue-jsx": "^4.1.1",
    "commitizen": "^4.3.1",
    "cz-git": "^1.11.0",
    "eslint": "8.57.0",
    "eslint-config-airbnb-base": "^15.0.0",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-import": "^2.31.0",
    "eslint-plugin-prettier": "^5.2.1",
    "eslint-plugin-vue": "^9.32.0",
    "husky": "^9.1.7",
    "prettier": "^3.4.2",
    "sass": "^1.83.0",
    "stylelint": "^16.12.0",
    "stylelint-config-recess-order": "^5.1.1",
    "stylelint-config-recommended-scss": "^14.1.0",
    "stylelint-config-standard": "^36.0.1",
    "stylelint-config-standard-scss": "^14.0.0",
    "stylelint-config-standard-vue": "^1.0.0",
    "unplugin-auto-import": "^0.19.0",
    "unplugin-icons": "^0.22.0",
    "unplugin-vue-components": "^0.28.0",
    "unplugin-vue-setup-extend-plus": "^1.0.1",
    "vite": "^5.3.5",
    "vite-plugin-compression": "^0.5.1",
    "vite-plugin-eslint": "^1.8.1",
    "vite-plugin-svg-icons": "^2.0.1"
  }
}

代码规范和检查工具

  • @commitlint/cli, @commitlint/config-conventional: 提交信息格式检查
  • eslint, eslint-config-airbnb-base, eslint-config-prettier, eslint-plugin-import, eslint-plugin-prettier, eslint-plugin-vue: JavaScript/TypeScript 代码风格检查与错误提示
  • stylelint, stylelint-config-recess-order, stylelint-config-recommended-scss, stylelint-config-standard, stylelint-config-standard-scss, stylelint-config-standard-vue: CSS/SCSS 样式代码检查
  • husky: Git hooks 管理工具,用于在特定 Git 事件触发时执行命令

构建和打包工具

  • @vitejs/plugin-vue, @vitejs/plugin-vue-jsx, vite, vite-plugin-compression, vite-plugin-eslint, vite-plugin-svg-icons: Vite 构建工具及插件,用于提高开发效率和优化生产环境下的资源加载

自动化和增强工具

  • commitizen, cz-git: 结合使用以提供交互式的 Git 提交信息输入界面
  • unplugin-auto-import, unplugin-icons, unplugin-vue-components, unplugin-vue-setup-extend-plus: Vue 项目中自动导入、图标处理、组件按需加载等增强功能 -

样式预处理器

  • sass: SCSS/SASS css预编译器,vite中不再需要使用loader来进行解析,如果是webpack的话,需要sass-loader插件来解析

指南

创建项目
yarn create vite
根据提示配置

安装各种第三方库/插件 ---- dependencies
yarn add axios big.js echarts element-plus pinia pinia-plugin-persistedstate

devDependencies 这里要装的太多了,一行写不下,所以分次安装
yarn add --dev @commitlint/cli @commitlint/config-conventional @vitejs/plugin-vue-jsx 

yarn add --dev commitizen cz-git eslint@8.57.0 eslint-config-airbnb-base eslint-config-prettier eslint-plugin-import eslint-plugin-prettier eslint-plugin-vue 

yarn add --dev husky prettier sass stylelint

yarn add --dev stylelint-config-recess-order stylelint-config-recommended-scss stylelint-config-standard stylelint-config-standard-scss stylelint-config-standard-vue

yarn add --dev unplugin-auto-import unplugin-icons unplugin-vue-components unplugin-vue-setup-extend-plus vite-plugin-compression  vite-plugin-eslint vite-plugin-svg-icons

Eslint配置

eslintrc.cjs 文件

这里用cjs是因为我的package.js中用的是type:modules,主要因为commonjsesmodules的区别问题。

module.exports = {
  root: true,
  env: {
    browser: true,
    node: true,
    es6: true,
  },
  // 指定如何解析语法
  parser: 'vue-eslint-parser',
  // 优先级低于 parse 的语法解析配置
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
    ecmaFeatures: {
      jsx: false,
    },
  },

  // 继承某些已有的规则
  extends: [
    'plugin:vue/vue3-recommended', // 使用插件支持vue3
    'airbnb-base',
    'plugin:prettier/recommended',
    './plugins/modules/.eslintrc-auto-import.json', // 这个是后面设置自动导入的时候用到的
  ],
  /**
   * "off" 或 0  ==>  关闭规则
   * "warn" 或 1 ==>  打开的规则作为警告(不影响代码执行)
   * "error" 或 2 ==>  规则作为一个错误(代码不能执行,界面报错)
   */
  rules: {
    // eslint (http://eslint.cn/docs/rules)
    'prettier/prettier': 'off', // 关闭 prettier 规则,允许使用其他格式化工具
    'import/extensions': 0, // 不要求导入语句中包含文件扩展名
    'prefer-template': 0, // 不强制使用模板字符串代替字符串拼接
    'no-var': 'error', // 要求使用 let 或 const 而不是 var,以避免变量提升等问题
    'no-console': 'off', // 允许使用 console 语句,方便调试
    'no-multiple-empty-lines': ['error', { max: 1 }], // 不允许多个空行,最多允许一个空行
    'prefer-const': 'off', // 不强制使用 const 声明不会被重新赋值的变量
    'no-use-before-define': 'off', // 允许在定义之前使用函数、类或变量
    'no-irregular-whitespace': 'off', // 允许不规则的空白字符,如全角空格等
    'import/no-cycle': 0, // 不检查模块之间的循环依赖
    'no-nested-ternary': 0, // 允许使用嵌套的三元运算符
    'import/prefer-default-export': 0, // 不强制使用默认导出
    'import/no-unresolved': 0, // 不检查导入路径是否正确解析
    'prefer-destructuring': 0, // 不强制使用解构赋值
    'no-shadow': 0, // 允许变量名与外层作用域中的变量名相同
    'no-param-reassign': 0, // 允许修改函数参数的值
    'consistent-return': 0, // 不要求所有 return 语句返回相同的类型
    'no-case-declarations': 0, // 允许在 switch 语句的 case 中声明变量
    'prefer-promise-reject-errors': 0, // 不强制使用 Error 对象作为 Promise 拒绝的原因
    'jsx-a11y/click-events-have-key-events': 0, // 不强制为 JSX 点击事件添加键盘事件处理程序
    'jsx-a11y/no-static-element-interactions': 0, // 允许静态元素(如 div)有交互事件(如 onClick)
    'camelcase': 0, // 不强制使用驼峰命名法
    'import/no-extraneous-dependencies': 0, // 不检查是否存在多余的依赖项
    'no-underscore-dangle': 0, // 允许使用下划线开头的属性名
    'no-debugger': 0, // 允许使用 debugger 语句,方便调试
    'no-promise-executor-return': 0, // 允许 Promise 构造函数的执行器返回值
    'vue/script-setup-uses-vars': 'error', // 防止 <script setup> 使用的变量在 <template> 中被标记为未使用,此规则仅在启用 no-unused-vars 规则时有效
    'vue/v-slot-style': 'error', // 强制执行 v-slot 指令样式,确保一致性
    'vue/no-mutating-props': 'off', // 允许组件内部修改 prop 的值
    'vue/no-v-html': 'off', // 允许使用 v-html 指令插入 HTML 内容
    'vue/custom-event-name-casing': 'off', // 不强制自定义事件名称的大小写风格
    'vue/attributes-order': 'off', // 不强制 Vue 组件属性的顺序
    'vue/one-component-per-file': 'off', // 不强制每个组件必须单独保存在一个文件中
    'vue/html-closing-bracket-newline': 'off', // 不要求标签右括号前换行
    'vue/max-attributes-per-line': 'off', // 不限制每行的最大属性数
    'vue/multiline-html-element-content-newline': 'off', // 不要求多行元素内容前后换行
    'vue/singleline-html-element-content-newline': 'off', // 不要求单行元素内容前后换行
    'vue/attribute-hyphenation': 'off', // 不强制模板中自定义组件属性的命名风格
    'vue/require-default-prop': 'off', // 不要求必填的 prop 必须提供默认值
    'vue/multi-word-component-names': 'off', // 不要求组件名称必须是多个单词用连字符连接
    'vue.prefer-template': 'off', // 不强制使用模板字符串代替字符串拼接
    'vuejs-accessibility/click-events-have-key-events': 'off', // 不强制为点击事件添加键盘事件处理程序
  },
};

Prettier配置

.prettierrc.json

{
  "printWidth": 100, // 每行代码的最大长度
  "tabWidth": 2, // 缩进的空格数
  "useTabs": false, // 使用空格而不是制表符
  "semi": true, // 语句末尾添加分号
  "singleQuote": true, // 使用单引号而不是双引号
  "quoteProps": "as-needed", // 对象属性名仅在必要时使用引号
  "jsxSingleQuote": false, // JSX中使用双引号而不是单引号
  "trailingComma": "es5", // 在对象、数组等的最后一个元素后添加逗号
  "bracketSpacing": true, // 在对象字面量的括号之间添加空格
  "jsxBracketSameLine": false, // JSX的多行元素的右括号放置在最后一行的末尾
  "bracketSameLine": false, // 对于HTML、JSX等,右括号是否放在最后一行的末尾
  "arrowParens": "avoid", // 箭头函数的参数周围省略括号(仅当只有一个参数时)
  "endOfLine": "auto", // 根据系统自动检测换行符
  "embeddedLanguageFormatting": "auto", // 自动格式化嵌入的语言
  "vueIndentScriptAndStyle": false, // 在Vue文件中,script和style标签内的内容不缩进
  "singleAttributePerLine": false, // 每个属性占一行
  "htmlWhitespaceSensitivity": "css", // 根据CSS的规则处理HTML中的空白
  "requirePragma": false, // 需要在文件头部添加特定的注释才能格式化
  "insertPragma": false, // 在文件头部插入特定的注释
  "proseWrap": "preserve" // 保留Markdown等格式文本中的换行符
}

Stylelint配置

.stylelintrc.cjs

// @see https://stylelint.bootcss.com/
module.exports = {
  extends: [
    'stylelint-config-standard', // 配置stylelint拓展插件
    'stylelint-config-html/vue', // 配置 vue 中 template 样式格式化
    'stylelint-config-standard-scss', // 配置stylelint scss插件
    'stylelint-config-recommended-vue/scss', // 配置 vue 中 scss 样式格式化
    'stylelint-config-recess-order', // 配置stylelint css属性书写顺序插件,
    // 'stylelint-config-prettier', // 配置stylelint和prettier兼容, 注意stylelint大于等于v15是已经不需要此插件
  ],
  overrides: [
    {
      files: ['**/*.(scss|css|vue|html)'],
      customSyntax: 'postcss-scss',
    },
    {
      files: ['**/*.(html|vue)'],
      customSyntax: 'postcss-html',
    },
  ],
  ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts', '**/*.json', '**/*.md', '**/*.yaml'],
  /**
   * null  => 关闭该规则
   * always => 必须
   */
  rules: {
    'font-family-no-missing-generic-family-keyword': null,
    'value-keyword-case': null, // 在 css 中使用 v-bind,不报错
    'no-descending-specificity': null, // 禁止在具有较高优先级的选择器后出现被其覆盖的较低优先级的选择器
    'function-url-quotes': 'always', // 要求或禁止 URL 的引号 "always(必须加上引号)"|"never(没有引号)"
    'no-empty-source': null, // 关闭禁止空源码
    'selector-class-pattern': null, // 关闭强制选择器类名的格式
    'property-no-unknown': null, // 禁止未知的属性(true 为不允许)
    'block-opening-brace-space-before': 'always', //大括号之前必须有一个空格或不能有空白符
    'value-no-vendor-prefix': null, // 关闭 属性值前缀 --webkit-box
    'property-no-vendor-prefix': null, // 关闭 属性前缀 -webkit-mask
    'scss/dollar-variable-pattern': null,
    'scss/at-mixin-pattern': null,
    'selector-pseudo-class-no-unknown': [
      // 不允许未知的选择器
      true,
      {
        ignorePseudoClasses: ['global', 'v-deep', 'deep'], // 忽略属性,修改element默认样式的时候能使用到
      },
    ],
  },
};

Husky、Lintstage配置

其实在下载插件的时候会直接在你的项目当中生成一个.husky文件夹,这里面都是一些脚本的东西,不需要额外的配置

下面是lintstage的配置

{
  "src/**/*.{vue,js}": ["npm run prettier", "npm run eslint"],
  "src/**/*.{vue,scss,css,less}": ["npm run stylelint:out"]
}

Commitlint 代码提交规范

.commitlintrc.cjs

module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'body-leading-blank': [1, 'always'],
    'body-max-line-length': [2, 'always', 100],
    'footer-leading-blank': [1, 'always'],
    'footer-max-line-length': [1, 'always', 100],
    'header-max-length': [2, 'always', 100],
    'subject-case': [2, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']],
    'subject-empty': [2, 'never'],
    'subject-full-stop': [2, 'never', '.'],
    'type-case': [2, 'always', 'lower-case'],
    'type-empty': [2, 'never'],
    'type-enum': [
      2,
      'always',
      [
        'build',
        'chore',
        'ci',
        'docs',
        'feat',
        'fix',
        'perf',
        'refactor',
        'revert',
        'style',
        'test',
        'conflict',
        'fonts',
        'delete',
        'stash',
      ],
    ],
  },
};

.czrc

{
  "path": "cz-git",
  "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": "feat:     ✨  新增功能",
      "emoji": ":sparkles:"
    },
    {
      "value": "fix",
      "name": "fix:      🐛  修复缺陷",
      "emoji": ":bug:"
    },
    {
      "value": "docs",
      "name": "docs:     📚  文档变更",
      "emoji": ":books:"
    },
    {
      "value": "style",
      "name": "style:    💄  代码格式(不影响功能,例如空格、分号等格式修正)",
      "emoji": ":lipstick:"
    },
    {
      "value": "refactor",
      "name": "refactor: 📦  代码重构(不包括 bug 修复、功能新增)",
      "emoji": ":package:"
    },
    {
      "value": "perf",
      "name": "perf:     🚀  性能优化",
      "emoji": ":rocket:"
    },
    {
      "value": "conflict",
      "name": "conflict: 🧰  修改冲突",
      "emoji": ":rewind:"
    },
    {
      "value": "test",
      "name": "test:     ✅  添加疏漏测试或已有测试改动",
      "emoji": ":white_check_mark:"
    },
    {
      "value": "build",
      "name": "build:    🔧  构建流程、外部依赖变更(如升级 npm 包、修改 vite 配置等)",
      "emoji": ":wrench:"
    },
    {
      "value": "ci",
      "name": "ci:       🎡  修改 CI 配置、脚本",
      "emoji": ":ferris_wheel:"
    },
    {
      "value": "revert",
      "name": "revert:   ⏪️  回滚 commit",
      "emoji": ":rewind:"
    },
    {
      "value": "chore",
      "name": "chore:    🔨  对构建过程或辅助工具和库的更改(不影响源文件、测试用例)",
      "emoji": ":hammer:"
    },
    {
      "value": "fonts",
      "name": "fonts:    🔠  字体文件更新",
      "emoji": ":capital_abcd:"
    },
    {
      "value": "delete",
      "name": "delete:   🆑  删除文件",
      "emoji": ":cl:"
    }
  ],
  "useEmoji": true,
  "emojiAlign": "center",
  "useAI": false,
  "aiNumber": 1,
  "themeColorCode": "",
  "scopes": [],
  "allowCustomScopes": true,
  "allowEmptyScopes": true,
  "customScopesAlign": "bottom",
  "customScopesAlias": "custom",
  "emptyScopesAlias": "empty",
  "upperCaseSubject": false,
  "markBreakingChangeMode": false,
  "allowBreakingChanges": [
    "feat",
    "fix"
  ],
  "breaklineNumber": 100,
  "breaklineChar": "|",
  "skipQuestions": [],
  "issuePrefixes": [
    {
      "value": "closed",
      "name": "closed:   ISSUES has been processed"
    }
  ],
  "customIssuePrefixAlign": "top",
  "emptyIssuePrefixAlias": "skip",
  "customIssuePrefixAlias": "custom",
  "allowCustomIssuePrefix": true,
  "allowEmptyIssuePrefix": true,
  "confirmColorize": true,
  "minSubjectLength": 0,
  "defaultBody": "",
  "defaultIssues": "",
  "defaultScope": "",
  "defaultSubject": ""
}

忽略文件

项目中不是所有文件都需要 Eslint、Prettier、Stylelint 的,所以要排除一些文件,这时候在根目录下分别创建.prettierignore .stylelintignore .eslintignore

把下面这些东西粘贴进去即可
dist
build
node_modules
public
.husky
.bin
docs
html
.vscode
.idea
*.sh
*.md

到此其实已经差不多了,

但是工程化还有一个最大的作用,就是减少开发者的重复工作,减轻开发者的负担,提高效率,所以我还配置了一些自动导入的功能,比如 vue 3 的组件自动导入,css 预处理器自动导入,等等,

我这里是另起了一个文件,然后将这些自动导入的东西给抽离出来了。在根目录下创建了一个 plugins 文件。目录结构如下

plugins

  • modules .eslintrc-auto-import.json auto-import.js compression.js element-plus.js eslint-plugin.js setup-extend.js svg-icon.js
  • index.js

plugins/modules/auto-import.js

import autoImport from 'unplugin-auto-import/vite';

export default function createAutoImport(path) {
  return autoImport({
    imports: ['vue', 'vue-router', 'pinia', '@vueuse/core'],
    eslintrc: {
      enabled: true, // Default `false`
      // 这里就和上面的那个 .eslintrc.cjs 文件中的 './plugins/modules/.eslintrc-auto-import.json' 形成了对应。
      filepath: path.resolve(__dirname, './.eslintrc-auto-import.json'), // Default `./.eslintrc-auto-import.json`
      globalsPropValue: true, // Default `true`, (true | false | 'readonly' | 'readable' | 'writable' | 'writeable')
    },
    dts: 'types/auto-imports.d.ts',
  });
}

plugins/modules/compression.js

import compression from 'vite-plugin-compression';

export default function createCompression(env) {
  const { VITE_BUILD_COMPRESS } = env;
  const plugin = [];
  if (VITE_BUILD_COMPRESS) {
    const compressList = VITE_BUILD_COMPRESS.split(',');
    if (compressList.includes('gzip')) {
      plugin.push(
        compression({
          ext: '.gz',
          deleteOriginFile: false,
        })
      );
    }
    if (compressList.includes('brotli')) {
      plugin.push(
        compression({
          ext: '.br',
          algorithm: 'brotliCompress',
          deleteOriginFile: false,
        })
      );
    }
  }
  return plugin;
}

plugins/modules/element-plus.js

import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
import Icons from 'unplugin-icons/vite';
import IconsResolver from 'unplugin-icons/resolver';

export default function createElementPlusPlugin() {
  return [
    AutoImport({
      resolvers: [ElementPlusResolver(), IconsResolver({})],
      dts: 'types/auto-imports.d.ts',
    }),
    Components({
      resolvers: [
        ElementPlusResolver(),
        IconsResolver({ enabledCollections: ['ep'] }),
      ],
      dirs: ['src/components', 'src/**/components'],
      dts: 'types/components.d.ts',
    }),
    Icons({
      autoInstall: true,
    }),
  ];
}

plugins/modules/eslint-plugin.js

import eslintPlugin from 'vite-plugin-eslint';

export default function createEsLintPlugin() {
  return eslintPlugin({
    include: ['src/**/*.js', 'src/**/*.vue', 'src/*.js', 'src/*.vue'],
  });
}

plugins/modules/setup-extend.js

// 用于 setup 语法糖增强
import setupExtend from 'unplugin-vue-setup-extend-plus/vite';

export default function createSetupExtend() {
  return setupExtend({});
}

plugins/modules/svg-icon.js

import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
import path from 'path';

export default function createSvgIcon(isBuild) {
  return createSvgIconsPlugin({
    // 指定需要缓存的图标文件夹
    iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
    symbolId: 'icon-[dir]-[name]',
    svgoOptions: isBuild,
  });
}

plugins/index.js

import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';

import createAutoImport from './modules/auto-import';
import createSvgIcon from './modules/svg-icon';
import createCompression from './modules/compression';
import createSetupExtend from './modules/setup-extend';
import createElementPlusPlugin from './modules/element-plus';
import createEsLintPlugin from './modules/eslint-plugin';

export default function createVitePlugins(viteEnv, path, isBuild = false) {
  const vitePlugins = [vue(), vueJsx()];
  vitePlugins.push(createSetupExtend());
  vitePlugins.push(createElementPlusPlugin());
  vitePlugins.push(createSvgIcon(isBuild));
  vitePlugins.push(createEsLintPlugin());
  vitePlugins.push(createAutoImport(path));
  if (isBuild) {
    vitePlugins.push(...createCompression(viteEnv));
  }
  return vitePlugins;
}

最后在 vite.config.js 中引入

import { defineConfig, loadEnv } from 'vite';
import * as path from 'path';
import createVitePlugins from './plugins';

// https://vite.dev/config/
export default defineConfig(({ mode, command }) => {
  const env = loadEnv(mode, process.cwd());
  const { VITE_APP_ENV } = env;

  return {
    base: VITE_APP_ENV === 'production' ? '/vue3-admin-template/' : '/',
    // 打包的配置,超过1500k会单独打包成文件。
    build: {
      chunkSizeWarningLimit: 1500,
      rollupOptions: {
        output: {
          manualChunks(id) {
            if (id.includes('node_modules')) {
              return id.toString().split('node_modules/')[1].split('/')[0].toString();
            }
          },
          chunkFileNames: chunkInfo => {
            const facadeModuleId = chunkInfo.facadeModuleId
              ? chunkInfo.facadeModuleId.split('/')
              : [];
            const fileName = facadeModuleId[facadeModuleId.length - 2] || '[name]';
            return `js/${fileName}/[name].[hash].js`;
          },
        },
      },
    },
    resolve: {
      alias: {
        '@': path.resolve(__dirname, './src'),
        '@c': path.resolve(__dirname, './src/components'),
        '@v': path.resolve(__dirname, './src/views'),
      },
      extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue', '.scss'],
    },
    server: {
      port: 5713,
      host: true,
      open: true,
      proxy: {
        '/dev-api': {
          target: 'http://localhost:8080',
          changeOrigin: true,
          rewrite: p => p.replace(/^\/dev-api/, ''),
        },
      },
    },
    plugins: createVitePlugins(env, path, command === 'build'),
    css: {
      preprocessorOptions: {
        scss: {
          javascriptEnabled: true,
          additionalData: `@use "@/assets/styles/modules/variables.scss" as *;`,
        },
      },
      postcss: {
        plugins: [
          {
            postcssPlugin: 'internal:charset-removal',
            AtRule: {
              charset(atRule) {
                if (atRule.name === 'charset') atRule.remove();
              },
            },
          },
        ],
      },
    },
  };
});

结语

当前模板已经基本满足我当前的需求,如果有什么问题,可以参考对应的官方文档。各位大佬,这篇文章纯粹是个人的笔记,如果正好解决了你的问题,请点个赞,谢谢!

我的模板已经上传到gitee上面了,大家可以clone下来参考。 Template-Vue3