vite6+vue3+ts+prettier+husky9+lint-staged+eslint9配置前端项目(后台管理系统、移动端H5项目通用配置)

很多小伙伴苦于无法搭建一个规范的前端项目,导致后续开发不规范,今天给大家带来一个基于Vite6+TypeScript+Vue3+ESlint9+Prettier的搭建教程。

环境依赖版本

  • node:v20.17.0
  • vite:^6.0.5
  • typescript:~5.8.0
  • vue:^3.5.13
  • eslint: ^9.22.0,
  • vite-plugin-checker: ^0.9.1
  • husky: ^9.1.7
  • lint-staged: ^15.5.1

一、基础配置

最新版vue官方已放弃webpack作为构建工具,vue官方之前一直是以webpack,但是近期我发现vue官网已经更新了相关内容,目前开始主推vite作为脚手架构建的工具,使用官方推荐的脚手架会更加方便,脚手架可自行选择ts、pinia 、router等相关配置,不用再像之前一样从头到尾进行安装,简单又方便!!!
官网地址 :https://cn.vuejs.org/guide/quick-start.html

1、初始化项目

注意:此处使用node版本需要>18.3

此处建议用pnpm,因为它可以很好的解决幽灵依赖的问题,什么是幽灵依赖部分本文重点,不做过多解释,可以自行百度或者参考这篇文章,运行以下命令:

pnpm create vue@latest

运行指令后接下来就是根据需要安装所需的功能
img
初始化完成的结构如图所示
img

2、代码质量风格的统一

eslint可以保证项目的质量,prettier可以保证项目的统一格式、风格。
每个公司的开发规则各有不同,此处根据各自的需求自行配置,下方是我常用的风格配置(仅供参考)

2.1、配置eslint

利用脚手架搭建好的项目vue官方已经安装配置了很多实用插件,没有特殊需求没必要再安装其他多余的插件

  • 注意

最新版本的配置已经不是 .eslintrc.cjs 文件了!!!旧版的内容不能直接复制到新版!!!

1、自 ESLint v9.0.0 以后,平面配置文件格式就是默认的配置文件格式。

2、默认情况下,ESLint CLI 将搜索eslint.config.(js | cjs | mjs)而不是 .eslintrc.* 文件。如果未找到 eslint.config.* 文件,CLI 会将其视为错误,并且不会运行。 https://eslint.org.cn/docs/latest/use/configure/configuration-files

3、如果一定要用 .eslintrc.cjs,那就看文档自个配置→https://eslint.org.cn/docs/latest/use/configure/configuration-files-deprecated#configuration-file-formats

以下是官方给出的具体解释:https://eslint.org/docs/latest/use/configure/migration-guide

可参考以下文章:
探索 Antfu ESLint 配置:一款极为便捷的 ESLint 设置方案
ESLint 扁平化配置使用指南

2.1.1 eslint9拓展(熟悉的略过)
拓展一:扁平化配置
export default [
    { 配置对象 },
    扩展插件1,
    { 扩展插件1的配置对象 },
    扩展插件2,
    { 扩展插件2的配置对象 },
  	......
]
  • 重要配置属性

files 指定配置对象应用到的文件,如果当前对象未指定,则配置对象内容应用于全局所有文件。

**ignores:**指示配置对象不应用到的文件。如果未指定,则配置对象应用于 files 匹配的所有文件。如果 ignores 在配置对象中没有任何其他属性一起使用,则这些模式充当全局忽略项

plugins 一个插件对象,当指定 files 时,这些插件仅对匹配的文件可用,不指定则对全局的 files 生效, 定义了可用的规则,但启用它们需要在 rules 中明确启用和配置每条规则

rules 一个对象,包含已配置的规则。当指定 filesignores 时,这些规则配置仅对匹配的文件可用,不指定则对全局的 files 生效。

拓展二:plugins和extends(官方没有解释)

我分别创建了react和vue两个项目,得到了以下配置信息

  • vue配置文件

image-20250507155235579

  • react配置文件

image-20250507155235579

我发现这两个项目中关于plugins的配置不太一样,然后询问了ai,得到以下解释

  • 预设配置 (vue当前使用的方式):

    • 是最高级别的抽象
    • 通常包含了插件、规则和其他配置的组合,不需要手动配置每条规则,配置对象会被合并到最终的配置中
    • 简洁明了,直接使用官方或社区维护的预设配置
  • plugins 配置 :

    • 是中间级别的抽象
    • 定义了可用的规则,但不启用它们
    • 需要在 rules 中明确启用和配置每条规则
  • extends (旧版配置系统):

    • 在旧版配置中用于继承其他配置,虽然官方基础示例中可能没有直接展示 extends 参数,但这是扁平化配置中完全有效的一个属性
    • 在新版扁平化配置中,通过直接传递配置对象来实现类似功能,也就是预设配置

