1. 项目概述与核心价值最近在社区和招聘要求里Vue 3 生态的技术栈组合 “Vue 3 Vite TypeScript Pinia Element Plus” 出现的频率越来越高。很多刚接触这套技术栈的朋友或者是从 Vue 2 迁移过来的开发者在搭建第一个项目时往往会遇到各种配置上的“小坑”比如 TypeScript 报类型错误、Vite 的路径别名alias不生效、Element Plus 按需引入后样式丢失或者 Pinia 在组合式 API 里用起来总觉得别扭。我最近正好用这套技术栈启动了一个新的后台管理系统项目从头到尾踩了一遍配置的坑也梳理出了一套比较顺滑的搭建流程。这篇文章我就以一个实际项目为蓝本带你从零开始一步步搭建一个功能完整、配置清晰、开发体验优秀的现代 Vue 3 项目。我们不仅会完成基础的脚手架创建和依赖安装更会深入到每个环节的配置细节和原理比如如何优雅地配置 TypeScript 和路径别名如何实现 Element Plus 的自动按需导入以优化打包体积以及如何组织 Pinia 的模块化 store 来管理复杂的状态。目标是让你搭出来的项目不仅“能跑”而且“跑得好”具备良好的可维护性和扩展性可以直接作为你后续实际项目的模板。2. 技术栈选型与项目初始化2.1 为什么是这套组合在动手之前我们先简单聊聊为什么选择这套技术栈这有助于理解后续每个配置步骤背后的考量。Vue 3这已经是现代 Vue 开发的默认选择。其组合式 APIComposition API提供了更灵活的逻辑组织方式尤其是对于复杂组件代码的可读性和复用性比 Options API 强很多。响应式系统也重写了性能更好。Vite取代 Webpack 成为 Vue 官方的推荐构建工具。其基于原生 ES 模块的开发服务器启动速度极快热更新HMR几乎瞬间完成极大地提升了开发体验。虽然在一些超大型项目或特定场景下Webpack 的生态和深度定制能力仍有优势但对于绝大多数项目Vite 的体验是颠覆性的。TypeScript为 JavaScript 加上静态类型检查。在 Vue 3 中TypeScript 的支持是一等公民。它能帮助我们在编码阶段就发现潜在的错误提供更好的编辑器智能提示如 VSCode 的自动补全和跳转让代码更健壮尤其适合多人协作的中大型项目。PiniaVue 官方的状态管理库可以看作是 Vuex 的进化版。它的 API 设计更简洁完美支持组合式 API并且天然支持 TypeScript不需要复杂的类型定义。对于新项目Pinia 是比 Vuex 4 更推荐的选择。Element Plus基于 Vue 3 的桌面端组件库。它继承了 Element UI 的丰富组件和良好设计社区活跃文档齐全是快速搭建后台管理系统界面的利器。当然你也可以根据项目需求选择 Ant Design Vue、Naive UI 等优秀的组件库。这套组合可以说是目前 Vue 生态中兼顾开发效率、性能、类型安全和工程化水平的“黄金搭档”。2.2 使用 Vite 脚手架创建项目我们使用 Vite 官方提供的模板来创建项目这是最标准、最快捷的方式。打开终端进入你打算存放项目的目录。执行创建命令。这里我们使用npm create vitelatest它会引导你进行选择。npm create vitelatest my-vue-app执行后你会被提示输入项目名称这里我们直接用my-vue-app然后选择框架和变体。进行框架选择Select a framework:选择Vue。Select a variant:选择TypeScript。进入项目并安装依赖cd my-vue-app npm install启动开发服务器npm run dev如果一切顺利浏览器打开http://localhost:5173就能看到 Vue 的欢迎页面。Vite 的启动速度会非常快通常在一秒以内。注意npm create vitelatest命令会自动使用最新的 Vite 和 Vue 模板。你也可以通过npm create vitelatest my-vue-app -- --template vue-ts一条命令直接指定 Vue TypeScript 模板跳过交互选择。2.3 初始项目结构解析创建完成后项目结构大致如下my-vue-app/ ├── node_modules/ ├── public/ ├── src/ │ ├── assets/ │ ├── components/ │ ├── App.vue │ └── main.ts ├── index.html ├── package.json ├── tsconfig.json ├── tsconfig.node.json ├── vite.config.ts └── ...src/main.ts应用的入口文件在这里创建 Vue 应用实例。src/App.vue根组件。vite.config.tsVite 的配置文件我们后续的很多自定义配置都在这里进行。tsconfig.json和tsconfig.node.jsonTypeScript 的配置文件分别用于前端代码和 Vite 的 Node.js 环境配置。index.html应用的 HTML 入口。Vite 的一个特点是它把index.html放在了项目根目录并且可以像 PHP 那样使用 EJS 语法注入变量例如script typemodule src/src/main.ts/script。3. 核心依赖安装与基础配置3.1 安装 Pinia 和 Element Plus基础项目创建好后我们来安装核心的依赖Pinia 和 Element Plus。npm install pinia element-plus为了优化生产环境的打包体积我们还需要安装 Element Plus 的按需导入插件以及处理图标和样式的相关依赖。Element Plus 官方推荐使用unplugin-vue-components和unplugin-auto-import这两个 Vite 插件来实现自动导入。npm install -D unplugin-vue-components unplugin-auto-import element-plus/icons-vueunplugin-vue-components会自动按需导入你使用的 Vue 组件包括 Element Plus 的组件。unplugin-auto-import会自动按需导入 Vue、Vue Router、Pinia 等的组合式 API 函数如ref,computed,onMounted,useRouter,useStore等让你无需在每个文件中手动import。element-plus/icons-vueElement Plus 的图标库。3.2 配置 Vite (vite.config.ts)接下来是重头戏配置vite.config.ts文件。我们将在这里集成上述插件并配置一些常用的设置如路径别名。// vite.config.ts import { defineConfig } from vite import vue from vitejs/plugin-vue import path from path // 需要安装 types/node但通常 Vite 模板已包含 import AutoImport from unplugin-auto-import/vite import Components from unplugin-vue-components/vite import { ElementPlusResolver } from unplugin-vue-components/resolvers // https://vitejs.dev/config/ export default defineConfig({ plugins: [ vue(), // 自动导入 API AutoImport({ imports: [vue, vue-router, pinia], // 自动导入 vue, vue-router, pinia 的相关 API dts: src/auto-imports.d.ts, // 生成自动导入的 TypeScript 声明文件 resolvers: [ElementPlusResolver()], // 自动导入 Element Plus 相关函数如 ElMessage }), // 自动导入组件 Components({ resolvers: [ ElementPlusResolver(), // 自动导入 Element Plus 组件 ], dts: src/components.d.ts, // 生成自动导入的组件 TypeScript 声明文件 }), ], resolve: { alias: { : path.resolve(__dirname, src), // 设置 指向 src 目录 }, }, server: { port: 3000, // 设置开发服务器端口为 3000 open: true, // 启动后自动打开浏览器 cors: true, // 允许跨域 // 代理配置示例用于解决开发环境跨域问题 // proxy: { // /api: { // target: http://backend-api.com, // changeOrigin: true, // rewrite: (path) path.replace(/^\/api/, ), // }, // }, }, })配置解析与注意事项路径别名path.resolve(__dirname, src)将映射到项目的src目录。这需要在tsconfig.json中也进行相应配置才能让 TypeScript 识别。自动导入插件AutoImport会帮你自动引入ref,computed,onMounted,useRouter,storeToRefs等函数。dts选项会生成一个auto-imports.d.ts文件确保 TypeScript 类型正确。Components会自动识别你模板中使用的 Element Plus 组件如el-button并导入它们无需你在script setup里手动import { ElButton } from element-plus。同样dts选项生成类型声明文件。这两个插件极大地减少了样板代码是提升开发效率的神器。开发服务器配置server配置项可以自定义端口、代理等。上面的代理配置示例是一个常见模式将本地/api开头的请求转发到后端服务器解决开发时的跨域问题。使用时需要根据你的后端地址进行修改。3.3 配置 TypeScript (tsconfig.json)为了让路径别名在 TypeScript 中生效我们需要修改tsconfig.json。{ compilerOptions: { target: ES2020, useDefineForClassFields: true, lib: [ES2020, DOM, DOM.Iterable], module: ESNext, skipLibCheck: true, /* Bundler mode */ moduleResolution: bundler, allowImportingTsExtensions: true, resolveJsonModule: true, isolatedModules: true, noEmit: true, jsx: preserve, /* Linting */ strict: true, noUnusedLocals: true, noUnusedParameters: true, noFallthroughCasesInSwitch: true, // 新增或修改以下配置 baseUrl: ., // 基础路径 paths: { /*: [src/*] // 路径映射与 Vite 配置的 alias 对应 }, types: [element-plus/global] // 引入 Element Plus 的全局类型定义如果自动导入插件生成的类型文件不完整可能需要 }, include: [src/**/*.ts, src/**/*.d.ts, src/**/*.tsx, src/**/*.vue], references: [{ path: ./tsconfig.node.json }] }关键点baseUrl: .和paths: { /*: [src/*] }是让 TypeScript 编译器理解/components/HelloWorld等同于./src/components/HelloWorld的关键。types: [element-plus/global]这一行有时需要有时不需要。如果unplugin-vue-components生成的components.d.ts文件足够完整可以省略。如果遇到 Element Plus 组件类型报错可以加上试试。3.4 集成 Pinia 并创建应用实例现在我们来创建 Pinia 的 store 并将其集成到 Vue 应用中。创建 Pinia 实例并修改 main.ts// src/main.ts import { createApp } from vue import { createPinia } from pinia // 导入 createPinia import App from ./App.vue // 导入 Element Plus 的样式基础样式 import element-plus/dist/index.css // 如果只想导入某些组件的样式可以使用按需导入插件上面已配置这里无需手动导入 const app createApp(App) const pinia createPinia() // 创建 Pinia 实例 app.use(pinia) // 使用 Pinia app.mount(#app)这里我们全局引入了 Element Plus 的完整样式。由于我们配置了自动按需导入组件所以不需要再使用app.use(ElementPlus)来全局注册组件。样式全局引入是为了确保所有组件的样式都能被加载这是一种简单可靠的方式。如果你对打包体积有极致要求可以研究 Element Plus 按需导入样式的配置但过程稍显复杂。创建一个示例 Store 在src目录下创建stores文件夹然后创建一个counter.ts文件作为示例。// src/stores/counter.ts import { defineStore } from pinia import { ref, computed } from vue // 组合式 API // 使用组合式 API 风格定义 Store export const useCounterStore defineStore(counter, () { // 状态 const count ref(0) const name ref(Eduardo) // Getter (计算属性) const doubleCount computed(() count.value * 2) // Action (方法) function increment() { count.value } function reset() { count.value 0 } return { count, name, doubleCount, increment, reset } })这是 Pinia 的组合式 API 写法非常直观就像在写一个组合式函数。在组件中使用 Store 修改src/App.vue来测试我们的配置。!-- src/App.vue -- template div h1Vue 3 Vite TS Pinia Element Plus/h1 !-- 使用 Element Plus 组件 -- el-card template #header spanPinia Counter Store 测试/span /template pCount: {{ counter.count }}/p pDouble: {{ counter.doubleCount }}/p pName: {{ counter.name }}/p el-button typeprimary clickcounter.incrementIncrement/el-button el-button clickcounter.resetReset/el-button el-input v-modelcounter.name placeholder修改名字 stylewidth: 200px; margin-left: 10px;/el-input /el-card el-divider / !-- 测试自动导入的组件和图标 -- el-button typesuccess :iconCheck clickshowMessage成功按钮 (测试图标)/el-button /div /template script setup langts // 注意这里没有手动导入 ref, computed, onMounted它们会被 AutoImport 插件自动导入 // 也没有手动导入 ElButton, ElCard, ElInput, ElDivider它们会被 Components 插件自动导入 // 图标需要手动从 element-plus/icons-vue 导入或者也配置自动导入稍复杂 import { Check } from element-plus/icons-vue import { useCounterStore } from /stores/counter const counter useCounterStore() const showMessage () { // ElMessage 也会被 AutoImport 插件自动导入并挂载到全局 ElMessage.success(按钮点击成功) } // 测试生命周期钩子自动导入 onMounted(() { console.log(App mounted) }) /script style scoped /* 样式 */ /style关键点验证组件自动导入我们在模板中直接使用了el-button,el-card等没有在script setup里导入页面应该能正常渲染。API 自动导入我们在script中使用了onMounted和ElMessage没有手动导入。Pinia Store我们通过useCounterStore()获取 store 实例并可以访问其状态和动作。路径别名我们使用/stores/counter来导入 store 文件。现在运行npm run dev你应该能看到一个包含 Element Plus 样式组件的页面并且按钮和输入框的功能都正常。打开浏览器开发者工具的“网络”选项卡查看加载的 JS 文件你会发现 Element Plus 的组件是按需加载的而不是整个库被打包进来。4. 项目结构优化与工程化配置一个清晰的项目结构对于长期维护至关重要。下面是我推荐的一种结构你可以根据项目规模调整。4.1 推荐的目录结构src/ ├── api/ // 所有与后端交互的接口请求函数按模块组织 ├── assets/ // 静态资源如图片、字体、样式文件 │ └── styles/ // 全局样式、变量、混合等 ├── components/ // 公共组件 │ ├── common/ // 全局通用组件如按钮、弹窗封装 │ └── business/ // 业务相关公共组件 ├── composables/ // 组合式函数Vue 3 的 hooks ├── layouts/ // 布局组件如后台管理的主布局 ├── pages/ or views/ // 页面级组件由路由渲染 ├── router/ // 路由配置 ├── stores/ // Pinia 状态管理按模块划分 ├── types/ // TypeScript 类型定义文件 ├── utils/ // 工具函数库 ├── App.vue └── main.ts4.2 集成 Vue Router对于单页面应用路由是必不可少的。我们来安装和配置 Vue Router。安装 Vue Routernpm install vue-router4创建路由配置 在src/router目录下创建index.ts。// src/router/index.ts import { createRouter, createWebHistory, RouteRecordRaw } from vue-router import Home from /pages/Home.vue // 假设的页面组件 const routes: ArrayRouteRecordRaw [ { path: /, name: Home, component: Home, }, { path: /about, name: About, // 路由级代码分割生成单独的块 (about.[hash].js) component: () import(/pages/About.vue), }, ] const router createRouter({ history: createWebHistory(import.meta.env.BASE_URL), // 使用 HTML5 History 模式 routes, }) export default router创建示例页面组件 在src/pages下创建Home.vue和About.vue。在 main.ts 中使用路由// src/main.ts import { createApp } from vue import { createPinia } from pinia import App from ./App.vue import router from ./router // 导入路由 import element-plus/dist/index.css const app createApp(App) const pinia createPinia() app.use(pinia) app.use(router) // 使用路由 app.mount(#app)修改 App.vue 加入路由视图!-- src/App.vue -- template div idapp nav router-link to/Home/router-link | router-link to/aboutAbout/router-link /nav !-- 路由匹配的组件将渲染在这里 -- router-view / /div /template现在访问/和/about就能看到不同的页面了。由于我们配置了AutoImport在组件中使用useRouter()或useRoute()也无需手动导入。4.3 环境变量与模式配置Vite 使用.env文件来管理环境变量。这对于区分开发、测试、生产环境的 API 地址等配置非常有用。创建环境变量文件 在项目根目录创建.env所有环境共享的变量通常为空或放默认值。.env.development开发环境变量。.env.production生产环境变量。定义变量# .env.development VITE_APP_TITLEMy App (Dev) VITE_API_BASE_URL/api# .env.production VITE_APP_TITLEMy App VITE_API_BASE_URLhttps://api.myapp.com重要只有以VITE_开头的变量才会被 Vite 暴露给客户端代码。在代码中使用// 在 Vite 项目中通过 import.meta.env 访问 const apiBaseUrl import.meta.env.VITE_API_BASE_URL console.log(import.meta.env.VITE_APP_TITLE) // 在 vite.config.ts 中通过 process.env 或 loadEnv 访问配置打包命令package.json中的脚本默认已经区分了模式scripts: { dev: vite, // 默认使用 development 模式加载 .env.development build: vue-tsc vite build, // 默认使用 production 模式加载 .env.production preview: vite preview }你可以自定义模式例如创建一个测试环境scripts: { build:test: vue-tsc vite build --mode test }然后创建.env.test文件即可。4.4 样式与预处理器配置项目通常需要使用 CSS 预处理器如 Sass/SCSS 或 Less。Vite 内置了对它们的支持。安装 Sassnpm install -D sass使用 SCSS 在组件中可以直接使用style langscss。配置全局 SCSS 变量/混合 这是一个非常实用的技巧可以在所有组件中共享变量。在vite.config.ts中配置// vite.config.ts export default defineConfig({ // ... 其他配置 css: { preprocessorOptions: { scss: { additionalData: import /assets/styles/variables.scss;, // 全局注入变量文件 }, }, }, })然后在src/assets/styles/variables.scss中定义你的变量// variables.scss $primary-color: #409eff; $success-color: #67c23a; $border-radius: 4px;之后在任何组件的style langscss中都可以直接使用$primary-color等变量无需再次导入。5. 开发、构建与部署实战5.1 开发服务器与热更新运行npm run dev后Vite 会启动一个开发服务器。它的热更新HMR速度非常快几乎是实时的。但在一些复杂场景下你可能会遇到 HMR 失效的情况比如修改了 Pinia store 的定义。这时通常需要手动刷新页面。对于 CSS 和大部分 Vue 单文件组件的修改HMR 都能完美工作。5.2 代码质量工具集成ESLint Prettier为了保证代码风格一致和避免低级错误集成 ESLint 和 Prettier 是必要的。Vue 3 TypeScript 项目有比较成熟的配置方案。安装依赖npm install -D eslint eslint-plugin-vue typescript-eslint/parser typescript-eslint/eslint-plugin prettier eslint-config-prettier eslint-plugin-prettier创建配置文件.eslintrc.cjs(或 .eslintrc.js)module.exports { root: true, env: { node: true, browser: true, es2021: true, }, extends: [ eslint:recommended, plugin:vue/vue3-recommended, // Vue 3 规则 plugin:typescript-eslint/recommended, // TS 规则 plugin:prettier/recommended, // 集成 Prettier必须放在最后 ], parser: vue-eslint-parser, parserOptions: { parser: typescript-eslint/parser, ecmaVersion: latest, sourceType: module, }, rules: { // 可以在这里覆盖或添加自定义规则 vue/multi-word-component-names: off, // 关闭组件名必须多单词的规则 }, }.prettierrc{ semi: false, singleQuote: true, printWidth: 100, trailingComma: es5, tabWidth: 2 }配置 VSCode 安装 ESLint 和 Prettier 插件并在项目根目录创建.vscode/settings.json{ editor.codeActionsOnSave: { source.fixAll.eslint: true }, editor.formatOnSave: true, editor.defaultFormatter: esbenp.prettier-vscode, [vue]: { editor.defaultFormatter: esbenp.prettier-vscode }, [typescript]: { editor.defaultFormatter: esbenp.prettier-vscode } }这样保存文件时会自动用 ESLint 修复并用 Prettier 格式化。5.3 构建与优化运行npm run build会对项目进行打包。Vite 使用 Rollup 进行生产构建默认已经做了很多优化如代码分割、资源压缩等。分析构建产物 为了优化打包体积我们可以使用rollup-plugin-visualizer来分析。npm install -D rollup-plugin-visualizer在vite.config.ts中引入import { visualizer } from rollup-plugin-visualizer export default defineConfig({ plugins: [ // ... 其他插件 visualizer({ open: true, // 构建完成后自动打开分析报告页面 filename: dist/stats.html, // 分析文件输出位置 }), ], })构建后会生成一个stats.html文件用浏览器打开可以直观地看到每个模块的体积占比从而定位优化点。配置公共路径publicPath 如果你的项目不是部署在域名的根路径下例如https://example.com/my-app/需要在vite.config.ts中配置base选项。export default defineConfig({ base: /my-app/, // 对应上述例子 // ... })所有资源的路径都会自动加上这个前缀。5.4 部署注意事项Vite 构建生成的是纯粹的静态文件HTML, JS, CSS, 图片等可以部署到任何静态文件服务器或 CDN。本地预览构建结果 构建完成后可以使用npm run preview命令启动一个本地静态服务器来预览生产环境构建的结果检查是否有路径等问题。路由模式与 History API 如果你使用的是createWebHistory()即 HTML5 模式在部署到非根路径或某些静态服务器如 Nginx时需要配置 Fallback将所有找不到的路径重定向到index.html由前端路由处理。这是单页面应用的常见配置。Nginx 配置示例location / { try_files $uri $uri/ /index.html; }环境变量 确保生产环境的.env.production文件中的变量如 API 地址是正确的。这个文件不应该提交到代码仓库通常通过 CI/CD 流程或服务器环境变量注入。6. 常见问题排查与进阶技巧6.1 类型错误与声明文件问题1使用自动导入后ESLint 报错‘ElMessage’ is not defined或 TypeScript 报类型错误。解决确保unplugin-auto-import和unplugin-vue-components的dts选项已开启并且生成的auto-imports.d.ts和components.d.ts文件在tsconfig.json的include范围内。有时需要重启 TypeScript 语言服务在 VSCode 中执行CtrlShiftP-TypeScript: Restart TS Server。问题2在.ts或.vue文件中引入路径别名时VSCode 可以跳转但运行或构建时报错 “Cannot find module”。解决检查vite.config.ts和tsconfig.json中的别名配置是否一致且正确。确保tsconfig.json中的compilerOptions.paths配置正确并且baseUrl设置为.。有时需要重启 Vite 开发服务器。6.2 样式相关问题Element Plus 组件样式丢失或混乱。解决确保已全局引入样式在main.ts中import element-plus/dist/index.css。这是最稳妥的方式。检查样式引入顺序确保 Element Plus 的样式在你的自定义样式之前引入避免你的样式被覆盖。按需导入样式如果坚持按需导入样式需要使用unplugin-element-plus等插件配置相对复杂且可能在某些构建场景下出现问题新手不推荐。6.3 性能与打包优化技巧1组件库的按需导入我们已经通过unplugin-vue-components实现了 Element Plus 组件的按需导入这是优化打包体积的关键一步。你可以检查构建后的stats.html确认element-plus的 chunk 是否被拆分。技巧2路由懒加载在定义路由时使用() import(...)语法ViteRollup会自动进行代码分割将每个路由组件打包成独立的 chunk实现按需加载。技巧3谨慎使用第三方库在引入一个 npm 包前可以考虑一下是否有更轻量级的替代品是否只用到其中一小部分功能能否手动实现使用bundlephobia.com查看其体积。技巧4使用vite-plugin-compression进行 Gzip/Brotli 压缩在服务端配置压缩是标准做法但也可以在构建阶段预压缩静态资源减轻服务器压力。npm install -D vite-plugin-compression// vite.config.ts import viteCompression from vite-plugin-compression export default defineConfig({ plugins: [ // ... viteCompression({ algorithm: gzip, // 也可以使用 brotliCompress ext: .gz, }), ], })6.4 开发体验提升技巧配置路径别名智能提示在 VSCode 中即使tsconfig.json配置了别名有时在import时也没有智能提示。可以安装Path Intellisense插件并配置.vscode/settings.json{ path-intellisense.mappings: { : ${workspaceFolder}/src } }搭建这样一个项目骨架就像是给房子打好了地基和主体结构。后续的业务开发就是在这个坚实、高效的基础上添砖加瓦。每个配置的选择背后都权衡了开发效率、维护成本和运行时性能。希望这篇详细的指南能帮你跳过那些初期的坑快速搭建起一个属于你自己的、现代化的 Vue 3 开发环境。在实际开发中你可能会根据团队规范或特定需求调整某些配置但核心的思路和工具链是相通的。如果在搭建过程中遇到其他问题多查阅官方文档和社区讨论通常都能找到解决方案。