Skip to content
第 19 章 前端 ⏱ 13 分钟阅读

第 19 章:Vite + 工程化 ​

学习目标 ​

  • 配置 Vite 常用选项
  • 用环境变量区分 dev / prod
  • 配置 alias 简化路径
  • 选型 plugin(UnoCSS / Vue DevTools)

一、最小 vite.config.ts ​

ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { fileURLToPath, URL } from 'node:url';

export default defineConfig({
	plugins: [vue()],
	resolve: {
		alias: {
			'@': fileURLToPath(new URL('./src', import.meta.url)),
		},
	},
});

💡 逐行翻译:

ts
import { defineConfig } from 'vite'; // ① Vite 配置工具(类型补全)
import vue from '@vitejs/plugin-vue'; // ② Vue 插件(让 Vite 认 .vue 文件)
import { fileURLToPath, URL } from 'node:url'; // ③ Node 内置的 URL 处理工具

export default defineConfig({
	// ④ 配置入口,defineConfig 包一下有 TS 提示
	plugins: [vue()], // ⑤ 启用 Vue 插件(没它 .vue 文件不识别,必加)
	resolve: {
		// ⑥ 路径解析配置
		alias: {
			// ⑦ 别名:用 '@' 代替 './src'
			'@': fileURLToPath(new URL('./src', import.meta.url)), // ⑧ 转成绝对路径
		},
	},
});

三个 import 各干嘛:

import作用
defineConfigVite 配置函数,自带 TS 类型提示,不写也能跑
@vitejs/plugin-vueVue 必备插件,没它 .vue 文件直接报错
fileURLToPath, URL把 URL 形式转成本地路径(替代 __dirname,因为 ESM 里没有)

fileURLToPath(new URL('./src', import.meta.url)) 翻译:等价于 path.resolve(__dirname, './src'),得到 src/ 的绝对路径。import.meta.url 是当前文件 URL,.src 是相对路径,new URL(...) 拼成完整 URL,fileURLToPath 转成本地系统路径。Vite 配置是 ESM 模式,没有 __dirname,必须这样绕。

别名实际效果:

ts
// 原来
import Hello from '../../../components/Hello.vue';
// 现在
import Hello from '@/components/Hello.vue'; // @ = src/

二、环境变量 ​

bash
# .env.development
VITE_API_BASE=http://localhost:3000

# .env.production
VITE_API_BASE=https://api.example.com
ts
const base = import.meta.env.VITE_API_BASE;

⚠️ 坑 1:自定义变量必须 VITE_ 前缀,否则读不到(出于安全)。

三、常用配置 ​

ts
export default defineConfig({
	server: {
		port: 5173,
		open: true,
		proxy: {
			'/api': {
				target: 'http://localhost:3000',
				changeOrigin: true,
				rewrite: (p) => p.replace(/^\/api/, ''),
			},
		},
	},
	build: {
		outDir: 'dist',
		sourcemap: false,
		rollupOptions: {
			output: {
				manualChunks: {
					vue: ['vue', 'vue-router', 'pinia'],
					element: ['element-plus'],
				},
			},
		},
	},
});

💡 逐行翻译:

server(开发服务器)

ts
server: {
  port: 5173,                          // ① dev 时端口 http://localhost:5173
  open: true,                          // ② 启动后自动打开浏览器
  proxy: {                             // ③ 代理配置:解决本地跨域
    '/api': {                          // ④ 匹配前端发出的 /api 开头的请求
      target: 'http://localhost:3000', // ⑤ 转发到后端 3000 端口
      changeOrigin: true,              // ⑥ 改请求头 Host(避免后端校验失败)
      rewrite: (p) => p.replace(/^\/api/, ''),  // ⑦ 去掉 /api 前缀
                                       //   /api/users → /users
    },
  },
},

build(生产构建)