所以根据以上解释可以得出,假设不考虑file的显示,插件可作用于全局的情况下,react中的extends可做如下更改

image-20250507160233066

拓展三:ESlint合并的逻辑

扁平化结构的配置顺序很重要,首先ESlint会在数组中对象依次执行,对每个配置对象都会先看 filesignores 是否与目标执行文件路径匹配,匹配上的配置对象,会从前往后依次合并,同属性的后面覆盖前面。可参考配置文件 - 级联配置对象配置文件 - 配置规则配置文件 - 组合配置

我的理解是,类似于 Object.assign() 的合并规则,后面的相同的对象属性会覆盖前面的对象属性

2.1.2 修改eslint.config.ts配置信息

再原有配置的基础上新增规则配置

  • 规则
  • “off” 或 0 - 关闭规则
  • “warn” 或 1 - 打开规则作为警告(不影响退出代码)
  • “error” 或 2 - 打开规则作为错误(触发时退出代码为 1)
//用于全局忽略某些文件或目录,使它们不受 ESLint 检查
import { globalIgnores } from 'eslint/config';
// 负责 Vue 单文件组件中的 TypeScript 检查
import {
  defineConfigWithVueTs,
  vueTsConfigs,
} from '@vue/eslint-config-typescript';
import pluginVue from 'eslint-plugin-vue';
// 这是 Vue 官方提供的一个配置,专门用于 Vue 项目,跳过 ESLint 中与格式化相关的规则 ,让 Prettier 专门负责代码格式化,禁用 ESLint 中与 Prettier 冲突的规则,安装此插件后不用再安eslint-config-prettier
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting';


export default defineConfigWithVueTs(
  {
    name: 'app/files-to-lint',
    files: ['**/*.{ts,mts,tsx,vue}'],
  },
  globalIgnores([
    '**/dist/**',
    '**/dist-ssr/**',
    '**/coverage/**',
    'node_modules',
    'src/components/Login/sms-verifition-input/**',
    'src/lib/**',
  ]),
  pluginVue.configs['flat/essential'],
  vueTsConfigs.recommended,
  skipFormatting,
  {
    name: 'app/custom-rules',
    rules: {
      // 简化生产环境判断逻辑
      'no-console': 'off',
      'no-debugger': 'off',
      'key-spacing': [
        'error',
        {
          beforeColon: false,
          afterColon: true,
        },
      ],
      'space-in-parens': ['error', 'never'],
      'object-curly-spacing': ['error', 'always'],
      'object-curly-newline': [
        'error',
        {
          minProperties: 5,
          multiline: true,
          consistent: true,
        },
      ],
      'vue/object-curly-spacing': 'off',
      'max-len': 'off',
      'no-multi-spaces': 'error',
      'no-return-assign': 'off',
      semi: ['error', 'always'],
      eqeqeq: 'error',
      'jsx-quotes': ['off', 'prefer-single'],
      'import/prefer-default-export': 'off',
      'import/extensions': 'off',
      'import/no-unresolved': 'off',
      'no-multiple-empty-lines': [
        'error',
        {
          max: 2,
          maxEOF: 1,
        },
      ],
      'no-param-reassign': 'off',
      'vue/eqeqeq': ['error', 'always'],
      'vue/html-end-tags': 'error',
      'vue/no-spaces-around-equal-signs-in-attribute':
        'error',
      'vue/multi-word-component-names': 'off',
      'vue/no-template-shadow': 'error',
      'vue/require-prop-types': 'error',
      'vue/require-explicit-emits': 'error',
      'vue/mustache-interpolation-spacing': [
        'error',
        'always',
      ],
      'vue/no-multi-spaces': [
        'error',
        {
          ignoreProperties: false,
        },
      ],
      'vue/html-closing-bracket-newline': [
        'error',
        {
          singleline: 'never',
          multiline: 'always',
        },
      ],
      'vue/html-self-closing': 'off',
      'vue/block-lang': 'off',
      'vue/html-indent': [
        'error',
        2,
        {
          attribute: 1,
          baseIndent: 1,
          closeBracket: 0,
          alignAttributesVertically: true,
          ignores: ['VExpressionContainer'],
        },
      ],
      'vue/html-closing-bracket-spacing': [
        'error',
        {
          startTag: 'never',
          endTag: 'never',
          selfClosingTag: 'always',
        },
      ],
      'vue/max-attributes-per-line': [
        'error',
        {
          singleline: 3,
          multiline: 1,
        },
      ],
      'vue/attribute-hyphenation': 'off',
      '@typescript-eslint/no-shadow': 'off',
      '@typescript-eslint/no-explicit-any': 'off',
      '@typescript-eslint/no-unused-vars': 'warn',
      '@typescript-eslint/ban-ts-comment': 'off',
      '@typescript-eslint/indent': 'off',
      '@typescript-eslint/no-empty-object-type': 'off',
    },
  },
);

