第17期 | 工程化:Vite构建与项目规范——让项目结构像专业团队一样

🎯 今天你将学会

  • 理解 Vite 为什么比 Webpack 快
  • 配置路径别名、环境变量、代理、插件
  • 建立合理的项目目录结构
  • 配置 ESLint + Prettier + Husky 代码规范三件套
  • 用 AI 辅助配置 Vite 插件

📖 核心知识

1. 为什么用 Vite:构建工具的演进

Webpack 时代的问题(2015-2020):

  • 启动慢:需要把所有模块打包成 bundle 才能启动开发服务器,项目越大越慢
  • 热更新慢:改一行代码,需要重新打包整个 bundle

Vite 的解法(2020至今):

  • 开发时不打包:直接利用浏览器的 ES Module,按需加载文件
  • 热更新极快:只更新改动的那个模块,毫秒级响应
  • 生产构建用 Rollup:输出高度优化的 bundle
传统 Webpack:
源码 → 全部打包成 bundle.js → 开发服务器 → 浏览器

Vite 开发模式:
浏览器请求 → Vite 服务器实时转换对应模块 → 返回给浏览器

实测对比(中型项目):

操作 Webpack Vite
冷启动 30-60s 1-3s
热更新 3-5s <100ms
生产构建 60s 30s

2. 创建 Vite 项目

# 创建 React + TypeScript 项目
npm create vite@latest my-project -- --template react-ts
cd my-project
npm install
npm run dev

项目初始结构:

my-project/
├── public/            # 静态资源(不会被处理,原样输出)
│   └── favicon.ico
├── src/
│   ├── assets/        # 会被 Vite 处理的资源(图片等)
│   ├── App.tsx
│   ├── App.css
│   ├── main.tsx       # 入口文件
│   └── vite-env.d.ts  # 环境变量类型声明
├── index.html         # 根 HTML(Vite 的真正入口)
├── vite.config.ts     # Vite 配置文件
├── tsconfig.json
└── package.json

3. vite.config.ts 配置详解

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  // 插件
  plugins: [
    react(), // React 支持(JSX 转换 + 热更新)
  ],

  // 路径别名:让 @/components 代替 ../../components
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      '@components': path.resolve(__dirname, './src/components'),
      '@hooks': path.resolve(__dirname, './src/hooks'),
      '@stores': path.resolve(__dirname, './src/stores'),
      '@utils': path.resolve(__dirname, './src/utils'),
      '@types': path.resolve(__dirname, './src/types'),
    },
  },

  // 开发服务器
  server: {
    port: 3000,        // 开发端口
    open: true,        // 启动时自动打开浏览器
    
    // API 代理:解决开发时的跨域问题
    proxy: {
      '/api': {
        target: 'http://localhost:8080', // 后端地址
        changeOrigin: true,
        // 可选:重写路径(把 /api 前缀去掉)
        // rewrite: (path) => path.replace(/^\/api/, ''),
      },
    },
  },

  // 构建配置
  build: {
    outDir: 'dist',      // 输出目录
    sourcemap: false,    // 生产不需要 sourcemap
    minify: 'terser',    // 压缩方式
    
    // 分包策略:把大型依赖单独打包,提升缓存命中率
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          router: ['react-router-dom'],
          ui: ['antd'],
        },
      },
    },
  },

  // CSS 配置
  css: {
    preprocessorOptions: {
      scss: {
        // 全局注入 SCSS 变量(不需要在每个文件 import)
        additionalData: `@import "@/styles/variables.scss";`,
      },
    },
    modules: {
      // CSS Modules 类名转换规则
      localsConvention: 'camelCase',
    },
  },
});

同步更新 tsconfig.json 的路径别名:

{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"],
      "@components/*": ["./src/components/*"]
    }
  }
}

4. 环境变量管理

Vite 通过 .env 文件管理环境变量:

# .env(所有环境都读取)
VITE_APP_NAME=My App

# .env.development(开发环境)
VITE_API_URL=http://localhost:8080
VITE_DEBUG=true

# .env.production(生产环境)
VITE_API_URL=https://api.myapp.com
VITE_DEBUG=false

# .env.local(本地覆盖,不提交 git)
VITE_API_KEY=my_secret_key

注意:只有 VITE_ 前缀的变量才会暴露给前端代码!

// 在代码中使用
const apiUrl = import.meta.env.VITE_API_URL;
const isDebug = import.meta.env.VITE_DEBUG === 'true';