ts
build: {
  outDir: 'dist',                      // ① 产物输出到 dist/ 目录
  sourcemap: false,                    // ② 不生成 .map 文件(关掉省体积,报错定位会难)
  rollupOptions: {                     // ③ Rollup 打包选项(Vite 用 Rollup)
    output: {
      manualChunks: {                  // ④ 手动分包:把指定依赖拆成单独 chunk
        vue: ['vue', 'vue-router', 'pinia'],  // ⑤ vue 全家桶 → 一个 chunk
        element: ['element-plus'],            // ⑥ element-plus → 单独 chunk
      },
    },
  },
},

proxy 工作流程:

浏览器 GET /api/users
  → Vite dev server(5173)收到
  → 匹配 '/api' 规则
  → 改 Host 头 + 去掉 /api 前缀
  → 转发到 http://localhost:3000/users
  → 后端返回数据

为啥要 proxy:前端 5173 调后端 3000,浏览器会报跨域;Vite 在中间帮你转发,绕开浏览器的跨域检查。

manualChunks 拆包效果:

没拆:
  dist/assets/index-abc.js   (3 MB,全混一起,首加载巨慢)

拆了:
  dist/assets/index-main.js  (几百 KB,业务代码)
  dist/assets/vue-vendor.js  (几十 KB,vue 全家桶,长期缓存)
  dist/assets/element-vendor.js (几百 KB,ElementPlus,长期缓存)
                                 ↑
             浏览器缓存后,只下变了的那块,没变的不用重下

三个关键参数:

参数作用不写会怎样
changeOrigin: true改请求头 Host后端校验 Host 失败,403
rewrite 去掉 /api路径调整后端路径对不上,404
manualChunks 拆包缓存优化改一行代码整个包重下,慢

四、别名与路径 ​