2.2、配置prettier

  • 安装

eslint-plugin-prettier: 将 Prettier 作为 ESlint 的扩展插件,成为 ESlint 语法检查规则的扩展部分。

pnpm add eslint-plugin-prettier -D
  • 修改eslint.config.ts配置信息
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';

export default defineConfigWithVueTs(
  ...
  
  eslintPluginPrettierRecommended,//新增配置
  
 	...
  }
);

  • 修改.prettierrc.json文件
  • image-20250507160738359
{
  "$schema": "https://json.schemastore.org/prettierrc",
  "semi": true,
  "tabWidth": 2,
  "singleQuote": true,
  "printWidth": 150,
  "bracketSpacing": true,
  "arrowParens": "always",
  "endOfLine": "lf",
  "trailingComma": "all",
  "bracketSameLine": false,
  "embeddedLanguageFormatting": "auto",
  "useTabs": false,
  "htmlWhitespaceSensitivity": "ignore"
}

3、配置typescript规则

3.1、配置文件的区别

image-20250507161114556

我们可以看到vite的项目结构中有tsconfig.app.jsontsconfig.node.jsontsconfig.json三个文件,那么我们配置他相关规则的时候又需要配置再哪里呢?他们的区别是什么呢?

tsconfig.json
  • 顶层TypeScript配置文件,作为项目的主入口,通过 references 字段引用其他子配置文件(如 appnode)。
  • 采用“项目引用”模式,可以将不同用途的 TypeScript 配置拆分,提升构建效率和灵活性,便于分别管理前端应用代码和 Node 脚本/配置的类型检查。
  • 一般不直接在这里写具体的编译选项,而是通过 references 字段指向其他 tsconfig 文件,如需全局配置可在此补充。
tsconfig.app.json
  • 专门为前端应用(src 目录下的 Vue/TS 代码)服务的 TypeScript 配置文件,让前端业务代码(包括 .ts、.vue 文件)拥有独立的类型检查和编译选项。

  • 可以根据前端需求定制编译目标、模块解析、路径别名等。

  • 需要为前端代码调整的 TypeScript 配置(如 baseUrl 、 paths 、 lib 、 jsx 、 strict 等),都应该写在这里。

tsconfig.node.json
  • 专门为 Node.js 相关脚本(如 Vite 配置、测试配置、脚本工具等)服务的 TypeScript 配置文件。
  • Node 环境和前端环境的类型需求不同(如全局变量、模块解析方式等),需要单独配置。
  • 避免 Node 脚本和前端代码的类型冲突,提高类型检查的准确性。
  • 需要为 Node 脚本调整的 TypeScript 配置(如 types 、 moduleResolution 、 include 指向配置文件等),都应该写在这里。
  • 如果你要让 Vite、Vitest、Cypress 等配置文件获得 Node 类型提示,或调整 Node 相关的编译选项,就在这个文件里配置。
总结
  • 全局/项目结构相关配置 :放在 tsconfig.json ,主要维护 references
  • 前端业务代码相关配置 :放在 tsconfig.app.json ,如路径别名、严格模式、前端库等。
  • Node 脚本相关配置 :放在 tsconfig.node.json ,如 Node 类型、配置文件包含等。

3.2、调整的 TypeScript 配置

