Vue 3 的 Composition API 和响应式系统重构让框架本身的性能和开发体验都上了一个台阶,但框架只能解决写代码的问题,解决不了代码怎么组织、规范怎么统一、构建怎么提速这些事。这些恰恰是工程化要干的活。一个没有做工程化治理的 Vue 3 项目,通常在三个月后就会暴露出目录混乱、依赖冲突、类型报错满天飞的问题。本文把 Vue 3 工程化搭建的完整流程拆开讲清楚,从项目初始化一路讲到构建部署。

一、项目初始化与目录结构设计
官方推荐用 Vite 来创建 Vue 3 项目,执行 npm create vue@latest 后会进入交互式引导,可以按需勾选 TypeScript、Router、Pinia、ESLint、Prettier 等选项。相比手动搭脚手架,这种方式生成的项目已经预置好了 vite.config.ts、TS 配置和基础目录。如果追求极简,也可以用 npm create vite@latest my-app -- --template vue-ts 直接落成一个最小化的 TS 模板。
目录结构没有绝对标准,但要遵循一条原则:按职责分层,而不是按文件类型堆砌。推荐的结构大致如下:
src/ ├── api/ # 接口请求,按业务模块拆分文件 ├── assets/ # 静态资源 ├── components/ # 通用组件 ├── composables/ # 组合式函数,useXxx 命名 ├── layouts/ # 布局组件 ├── router/ # 路由配置 ├── stores/ # Pinia 仓库 ├── styles/ # 全局样式与变量 ├── types/ # TS 类型定义 ├── utils/ # 工具函数 ├── views/ # 页面级组件 ├── App.vue └── main.ts
把组合式函数单独放进 composables 目录是 Vue 3 项目的惯例,凡是跨页面复用的逻辑,比如分页请求、防抖搜索、权限校验,都应该抽成 useXxx 形式的函数,而不是塞进 mixins。Mixins 在 Vue 3 中已被官方明确不推荐,命名冲突和来源不透明的老问题在大型项目里会被无限放大。
路径别名也是初始化阶段就该配好的。在 vite.config.ts 中设置别名之后,import 语句就不用再写一长串相对路径,重构移动文件时也不用逐个修改引用:
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
port: 5173,
proxy: {
// 本地开发代理,解决跨域
'/api': {
target: 'http://127.0.0.1:3000',
changeOrigin: true
}
}
}
})
注意别名要在 tsconfig.json 里同步配置 paths 字段,否则 Vite 能编译通过,但 IDE 的类型提示会报找不到模块。
二、代码规范与提交规范治理
多人协作的项目里,规范不统一是最消耗精力的事。工程化的做法是把规范固化到工具链里,让代码在提交前自动被检查和格式化。核心组合是 ESLint 负责代码质量检查,Prettier 负责格式统一,husky 加 lint-staged 负责在 git 提交钩子上强制执行前两者。
Vue 3 项目推荐直接使用官方的 @vue/eslint-config-typescript 配置集,它已经处理好了 .vue 单文件组件中 script 部分的解析问题。一份典型的基础配置如下:
import pluginVue from 'eslint-plugin-vue'
import vueTsEslintConfig from '@vue/eslint-config-typescript'
export default [
...pluginVue.configs['flat/recommended'],
...vueTsEslintConfig(),
{
rules: {
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-explicit-any': 'warn'
}
}
]
接下来装上 husky 和 lint-staged,配置在 package.json 中:
{
"lint-staged": {
"*.{ts,tsx,vue}": ["eslint --fix", "prettier --write"],
"*.{css,scss,json,md}": ["prettier --write"]
}
}
这样配置之后,任何不规范或未格式化的代码都无法通过 git commit,规范从口头约定变成了流程约束。再进一步可以引入 commitlint 约束提交信息格式,要求提交说明必须符合 feat、fix、docs 这类前缀,方便后续生成变更日志和版本号。
三、TypeScript 接入要点与常见坑
Vue 3 对 TypeScript 的支持是原生级别的,<script setup lang="ts"> 写法下,ref、computed、props 都能获得完整类型推导。但有几个坑值得提前知道。
第一个坑是 ref 的类型收窄。ref(1) 会被推导成 Ref<number>,这是对的,但如果初始值是 null,比如获取 DOM 引用时写 ref(null),类型就成了 Ref<null>,后续赋值会报错。正确做法是显式标注泛型:
import { ref } from 'vue'
// 错误:类型被推导为 Ref<null>
const box = ref(null)
// 正确:显式指定联合类型
const inputEl = ref<HTMLInputElement | null>(null)
第二个坑是 defineProps 的类型写法。在 <script setup> 中推荐直接用类型参数声明 props,编译宏会自动生成运行时校验:
<script setup lang="ts">
interface Props {
title: string
count?: number
list: string[]
}
const props = withDefaults(defineProps<Props>(), {
count: 0
})
</script>
第三个坑是第三方库的类型缺失。有些老库没有自带类型声明,直接 import 会报 TS 错误。可以在 types 目录下写一个 .d.ts 文件声明模块,或者临时用 declare module 兜底,但长期方案还是优先选择有类型维护的替代库。
接口返回值的类型也不该放任不管。建议在 types 目录里按业务定义响应结构,并封装统一的请求函数把后端返回解析成具体类型,这样组件里拿到的数据全程有类型提示,字段写错在编码阶段就会被标红。
四、构建优化与自动化部署
开发到一定规模后,构建产物的体积和依赖的加载方式需要专门治理。Element Plus 这类大型组件库如果全量引入,会把包体积撑大不少,配合 unplugin-vue-components 可以实现模板中用到哪个组件就自动按需引入哪个,无需手动注册:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
Components({
resolvers: [ElementPlusResolver()]
})
],
build: {
rollupOptions: {
output: {
// 按需拆包,利用浏览器缓存
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'],
echarts: ['echarts']
}
}
}
}
})
拆分 manualChunks 的意义在于业务代码频繁变动时,vendor 这类稳定依赖的缓存不会失效,用户二次访问能直接命中浏览器缓存,加载速度明显改善。拆分粒度也不宜过细,几十 KB 的小包反而会增加请求数。配好之后可以用 rollup-plugin-visualizer 生成体积分析报告,直观看到每个依赖占了多少空间,再决定优化目标。
部署环节建议交给 CI 流水线。以常见的方案为例,推送代码到仓库后自动触发流水线:安装依赖、执行类型检查和单元测试、跑生产构建、把 dist 目录发布到静态服务器或 CDN。package.json 里准备好脚本即可:
{
"scripts": {
"dev": "vite",
"build": "run-p type-check build-only",
"type-check": "vue-tsc --noEmit",
"build-only": "vite build",
"lint": "eslint . --fix"
}
}
用 vue-tsc --noEmit 做纯类型检查放在构建流程里,能在打包前拦住类型错误,避免把带病代码发上线。环境变量方面记得遵守 Vite 的约定:只有 VITE_ 前缀的变量会暴露给客户端代码,密钥类信息绝对不要写进 .env 文件,而是留在构建环境的私密变量里注入。
总结一下,Vue 3 的工程化本质上是一套流水线:Vite 负责构建效率,目录结构与组合式函数负责代码组织,ESLint 与 Prettier 负责规范统一,TypeScript 负责类型安全,按需加载与分包负责产物优化,CI 负责部署自动化。每个环节单独看都不复杂,串起来之后项目的可维护性会有质的提升,团队新人接手时也能快速找到代码的正确位置,这才是工程化真正的价值所在。
Vue 3工程化Vite配置TypeScript修改时间:2026-09-03 19:43:20