ts
resolve: {
  alias: {
    '@': fileURLToPath(new URL('./src', import.meta.url)),
    '@@': fileURLToPath(new URL('./', import.meta.url)),
  },
},
ts
// tsconfig.json
{
  "compilerOptions": {
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

⚠️ 坑 2:tsconfig.json 的 paths 必须和 Vite alias 同步,否则 TS 报错。

💡 paths 只负责编译前的 TS 类型检查,不影响运行;运行时找文件还是看 Vite alias。

TypeScript 检查代码时,根据 paths 解析路径——只管类型检查 / IDE 提示 / 跳转,不影响运行时。

三个时点对照:

你保存代码
   ↓
① IDE / `tsc` 检查(用 tsconfig.json 的 paths)         ← 这个时点才用到 paths
   - 报错/不报错
   - 提示/不提示
   - 跳转/不跳转
   ↓
② Vite 启动 dev / build(用 vite.config.ts 的 alias)    ← 这个时点用 alias
   - 真去磁盘找文件
   - 把 .vue / .ts 编译成浏览器能跑的代码
   ↓
③ 浏览器加载页面                                         ← 跟两个配置都没关系了
   - 直接看打包后的代码

paths 只在 ① 用——TS 看完一眼,任务结束;真正干活的是 Vite 的 alias(②)。

一句话总结:tsconfig paths = 编译前 TS 看一眼用的(类型检查 + 提示);Vite alias = 真去读文件。前者是"体检报告",后者才是"干活的"。

五、常用插件 ​

bash
npm i -D @vitejs/plugin-vue unocss/vite vite-plugin-vue-devtools
ts
import vue from '@vitejs/plugin-vue';
import vueDevTools from 'vite-plugin-vue-devtools';
import UnoCSS from 'unocss/vite';

export default defineConfig({
	plugins: [vue(), vueDevTools(), UnoCSS()],
});

六、CSS 预处理 ​

bash
npm i -D sass
vue
<style lang="scss" scoped>
.title {
	font-size: 20px;
	&:hover {
		color: red;
	}
}
</style>

七、SVG 组件化 ​

bash
npm i -D vite-svg-loader
ts
// vite.config.ts
import svgLoader from 'vite-svg-loader';

export default defineConfig({
	plugins: [vue(), svgLoader()],
});
vue
<script setup lang="ts">
import Logo from '@/assets/logo.svg?component';
</script>
<template>
	<Logo />
</template>

⚠️ 坑 3:Vite 5+ 已经默认支持 import svg from './a.svg?url' 拿 URL,vite-svg-loader 主要用来当组件用。

💡 vite-svg-loader = 把 SVG 转成 Vue 组件(?component),好处是组件能改颜色 / 加事件 / 响应式;单纯显示用 ?url 就行,Vite 5+ 自带。

三种 SVG 用法对照:

vue
<!-- ① 当组件用(本节写法) -->
<script setup>
import Logo from './logo.svg?component'   // ?component = 转成组件
</script>
<template><Logo class="icon" /></template>
<!-- 优点:可改 color / size / 加 @click,响应式 -->

<!-- ② 拿 URL,自己写 <img>(Vite 5+ 自带,不用装插件) -->
<script setup>
import logoUrl from './logo.svg?url'
</script>
<template><img :src="logoUrl" /></template>
<!-- 缺点:静态展示,不能改颜色,只能 CSS 整图调 -->

<!-- ③ 拿原始字符串(很少用) -->
import logoRaw from './logo.svg?raw'   // 拿到 "<svg>...</svg>" 字符串

?component 后缀翻译:Vite 的"导入修饰符",告诉 Vite "别给我 URL,把 SVG 转成 Vue 组件"。

啥时候用哪个:

想干嘛用啥
想改颜色 / 大小 / 加事件?component + vite-svg-loader(本节)
纯展示,静态 logo / 图片?url + <img>(Vite 自带,不装插件)
想用 CSS 控制颜色必须组件模式(<img> 改不了 SVG 内部 fill)

八、自动导入 ​

bash
npm i -D unplugin-auto-import unplugin-vue-components
ts
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';

export default defineConfig({
	plugins: [
		AutoImport({ resolvers: [ElementPlusResolver()] }),
		Components({ resolvers: [ElementPlusResolver()] }),
	],
});

💡 自动导入 = 不用写 import 直接用——ref / computed / <el-button> 全部不用手动 import。

两个插件各管啥:

插件负责不装会怎样
AutoImport自动导入 JS API(ref / computed / watch)每个文件都要 import { ref, computed } from 'vue'
Components自动注册 UI 组件(<el-button> / <el-card>)每个文件都要 import { ElButton } from 'element-plus'

实际效果对照:

vue
<!-- ❌ 不装自动导入:每个文件都得手动写 import -->
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { ElButton, ElInput } from 'element-plus'

const name = ref('zs')
</script>

<!-- ✅ 装了自动导入:啥都不写,直接用 -->
<script setup lang="ts">
const name = ref('zs')   // ref 自动从 'vue' 导入
</script>
<template>
  <ElButton>{{ name }}</ElButton>  <!-- ElButton 自动从 'element-plus' 导入+注册 -->
</template>

ElementPlusResolver() 是啥:告诉自动导入"我要支持 Element Plus"。不传 resolvers → 默认只支持 Vue / Vue Router / Pinia 的 API,UI 库不生效。

自动生成 .d.ts 类型:

ts
// auto-imports.d.ts(自动生成,别手改)
declare global {
  const ref: typeof import('vue')['ref']
  const computed: typeof import('vue')['computed']
}

tsconfig.json 的 include 要包含它(一般不用手改)。

坑 / 注意点:

坑解决
TS 报"找不到 ref"检查 auto-imports.d.ts 在 tsconfig include 里
新人接手不知道 ref 哪来的这是个缺点——可读性变差,大项目慎用
改了配置文件不生效重启 dev server
不同 UI 库想混用加多个 resolver:resolvers: [ElementPlusResolver(), AntDesignVueResolver()]

九、本章小结 ​

配置作用
alias路径别名
server.proxy跨域代理
build.rollupOptions分包
import.meta.env环境变量
UnoCSS / AutoImport提效

动手练习 ​

  1. 配置 alias:把 @ 指向 src
  2. 代理 /api:前端 5173 调后端 3000
  3. 自动导入:让 Element Plus 组件和 API 自动可用

下一章:第 20 章:TypeScript + Vue →

本站基于 VitePress 构建 · 由 StackHub 团队维护