通过上述文件的区分,我们可以很清楚的知道,我们主要只用配置tsconfig.app.json 文件(如果没有特殊需求可以直接将tsconfig.app.json文件的内容复制到 tsconfig.json中,然后删除 tsconfig.app.jsontsconfig.node.json

  • 修改配置tsconfig.app.json文件
{
  "extends": "@vue/tsconfig/tsconfig.dom.json",
  "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
  "exclude": ["src/**/__tests__/*"],
  "compilerOptions": {
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
    "target": "ES2020",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "node",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve",
    "allowJs": true,

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,

    /* Paths */
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}
  • 修改配置tsconfig.node.json文件
{
  "extends": "@tsconfig/node22/tsconfig.json",
  "include": [
    "vite.config.*",
    "vitest.config.*",
    "cypress.config.*",
    "nightwatch.conf.*",
    "playwright.config.*",
    "eslint.config.*"
  ],
  "exclude": ["src/**", "dist/**"],
  "compilerOptions": {
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
    "noEmit": true,
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "types": ["node"],
    "allowJs": true,
    "resolveJsonModule": true,
    "esModuleInterop": true
  }
}

4、配置代码检查器

vite-plugin-checker 是一个 Vite 插件,它能够在工作线程中运行 TypeScript、ESLint、vue-tsc、Stylelint 等多种静态代码检查工具,以提高开发效率并确保代码质量。

  • 安装
pnpm add vite-plugin-checker -D
  • 修改vite.config.ts配置
import { fileURLToPath, URL } from 'node:url';

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import checker from 'vite-plugin-checker';
import vueDevTools from 'vite-plugin-vue-devtools';

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    vueJsx(),
    vueDevTools(),
    checker({
      overlay: {
        initialIsOpen: false,
      },
      typescript: true,
      vueTsc: true,
    }),
  ],
});

5、修改路由配置信息

如果公司对应服务没有做相关的路由映射,需要将src/router/index.ts中的createWebHistory替换成createWebHashHistory如果有请忽略这一步骤~

如下所示
image-20250507161001646

二、重置浏览器默认样式(可选)

normalize.css 是一个用于重置浏览器默认样式的库,使得不同浏览器之间的渲染更加一致

  • 安装
pnpm add normalize.css
  • src/mian.ts引入
import './assets/main.css';

import { createApp } from 'vue';
import { createPinia } from 'pinia';

import App from './App.vue';
import router from './router';
import 'normalize.css';

const app = createApp(App);

app.use(createPinia());
app.use(router);

app.mount('#app');

三、安装样式预处理器(可选)

大家可以自行安装自己熟悉的预处理器(less、sass、stylus……),此处我选择自己常用的sass

pnpm add sass -D

tip:vite内置了常用的预处理器支持无需,安装配置sass-loader 即可使用

四、ui组件库安装(可选)

市面上的ui组件库有很多,此处我只提供我最常用的两种组件库进行配置

如果是搭建后台管理系统,此处可看element-plus配置。
如果是搭建移动端h5,此处建议可看vant组件库

1、element-plus组件库配置(后台管理系统推荐)

官方文档配置:https://element-plus.org/zh-CN/guide/quickstart.html

  • 安装
pnpm add element-plus
pnpm add -D unplugin-vue-components unplugin-auto-import
  • 修改vite.config.ts配置
import { fileURLToPath, URL } from 'node:url';

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import checker from 'vite-plugin-checker';
import vueDevTools from 'vite-plugin-vue-devtools';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    vueJsx(),
    vueDevTools(),
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
    checker({
     eslint:
          mode === 'production'
            ? false
            : {
                useFlatConfig: true, //此处必须配置
                lintCommand:
                  'eslint  "./src/**/*.{ts,mts,tsx,vue,js,jsx}"',
              },
        terminal: true,
      }),
      overlay: {
        initialIsOpen: false,
      },
      typescript: true,
      vueTsc: true,
    }),
  ],
});

2、vant组件库配置(移动端推荐)

官方文档配置: https://vant-ui.github.io/vant/#/zh-CN/quickstart

  • 安装
pnpm add vant
pnpm add /auto-import-resolver unplugin-vue-components unplugin-auto-import -D
  • 修改vite.config.ts配置
import { fileURLToPath, URL } from 'node:url';

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import checker from 'vite-plugin-checker';
import vueDevTools from 'vite-plugin-vue-devtools';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { VantResolver } from '@vant/auto-import-resolver';

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    vueJsx(),
    vueDevTools(),
    AutoImport({
      resolvers: [VantResolver()],
    }),
    Components({
      resolvers: [VantResolver()],
    }),
    checker({
      eslint: {
        useFlatConfig: true,
        lintCommand: 'eslint "./src/**/*.{ts,tsx,vue}"',
      },
      overlay: {
        initialIsOpen: false,
      },
      typescript: true,
      vueTsc: true,
    }),
  ],
});

五、二次封装axios(可选)

  • 安装
pnpm add axios

新增src/request/type.ts文件

import axios from 'axios';
import type {
  AxiosResponse,
  AxiosInstance,
  AxiosRequestConfig,
} from 'axios';

// 提取函数参数类型
export type AxiosMethodParams<
  T extends (...args: any) => any,
> = Parameters<T>;

// export type AxiosMethodReturn<T extends (...args: any) => any> = ReturnType<T>;
//  axios 请求方法的返回类型
export type AxiosMethodReturn<T = any> = Promise<
  AxiosResponse<T>