// 添加类型声明(避免 TS 报 any 类型)
// src/vite-env.d.ts
interface ImportMetaEnv {
  readonly VITE_API_URL: string;
  readonly VITE_APP_NAME: string;
  readonly VITE_DEBUG: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

5. 专业的项目目录结构

src/
├── assets/              # 图片、字体、SVG 等静态资源
│   ├── images/
│   └── fonts/
│
├── components/          # 公共组件
│   ├── ui/             # 基础 UI 组件(Button、Input、Modal 等)
│   │   ├── Button/
│   │   │   ├── Button.tsx
│   │   │   ├── Button.module.css
│   │   │   └── index.ts  # 统一导出
│   │   └── Input/
│   └── layout/         # 布局组件(Header、Sidebar、Footer)
│
├── features/            # 按业务功能划分(Feature-based 架构)
│   ├── auth/           # 认证功能
│   │   ├── components/ # 该功能专属组件
│   │   ├── hooks/      # 该功能专属 hooks
│   │   ├── stores/     # 该功能的状态
│   │   ├── api.ts      # 该功能的 API 调用
│   │   └── types.ts    # 该功能的类型定义
│   ├── posts/
│   └── dashboard/
│
├── hooks/               # 全局通用自定义 Hooks
│   ├── useFetch.ts
│   ├── useDebounce.ts
│   └── useLocalStorage.ts
│
├── lib/                 # 第三方库的封装/初始化
│   ├── axios.ts        # axios 实例配置
│   └── dayjs.ts        # dayjs 配置(时区、插件)
│
├── pages/               # 路由页面
│   ├── Home/
│   ├── PostDetail/
│   └── Profile/
│
├── router/              # 路由配置
│   └── index.tsx
│
├── stores/              # 全局状态(Zustand)
│   ├── userStore.ts
│   └── notificationStore.ts
│
├── styles/              # 全局样式
│   ├── global.css
│   └── variables.scss
│
├── types/               # 全局类型定义
│   ├── api.ts          # API 响应类型
│   └── models.ts       # 业务模型类型
│
├── utils/               # 工具函数
│   ├── format.ts
│   └── validate.ts
│
├── App.tsx
└── main.tsx

6. ESLint + Prettier + Husky:代码规范三件套

安装:

npm install -D eslint prettier eslint-config-prettier eslint-plugin-react-hooks @typescript-eslint/eslint-plugin @typescript-eslint/parser lint-staged husky

.eslintrc.cjs 配置:

module.exports = {
  root: true,
  env: { browser: true, es2020: true },
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:react-hooks/recommended',
    'prettier', // 必须放最后,覆盖与 Prettier 冲突的规则
  ],
  ignorePatterns: ['dist', '.eslintrc.cjs'],
  parser: '@typescript-eslint/parser',
  plugins: ['react-refresh'],
  rules: {
    'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
    '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
    'no-console': ['warn', { allow: ['warn', 'error'] }], // 允许 console.warn/error
  },
};

.prettierrc 配置:

{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5",
  "printWidth": 100,
  "arrowParens": "avoid"
}

配置 Husky(提交前自动 lint):

# 初始化 husky
npx husky init

# .husky/pre-commit 内容:
npx lint-staged

package.json 中配置 lint-staged:

{
  "lint-staged": {
    "src/**/*.{ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "src/**/*.{css,scss,json,md}": [
      "prettier --write"
    ]
  }
}

现在每次 git commit 前,会自动对暂存区的文件运行 lint 和格式化,确保提交的代码永远规范。


🤖 AI协作实战:AI 解释 Vite 插件配置

场景: 我想给项目加上 SVG 图标组件化支持,但不知道怎么配置。

我给 AI 的 Prompt:

我的 Vite + React + TypeScript 项目需要支持将 SVG 文件作为 React 组件导入(而不是作为图片URL)。
比如:import { ReactComponent as Logo } from './logo.svg'
或者:import Logo from './logo.svg?react'

请帮我:
1. 安装什么包
2. 如何配置 vite.config.ts
3. 给出使用示例
4. 说明这两种导入方式的区别

AI 的回答(整理):

安装:npm install -D vite-plugin-svgr

配置:

import svgr from 'vite-plugin-svgr';

export default defineConfig({
  plugins: [
    react(),
    svgr(), // 添加这行
  ],
});

两种使用方式:

// 方式1:?react 查询参数(推荐,更明确)
import Logo from './logo.svg?react';
<Logo className="logo" width={32} height={32} />

// 方式2:命名导入(兼容 CRA 风格,需配置 exportAsDefault: false)
import { ReactComponent as Logo } from './logo.svg';
<Logo className="logo" />

TypeScript 类型声明(加到 vite-env.d.ts):

declare module '*.svg?react' {
  import React from 'react';
  const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
  export default ReactComponent;
}

💻 动手练习

简单: 创建一个新的 Vite + React + TS 项目,配置路径别名 @ 指向 src,然后在组件中用 @/utils/format.ts 导入一个格式化函数验证配置生效。

中等: 在项目中配置三个环境:

  • 开发环境(.env.development):API 地址指向 localhost:3000
  • 测试环境(.env.staging):API 地址指向 staging.myapp.com
  • 生产环境(.env.production):API 地址指向 api.myapp.com
  • 在首页显示当前环境名称和 API 地址

挑战: 完整配置代码规范三件套(ESLint + Prettier + Husky),然后:

  • 故意写一个违反 ESLint 规则的代码(比如未使用的变量)
  • 尝试 git commit,观察是否被阻止
  • 修复后再次 commit,确认流程正常

📌 本期要点

  1. Vite 快的原因:开发时不打包,利用浏览器原生 ES Module + 按需编译
  2. 路径别名:在 vite.config.tstsconfig.json 都要配,缺一不可
  3. 环境变量必须加 VITE_ 前缀,否则前端访问不到
  4. Feature-based 目录结构比按类型分目录更适合大型项目,修改某功能时只改一个文件夹
  5. Husky + lint-staged:让"提交即规范",团队协作的基础设施

🔗 下期预告

第18期:实战2——Todo App 升级版,把 CRUD + Router + Zustand + TypeScript 全部整合在一个真实项目里,AI 结对编程,你主导决策。
如果你没有苹果电脑,需要上传ios到APPStore可以访问以下网站
iPA上传工具 - IPA解析与AppStore提交

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