>;

export interface AxiosStatic extends AxiosInstance {
  GET: <T = any, D = any>(
    url: string,
    params?: D,
    config?: AxiosRequestConfig<D>,
  ) => AxiosMethodReturn<T>;

  POST: <T = any, D = any>(
    ...args: AxiosMethodParams<
      typeof axios.post<T, AxiosResponse<T>, D>
    >
  ) => AxiosMethodReturn<T>;

  PUT: <T = any, D = any>(
    ...args: AxiosMethodParams<
      typeof axios.put<T, AxiosResponse<T>, D>
    >
  ) => AxiosMethodReturn<T>;

  DELETE: <T = any, D = any>(
    url: string,
    params?: D,
    config?: AxiosRequestConfig<D>,
  ) => AxiosMethodReturn<T>;
}

新增src/request/index.ts文件

import axios from 'axios';
import type {
  AxiosResponse,
  AxiosRequestConfig,
  InternalAxiosRequestConfig,
} from 'axios';
import type {
  AxiosStatic,
  AxiosMethodParams,
} from './type.ts';
import { showToast } from 'vant';

// 创建一个扩展的 axios 实例
const Axios = axios.create({
  timeout: 20000,
}) as AxiosStatic;

// 创建统一的错误处理函数
const handleRequestError = (error: any) => {
  if (error?.message === 'Network Error') {
    showToast('网络开小差啦,请稍后重试~');
  } else {
    showToast('活动火爆,请您稍后重试~');
  }
  return Promise.reject(error);
};

Axios.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    return config;
  },
  handleRequestError,
);

// 请求结束关闭loading
Axios.interceptors.response.use((res: AxiosResponse) => {
  return res || {};
}, handleRequestError);

Axios.GET = <T = any, D = any>(
  url: string,
  params?: D,
  config?: AxiosRequestConfig<D>,
) => {
  const mergedConfig: AxiosRequestConfig<D> = {
    ...config,
    params: {
      ...params,
      ...(config?.params || {}),
    },
  };

  return Axios.get<T, AxiosResponse<T>, D>(
    url,
    mergedConfig,
  );
};

Axios.POST = <T = any, D = any>(
  ...args: AxiosMethodParams<
    typeof axios.post<T, AxiosResponse<T>, D>
  >
) => {
  return Axios.post<T, AxiosResponse<T>, D>(...args);
};

Axios.PUT = <T = any, D = any>(
  ...args: AxiosMethodParams<
    typeof axios.put<T, AxiosResponse<T>, D>
  >
) => {
  return Axios.put<T, AxiosResponse<T>, D>(...args);
};

Axios.DELETE = <T = any, D = any>(
  url: string,
  params?: D,
  config?: AxiosRequestConfig<D>,
) => {
  const mergedConfig: AxiosRequestConfig<D> = {
    ...config,
    params: {
      ...params,
      ...(config?.params || {}),
    },
  };

  return Axios.delete<T, AxiosResponse<T>, D>(
    url,
    mergedConfig,
  );
};

export default Axios;

六、配置环境变量(可选)

我们开发系统肯定会分为环境开发。有时候在开发、测试环境想要打印调试,但是生产环境又不想进行打印的,或者说开发测试环境生成source map文件定位错误,打包的时候不生成,这个时候环境变量就能派上用场了

Tip: 如果不想创建多环境文件,也可以直接创建一个.env文件,然后每次手动修改开发、测试、生产环境的变量

1、创建配置文件(可选)

根目录创建环境变量配置文件,更多环境一样如此操作

注: 定义的变量必须以VITE_开头, vite做了一个拦截, 他为了防止我们将隐私性的变量直接送进import.meta.env中, 所以他做了一层拦截, 如果你的环境变量不是以VITE开头的, 他就不会帮你注入到客户端中去, 如果我们想要更改这个前缀, 可以去使用envPrefix配置

  • .env(所有环境都用到的环境变量)
VITE_APP_ENV = 'production';
  • .env.dev (开发环境)
VITE_APP_ENV = 'development';
VITE_APP_API_URL = /api / xxx务后地服本端 / xxx测试 / xxx生产都行;
  • .env.test (测试环境)
VITE_APP_ENV = 'test';
VITE_APP_API_URL = xxx测试域名;
  • .env.prod (生产环境)
VITE_APP_ENV = 'production';
VITE_APP_API_URL = xxx生产域名;

2、使用变量

  • 在代码中使用
const baseUrl = import.meta.env.VITE_BASE_URL;
  • 在vite.config.ts中使用环境变量
// 使用loadEnv方法加载环境变量
import { defineConfig, loadEnv } from 'vite';
//...

export default ({ mode }) => {
  console.log('加载的环境变量', loadEnv(mode, process.cwd()).VITE_BASE_URL);
  return defineConfig({
    //...
  });
};

3、修改package.json启动命令

"scripts": {
  "dev": "vite --host --mode dev",
  "build": "vite build",
  "build:dev": "vite build --mode dev",
  "build:test": "vite build --mode test",
  "build-preview:dev": "pnpm build:dev && vite preview --mode dev",
  "build-preview:test": "pnpm build:test && vite preview --mode test",
  "build-preview": "pnpm build && vite preview"
},

在vite中的环境变量处理:

vite内置了dotenv这个第三方库,dotenv会自动读取.env文件, 并解析这个文件中的对应环境变量 并将其注入到process对象下(但是vite 考虑到和其他配置的一些冲突问题, 他不会直接注入到process对象下)

--mode development 会将mode设置为development传递进来

当我们调用loadenv的时候, 他会做如下几件事:

  1. 直接找到.env文件不解释 并解析其中的环境变量 并放进一个对象里

  2. 会将传进来的mode这个变量的值进行拼接: .env.development, 并根据我们提供的目录去取对应的配置文件并进行解析, 并放进一个对象

  3. 我们可以理解为

     const baseEnvConfig = 读取.env的配置
     const modeEnvConfig = 读取env相关配置
     const lastEnvConfig = { ...baseEnvConfig, ...modeEnvConfig }
    

如果是客户端,vite会将对应的环境变量注入到import.meta.env里去

4、修改vite.config.ts文件配置

import { fileURLToPath, URL } from 'node:url';

import { defineConfig, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import vueDevTools from 'vite-plugin-vue-devtools';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { VantResolver } from '@vant/auto-import-resolver';
import checker from 'vite-plugin-checker';
import legacy from '@vitejs/plugin-legacy';
import autoprefixer from 'autoprefixer';
// @ts-ignore
import postcssPluginPx2rem from 'postcss-plugin-px2rem';

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

  return {
    esbuild: {
      drop: isProd ? ['console', 'debugger'] : [], // 移除 console 和 debugger
    },
    base: './',// 设置静态资源的基础路径
    build: {
      assetsDir: 'assets',
      chunkSizeWarningLimit: 1000,//chunk 大小警告阈值(KB)
      minify: 'terser', // 启用terser代码压缩
      sourcemap: isProd ? false : 'inline', // 生产环境禁用,开发环境内联
      reportCompressedSize: false, // 禁用压缩大小报告以提高构建速度
      rollupOptions: {
        output: {
          assetFileNames:
            'assets/[ext]/[name]-[hash][extname]',
          entryFileNames: 'assets/js/[name]-[hash].js',
          chunkFileNames: 'assets/js/[name]-[hash].js',
          // 实现手动分包
          manualChunks(id) {
            if (/(vconsole)/.test(id)) return 'tool';
            if (id.includes('node_modules')) {
              if (id.includes('vant')) return 'vant';
              if (id.includes('vue')) return 'vue';
              if (id.includes('lodash')) return 'lodash';
              return 'vendor';
            }
          },
        },
      },
      target: 'es5', // 强制构建目标为ES5
      cssTarget: 'chrome49', // 针对老旧浏览器设置CSS目标
    },
    plugins: [
      vue(),
      vueJsx(),
      vueDevTools(),
      AutoImport({
        resolvers: [VantResolver()],
      }),
      Components({
        resolvers: [VantResolver()],
      }),
      checker({
        typescript: true,
        vueTsc: true,
        eslint: {
          useFlatConfig: true,
          lintCommand:
            'eslint "./src/**/*.{ts,mts,tsx,vue,js,jsx}"',
        },
        terminal: true,
      }),
    ],
    // 路径别名
    resolve: {
      alias: {
        '@': fileURLToPath(
          new URL('./src', import.meta.url),
        ),
      },
    },
    css: {
      postcss: {
        plugins: [
          autoprefixer(), // 自动使用browserslist配置
        ],
      },
    },
    server: {
      host: '0.0.0.0',
      port: 9999,
    },
    optimizeDeps: {
      include: ['vue', 'vant'], // 预构建常用依赖
    },
  };
});

上述配置大部分都做了注释,可根据项目需求自行调整(仅供参考),不理解的官网自查配置信息 ☞ https://cn.vite.dev/config/

要是还不懂,就去问AI

七、配置Husky和Lint-staged (可选)

不知道大家有没有过这种经历,在同一套项目代码里,遇到这种两模两样的代码风格。
image-20250507164132382

image-20250507162609818

当你pnpm ipnpm run dev运行项目的时候,控制台呼啦呼啦的冒出n多条的warning和error,你找同事了解情况,得知人家不仅把编辑器的报错行为关闭了,还把保存自动修复也给关闭了,还直接忽略warning提示,然后就直接提交了。此时内心一万匹草泥马飘过😒……

于是乎开始思考解决方案,有没有什么东西能让同事的报错代码提交不了,提交的时候有格式问题的代码自动修复呢?

借助eslint --fix的代码修复功能,可以尽最大可能的保持代码一致。当我执行eslint --fix的修复功能时,代码中所有的格式相关问题都被自动修复了,并且提示相关代码中格式的错误。那么有没有什么方法可以让代码在提交到git之前执行自动修复的指令呢?这个时候,优秀的第三方工具库HuskyLint-staged就出现了。

1、什么是Husky和Lint-staged

HuskyLint-staged 是现代前端开发中常用的工具组合,用于在提交代码前进行检查和修复,确保代码质量并规范团队协作流程

  • Husky: husky 是一个用于简化Git钩子(hooks)的设置的工具,允许开发者轻松地在各种Git事件触发时运行脚本。例如,在提交之前(pre-commit)、推送之前(pre-push)、或者在提交信息被写入后(commit-msg)等,它通常与lint-staged一起使用,以在提交前自动执行代码的静态检查。

    • 替代 Git 原生钩子,方便管理和配置。
    • 集成团队协作流程,减少低质量代码进入仓库。
  • Lint-staged: lint-staged 是一个在提交代码之前运行linter或其他工具的工具。使用lint-staged可以确保只有符合项目规定代码质量标准的代码被提交,减少了不必要的错误和风格问题被引入代码库的可能性。

    • 仅处理需要提交的文件,节约时间。
    • 可结合 ESLint、Prettier 等工具自动修复代码。

2、配置Husky

  • 安装
 pnpm add -D husky
  • 初始化husky
npx husky init

自动生成的 .husky 目录和指令:

image-20250507162015634

prepare 是 NPM 的一个特殊生命周期脚本,它会在以下场景自动执行:

  • 安装依赖时(运行 npm install)。
  • 发布包时(运行 npm publish)。
  • 修改 .husky/pre-commit

根目录 .husky 目录下 pre-commit 文件中的 npm test 修改为 npx lint-staged

image-20250507162059297

3、配置Lint-staged

  • 安装
pnpm add -D lint-staged
  • 修改package.json
    package.json 中添加以下字段
"scripts": {
  "lint": "eslint . --fix",
  "format": "prettier --write src/",
  "prepare": "husky"
},
"lint-staged": {
  "*.{js,ts,jsx,tsx,vue}": [
    "pnpm lint",
    "pnpm format"
  ],
  "*.{scss,css,html,json,md}": [
    "pnpm format"
  ]
}

如果你希望不止拦截error还要拦截warn的信息,则将 "lint": "eslint . --fix" 配置成 "lint": "eslint . --max-warnings=0 --fix"

4、提交代码后执行结果

现在,当我们提交代码时,husky会帮我们执行gitgit commit钩子,从而触发lint-staged,针对各类型文件执行pnpm lintpnpm format
image-20250507164349511

九、拓展实用插件(按需安装配置)

此处推荐一些我常用的工具库,大家可以参考按需安装。相关使用方法网上一搜一大堆,这边就不多余再演示了

1、dayjs 时间处理

Day.js是一个极简的JavaScript库, 可以为现代浏览器解析、验证、操作和显示日期和时间,文件大小只有2KB左右。Day.js对国际化有很大的支持。

pnpm add dayjs

2、qs

qs是一个流行的查询参数序列化和解析库。

pnpm add qs
// 如果项目配置了typescript需安装
pnpm add @types/qs -D

3、lodash实用工具库

Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。它提供了一套从数组、数字、对象、字符串、日期等常见数据类型中提取值的函数,以及很多其他实用功能。Lodash 旨在通过提高抽象程度来减少代码量,提高代码的可维护性。

pnpm add lodash
// 如果项目配置了typescript需安装
pnpm add @types/lodash -D

4、big.js(涉及计算相关)

big.js 是一个用于任意精度十进制算术的小型快速 JavaScript 库。 它允许你创建、操作和比较大数字,这些数字的精度超过了 JavaScript 原生 Number 类型所能提供的范围。主要可以用于处理需要高精度计算的场景,例如金额计算、科学计算、密码学等等。

pnpm add big.js
// 如果项目配置了typescript需安装
pnpm add @types/big.js -D

5、js-cookie

是一个简单、轻量级的 JavaScript API 库,用于处理浏览器 cookies。它允许你创建、读取、删除和操作 cookie,而不需要担心浏览器的兼容性问题。

pnpm add js-cookie
// 如果项目配置了typescript需安装
pnpm add @types/js-cookie -D

6、postcss-plugin-px2rem

postcss-plugin-px2rem 是一个 PostCSS 插件,它可以自动 将 CSS 文件中的像素单位(px)转换为相对单位(rem),以实现响应式布局和移动端适配。这个插件特别适用于需要根据不同分辨率的移动设备进行适配的场景。

  • 安装
pnpm add -D postcss-plugin-px2rem autoprefixer
  • 配置vite.config.js
import autoprefixer from 'autoprefixer'
import postcssPluginPx2rem from 'postcss-plugin-px2rem';
import { defineConfig } from 'vite';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
  ...
  ],
  resolve: {
  ...
  },
  css: {
    postcss: {
      plugins: [
        autoprefixer,
        postcssPluginPx2rem({
          remUnit: 108,
          rootValue: 108, // 换算基数,1rem 相当于多少 px
		  unitPrecision: 5, // 允许 REM 单位增长到的十进制数字
		  propWhiteList: [], // 白名单,指定哪些属性不转换为 rem
		  propBlackList: [], // 黑名单,指定哪些属性需要转换为 rem
		  exclude: /(node_module)/, // 排除的文件夹或文件
		  selectorBlackList: [], // 要忽略并保留为 px 的选择器
		  mediaQuery: false, // 允许在媒体查询中转换 px
		  minPixelValue: 3 // 设置要替换的最小像素值
        }),
      ],
    },
  },
});

7、@vitejs/plugin-legacy(兼容旧浏览器,移动端项目重点推荐!!!)

公司合作的一些客户自带的客户端浏览器版本超级无敌老旧(此处内涵某些银行😒),项目发布后由于浏览器过于老旧无法处理新版本的语法产生白屏问题,使用这个插件可以生成兼容旧版浏览器的构建文件解决这个问题(救我狗命!!!)。

@vitejs/plugin-legacy 是一个 Vite 插件,用于为 Vite 项目提供对旧版浏览器的支持。 这个插件可以根据你在项目配置中指定的目标浏览器列表(通过 browserslist 字段),自动生成兼容旧版浏览器的构建文件。这些构建文件将包含经过转换和降级处理的代码,以 确保在不支持最新 JavaScript 特性的浏览器中正常运行。 使用 @vitejs/plugin-legacy 插件后,当用户访问你的网站时,Vite 将根据用户的浏览器版本动态加载适合其浏览器的构建文件。

  • 安装
pnpm add -D @vitejs/plugin-legacy
  • 配置vite.config.js
import legacy from '@vitejs/plugin-legacy';
import { defineConfig } from 'vite';

// https://vitejs.dev/config/
export default defineConfig({
  	plugins: [
  	  vue(),
	  legacy({
      targets: ['defaults', 'not IE 11'],
      modernPolyfills: true, //启用现代浏览器 polyfills
      renderLegacyChunks: false, // 禁用旧版本代码块生成
    }),
   ],
  resolve: {
  ...
  },
});
  • 配置package.json
    在 package.json 文件中的 “browserslist” 字段中指定需要支持的目标浏览器。例如,如果你想要支持最近两个版本的 Chrome 和 Firefox 浏览器,可以将该字段修改为:
"browserslist": [
  "last 2 Chrome versions",
  "last 2 Firefox versions"
]

8、vconsole(移动端调试)

vConsole模拟了浏览器的 Console、Network、Storage 等核心功能,使得开发者可以在 移动端网页中直接进行调试。vConsole 提供了丰富的功能,包括查看 console 日志、监控网络请求、检查页面 DOM 结构以及管理 Cookies 和 localStorage 等,极大地便利了开发者的调试工作。

  • 安装
pnpm add vconsole
  • 使用
import VConsole from 'vconsole';
const isPc = () => {
  const userAgentInfo = navigator.userAgent;
  const Agents = ['Android', 'iPhone', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod'];
  let flag = true;
  for (let v = 0; v < Agents.length; v++) {
    if (userAgentInfo.indexOf(Agents[v]) > 0) {
      flag = false;
      break;
    }
  }
  return flag;
};

if (import.meta.env.VITE_APP_ENV !== 'production' && !isPc) {
  new VConsole();
}
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值