尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Vue3工程创建全流程:从create-vue脚手架到nginx部署实践

Vue3工程创建全流程:从create-vue脚手架到nginx部署实践 开头部分我最早接触Vue3工程创建的时候其实并没有太多可以抄作业的资料。那时候前端社区还在争论要不要上ViteVue3的生态还没完全铺开很多教程还是老一套的Webpack配置方式。两年多过去现在的Vue3工程创建已经完全不同了官方脚手架create-vue基本成为事实标准TypeScript默认开启Vite早就把DevServer速度拉满了。这篇文章不打算只讲敲哪个命令我想把从零创建Vue3工程的全过程拆开讲清楚包括工具链选型、交互式选项怎么选、目录结构怎么组织、请求层和状态管理怎么接入、工程化护栏怎么配、最后怎么部署到nginx。内容面向用过Vue2、刚切入Vue3的开发者也适合准备从零搭一个Vue3后台管理系统的团队参考。1. 为什么我劝你直接上官方脚手架而不是手动搭或者守着老模板先说结论创建Vue3工程这件事现在已经不需要自己操心了。但在实际问过不少开发者之后我发现很多人还是不太清楚create-vue、create vite、vue-cli之间的区别也不知道哪个方案最稳。这一节把这几个方案摊开来说清楚。1.1 三种建工程方式的真实差异vue-cli是Vue2时代的产物底层基于Webpack。虽然它后来也支持Vue3但用过的人都知道创建完工程后DevServer冷启动可能要等十几秒甚至几十秒热更新在稍微大一点的项目里也会有明显延迟。Webpack本质上是一层层递归地对模块做静态分析大型项目构建链路特别长再加上各种loader的配置维护成本不低。create-vite是Vite官方提供的通用脚手架它创建的工程不带Router、不带Pinia、不带ESLint等Vue生态的东西适合追求极简或者有特殊需求的场景。如果你只是想试一下Vite本身用它没问题但创建Vue3完整工程的话后续还是要手动补一堆依赖和配置不划算。create-vue是Vue官方针对Vue3提供的工程脚手架底层调用Vite进行构建同时把TypeScript、Vue Router、Pinia、Vitest、ESLint、Prettier等选项全部做成交互式选择。它解决的问题直击痛点创建出来的工程不仅构建速度快而且目录结构、TS配置、代码规范都是Vue官方团队长期维护验证过的最佳实践组合。我们团队在3.0版本重写后台系统的时候就是直接用create-vue生成的后面基本没有做结构性调整。如果你现在还在犹豫要不要把老工程从Webpack迁到Vite我的建议是新项目一律优先create-vue老项目迁移则单独评估不要为了追新而把正在稳定运行的系统推倒重来。1.2 为什么Vite能比Webpack快这么多这个问题的本质在于构建机制完全不同。Webpack在开发环境下必须从入口文件开始把所有模块打包成一个bundle再启动服务而Vite利用浏览器原生ESM的支持开发者请求哪个模块Vite才实时编译哪个模块。加上Vite预构建依赖用的是esbuildGo语言实现处理node_modules里的依赖包时不会因为项目膨胀而变慢。具体到体感上一个包含60多个页面路由的Vue3后台工程用Vite启动开发服务器的耗时大概是400到800毫秒页面切换秒开。这在以前Webpack时代是不可想象的。团队里新同学第一天接入项目基本不用浪费时间等待构建。1.3 一个反直觉的结论模板越多越难维护提到脚手架很多人会倾向去GitHub上找一个开源的Vue3后台管理模板因为模板通常自带权限系统、多级菜单、动态路由、大屏页面、表单设计器之类的功能看起来省事。但我的实际体验是开源模板的问题不在功能少而在功能太多、耦合太深。你要改一个请求超时时间可能得先摸清楚它的axios封装在哪里、环境变量走哪个配置、mock数据怎么拦截你要对UI框架降级或者换版本可能牵一发动全身。所以创建一个干净的Vue3工程等于把底子握在自己手里。模板可以作为参考但不建议当成项目的起点。自己创建工程省去后面无数debug时间。2. 开始之前Node版本和包管理器这两个坑先填平创建Vue3工程之前环境准备比想象中重要。很多人卡在安装报错上实际不是命令的问题而是Node版本或者包管理器的问题。这两块没有处理好后面每一步都可能出各种莫名其妙的错。2.1 Node版本怎么选不是越新越好但也不能太旧Vite 5以上的版本对Node版本有硬性要求package.json里通常写着node: ^18.0.0 || 20.0.0。我在Node 16环境里跑Vite 5就遇到过一个经典报错Error: require() of ES Module ... not supported说白了这个构建工具依赖的ESM特性在旧版Node里不完整。解决方案也很直接升级Node。现在新项目我基本推荐直接用Node 20 LTS如果是Node 22稳定版本也无妨。检查当前Node版本的命令随手就能敲node -v npm -v如果版本不满足建议用nvmNode Version Manager管理Node版本。nvm可以同时装多个Node版本随时切换比如开发环境用20某个老项目需要16的时候就立刻切回去。macOS和Linux下推荐用nvmWindows下可以用nvm-windows或者fnmfnm本身基于Rust实现切换速度比nvm还快一些。2.2 包管理器选型与切换源的实战细节npm、yarn、pnpm三个包管理器在Vue3工程里都能用但我的经验是优先用 pnpm。原因有两个第一pnpm的依赖管理方式是全局存储 硬链接 符号链接多个项目共享同一份依赖缓存磁盘占用明显少。第二pnpm默认不会扁平化node_modules而是把依赖按内容寻址的方式存放这样安装依赖更干净也不会出现幽灵依赖问题。实际切换仓库地址也要提前做。尤其在国内网络环境下npm官方源安装依赖经常卡在npm install等半天还可能失败。最省心的方式是直接把镜像仓库切到国内仓库npm config set registry https://registry.npmmirror.com这个操作会写入全局配置文件~/.npmrc以后所有npm命令都走国内镜像。如果不想改全局配置也可以在项目根目录的.npmrc里单独指定registryhttps://registry.npmmirror.compnpm的仓库源配置也是同理pnpm config set registry https://registry.npmmirror.com注意如果你用nvm管理Node切Node版本后registry会保持之前的配置不需要重新设置。踩过一次坑某次在CI环境里安装依赖时因为CI只装了npm、没装pnpm导致构建失败。后来我在package.json里加了packageManager: pnpm9.x.x字段配合Corepack的corepack enableCI里自动就会切到pnpm。这个字段在本地开发中也提醒团队不要混用包管理器。3. 实际操作create-vue交互式创建每一步该怎么选环境准备好之后进入核心操作环节。create-vue的交互式问答一共包含10来个选项很多第一次用的人会在这儿犹豫不决尤其是TypeScript、JSX、Pinia、Vitest这种到底该不该勾选对后面的工程结构影响很大。我逐个说清楚我的选法和理由。3.1 完整创建命令及每个交互选项的推荐值执行创建命令npm create vuelatest也可以借助npm的--传参方式省去交互输入不过第一次创建建议还是走一遍交互能让你对工程生成的东西有数。交互式问答涉及的主要选项如下表选项含义我的推荐Project name项目名称默认是 vue-project按实际项目命名比如 admin-webAdd TypeScript?是否添加类型系统是强烈建议Add JSX Support?是否支持JSX语法组件不习惯写JSX就选No需要的话可以后加Add Vue Router?是否集成路由单页应用绝大部分需要选YesAdd Pinia?是否集成状态管理需要跨组件共享状态就选YesAdd Vitest?是否集成单元测试框架团队有测试规范就选Yes小项目可NoAdd End-to-End Testing?是否集成E2E测试后台系统长期维护建议选Yes否则NoAdd ESLint?是否集成ESLint静态检查强烈建议选YesAdd Prettier?是否集成Prettier格式化强烈建议选YesAdd Vue DevTools 7?是否集成Vue官方浏览器调试工具选Yes调试体验好很多这里重点说TypeScript与ESLint这两个单选Yes的原因。TypeScript对Vue3工程的意义不只是类型安全这种抽象好处而是实打实地提升了协作效率。比如接口返回的列表数据以前在JavaScript里你不清楚某个字段有没有得反复console.log有了类型定义编辑器直接给出字段提示字段拼写错了编译阶段就报错。Vue3的组合式API和defineProps、defineEmits这些宏与TS结合得很好写起来不会觉得别扭。实际上Vue3官方文档里把TypeScript作为一等公民推荐整个类型的推导能力做得比Vue2时代好了不止一个档次。ESLint选Yes的原因更直接你不想在Code Review时为了这里多了一个分号那里用了双引号和同事争论。ESLint把这些规则自动化不符合规范根本提交不上去。我一般在TypeScript、Vue Router、Pinia、ESLint、Prettier这几个都选Yes其他根据项目需要决定。在开发后台管理系统这类中大型项目时Vitest和E2E也会打开。3.2 创建完成后的目录结构到底有什么门道创建完成后工程根目录大致长这样├── .vscode/ # 编辑器统一设置 ├── public/ # 纯静态资源不会被打包处理 ├── src/ │ ├── assets/ # 需要走构建管线的静态资源 │ ├── components/ # 公共组件 │ ├── router/ # 路由配置 │ ├── stores/ # Pinia 状态管理 │ ├── views/ # 页面级组件 │ ├── App.vue # 根组件 │ └── main.ts # 应用入口 ├── .gitignore ├── .npmrc # 项目级npm配置 ├── env.d.ts # 类型声明文件 ├── index.html # Vite的入口HTML文件 ├── package.json ├── tsconfig.json # TypeScript 配置总入口 ├── tsconfig.app.json # 应用代码的TS配置 └── vite.config.ts # Vite 配置说一下容易忽略的几个点。tsconfig.app.json和tsconfig.node.json是create-vue自动生成的分层配置。tsconfig.app.json管理 src 目录下的应用代码tsconfig.node.json管理 vite.config.ts 这种Node环境下的脚本文件。这样做的好处是不同类型代码的编译目标、模块解析策略不同混在一个配置里反而容易出问题。public目录和src/assets目录容易混。public里的文件会原封不动拷贝到构建输出目录引用时直接以根路径/开头比如/favicon.icosrc/assets下的文件都会被Webpack/Vite处理支持压缩、指纹、字体、图片的雪碧图之类。经验是logo文件、favicon、外链资源之类的放public组件里要用的小图片、自定义字体走src/assets。初始目录里的HelloWorld.vue和TheWelcome.vue这些示例组件删掉就好。它们解决的问题只有一个验证脚手架真的能跑。留着会干扰实际开发。3.3 启动项目前必须做的三个调整创建完、装完依赖默认工程虽然能启动但直接在这个基础上开发会有点别扭。我一般会先做三件事。第一重置默认样式。很多项目会引入normalize.css或者reset.css把浏览器默认的margin、padding、字体大小统一掉。我在工程里通常直接用一个新的全局样式文件src/styles/index.scss在里面做变量定义和基础样式重置。第二配置路径别名。默认的src路径在每个组件里写../../../utils这种简直痛苦。在vite.config.ts里加配置import { fileURLToPath, URL } from node:url import { defineConfig } from vite export default defineConfig({ resolve: { alias: { : fileURLToPath(new URL(./src, import.meta.url)) } } })配上之后组件里import request from /utils/request就优雅多了。注意这一步对应tsconfig.app.json里的paths也要同步修改不然TypeScript不认别名会一直报红。第三配置env.d.ts。如果后端接口请求、环境变量等要用到import.meta.env这种Vite暴露的对象建议在env.d.ts里加上类型声明编辑器才能有提示。4. 创建完工程的第一件事把请求层、路由和状态管理串起来脚手架生成的工程是一张白纸。真正开始写业务之前我会先把请求层、路由、状态管理这三根脊椎搭好否则后面每写一个页面都要回头补齐基础设施。4.1 环境变量按环境拆分不要相信改一处生效全环境项目一接入后端马上就会遇到不同环境对应不同API地址的问题本地开发用http://localhost:8080测试环境用https://test-api.example.com生产环境用https://api.example.com。如果把这个值硬编码在axios封装里每次部署都得改代码极其容易出事。Vite对多环境配置文件有明确的约定我习惯在根目录新建三个文件.env # 所有环境下都会加载的基础变量 .env.development # 开发环境 .env.production # 生产环境.env.development示例VITE_API_BASE_URLhttps://test-api.example.com VITE_APP_TITLE后台管理系统(测试环境).env.production示例VITE_API_BASE_URLhttps://api.example.com VITE_APP_TITLE后台管理系统变量名必须以VITE_开头Vite才会把这几个变量暴露到客户端代码中这是官方约定的安全边界防止服务端密钥之类的变量被不小心打进前端包。后面代码里通过import.meta.env.VITE_API_BASE_URL就能拿到。还需要在env.d.ts中做类型声明/// reference typesvite/client / interface ImportMetaEnv { readonly VITE_API_BASE_URL: string readonly VITE_APP_TITLE: string } interface ImportMeta { readonly env: ImportMetaEnv }好几个项目都遇到过一个隐蔽的坑改了.env文件之后热更新不会自动触发必须手动停掉DevServer再重新跑npm run dev这个变量才会重新加载。我自己就因为在环境变量文件里加了新变量但忘了重启导致后端请求地址一直走的旧值排查了半天。4.2 axios 封装拦截器里只放通用逻辑请求层一般基于axios封装一个可复用的实例。我们要做的不是把代码写得多花哨而是保证两点请求出去时能自动带上token返回回来时能统一处理错误码。src/utils/request.ts里一个比较通用的封装形式import axios from axios import { ElMessage } from element-plus const service axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 10000 }) // 请求拦截 service.interceptors.request.use( (config) { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }, (error) Promise.reject(error) ) // 响应拦截 service.interceptors.response.use( (response) { const res response.data // 这里根据后端约定的code字段做统一处理 if (res.code ! 0) { ElMessage.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res }, (error) { ElMessage.error(error.message || 网络异常) return Promise.reject(error) } ) export default service代码看着简单但有几个细节要说清楚响应拦截器里处理的是后端业务错误不是HTTP层错误。比如登录过期、无权限、参数校验失败这些本质上HTTP状态码可能都是200但业务code是不同的值。我们统一判断res.code错误提示也在这层一次性做完业务组件里就不用到处try...catch再弹message了。请求拦截器里处理的是鉴权信息。token从哪拿、过期了怎么跳登录页不同项目有差异。我习惯把token放在localStorage路由守卫里再统一判断有没有token没有就重定向到/login。为什么不在拦截器里塞一堆和业务强相关的东西比如某个页面特殊要传额外header某个接口不需要带token。强行在拦截器里写判断代码会越来越乱。更好的方式是通过axios配置文件或者请求参数里加一个自定义配置项比如config.headers.noAuth true然后在拦截器里根据这个字段跳过写token的逻辑。4.3 路由和Pinia的接入顺序路由的创建在Vue3里很简单只需要在src/router/index.ts里配置路由表import { createRouter, createWebHistory } from vue-router import { useUserStore } from /stores/user const router createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: /login, name: login, component: () import(/views/login/index.vue), meta: { title: 登录 } }, { path: /, component: () import(/layout/index.vue), redirect: /dashboard, children: [ { path: dashboard, name: dashboard, component: () import(/views/dashboard/index.vue), meta: { title: 工作台, requiresAuth: true } } ] } ] })路由懒加载是必须的这个很好理解不懒加载的话用户首屏进来会把所有页面组件全部拉下来项目页面一多首屏JS体积直接爆炸。component: () import(/views/xxx/index.vue)会把对应路由的组件拆成单独chunk只有访问到该路由时才加载。全局前置守卫的思路也很固定router.beforeEach((to) { const userStore useUserStore() if (to.meta.requiresAuth !userStore.token) { return { name: login, query: { redirect: to.fullPath } } } return true })这里有个容易忽略的细节useUserStore()一定要在router.beforeEach回调函数内部调用不能在模块顶部调用。因为createPinia的实例还没挂载到app上时直接调用会报getActivePinia()的错误。这个坑我在第一次写路由守卫时踩过一次报错信息还不太直观。Pinia的 store 定义则很贴近Vue3组合式API的风格import { ref } from vue import { defineStore } from pinia import { loginApi, getUserInfoApi } from /api/user export const useUserStore defineStore(user, () { const token ref(localStorage.getItem(token) || ) const userInfo refUserInfo() async function login(loginForm: LoginForm) { const data await loginApi(loginForm) token.value data.token localStorage.setItem(token, data.token) } async function getUserInfo() { userInfo.value await getUserInfoApi() } function logout() { token.value userInfo.value undefined localStorage.removeItem(token) } return { token, userInfo, login, getUserInfo, logout } })这种setup写法比旧版options模式直观很多。你把它理解成把一组响应式数据和操作方法聚在一起哪个组件要用就去store里拿基本就抓住了Pinia的本质。4.4 跑通一个从登录页到列表页的最小闭环搭完上面这些基础设施我建议不要马上写业务代码先按下面这个顺序跑通最小闭环验证系统各部分真的联通登录页调用loginApi成功拿到token存入Pinia和localStorage。路由跳转到首页/工作台触发全局路由守卫读到token放行。列表页在onMounted里调用listApi请求拦截器自动带上token后端校验通过返回数据。故意让token失效测试响应拦截器是否正确弹错和跳转。整个闭环里前端请求数据的链路是组件方法 - API模块 - axios instance - 拦截器 - 后端返回数据再原路返回。任何一个环节断了在这个最小闭环里都能立刻暴露出来好过写了几十个页面之后才回头排查为什么接口全部401。5. 工程化护栏ESLint、Prettier、husky 一次性配到位很多人觉得ESLint和Prettier是浪费时间配置的东西直到在联调阶段因为代码风格问题被队友连续review打回才开始重视。在这个部分我会讲清楚create-vue生成的ESLint和Prettier应该如何理解以及如何用husky把代码检查做成提交前的硬性门槛。5.1 ESLint 9 的扁平化配置与传统eslintrc的区别create-vue现在生成的ESLint配置已经是ESLint 9的扁平化格式在eslint.config.ts文件里定义不再是老的.eslintrc.cjs。这一点要注意网上很多教程还是旧的extends写法直接复制过来大概率不生效。Vue3工程的eslint.config.ts主要做的事情可以理解为三部分加上对JS、TS、Vue文件的默认规则集加上对Vue单文件组件的代码规范解析加上TypeScript的类型感知规则。我用create-vue生成的默认配置就够用不用自己去网上找一堆最佳实践配置堆上去。默认配置里已经包含了 vue 官方推荐的 essential 级别规则再配合Prettier把格式类问题收走基本就覆盖了日常需求。有的团队喜欢再加一些高阶规则比如禁止any、强制导入顺序、强制组件的name和文件名一致等这些可以等团队形成规范后慢慢加。一开始就把规则拉满会让新同学特别难受容易产生抵触情绪。5.2 Prettier 配置依赖默认值也是一种选择Prettier的理念是有主见但无需讨论的代码格式化工具。很多团队为了守住自己的代码风格会在.prettierrc.json里配一堆选项比如semi: false、singleQuote: true、printWidth: 100之类。我的建议是不要为了顺手去改这些配置如果你没有特别强烈的审美偏好直接保持create-vue默认生成的Prettier配置就很好。默认值本身经过了大部分项目的验证一致性才是格式化的真正价值具体是双引号还是单引号并没有那么重要。如果配置了editor.formatOnSave: true和默认格式化工具指向Prettier那么每次保存文件代码自动格式化团队所有成员风格一致{ editor.defaultFormatter: esbenp.prettier-vscode, editor.formatOnSave: true }5.3 husky lint-staged把检查卡在提交之前光有ESLint和Prettier还不够因为开发者可能不在编辑器里开保存自动格式化直接git commit就把代码推上去了。所以要在提交之前加一道关卡。新版husky的初始化比旧版简单很多npx husky init这个命令会在项目根目录生成.husky/文件夹并注册pre-commit钩子。然后安装并配置lint-staged让它只对暂存区的文件跑检查而不是每次commit全量检查整个项目后者在大型工程里慢到让人崩溃。package.json里添加配置{ lint-staged: { *.{ts,tsx,vue}: [eslint --fix, prettier --write], *.{js,jsx,json,md}: [prettier --write] } }.husky/pre-commit文件里写上npx lint-staged从此每次git commit只会针对本次改动的文件做格式化和代码检查有问题就拦截在提交前明显减少CI跑了十分钟发现ESLint报错的尴尬。注意如果团队里有人用Windows开发建议在提交信息里让lint-staged处理换行符问题不要把Windows默认的CRLF带进仓库。通常Prettier格式化后默认统一为LF能省去很多文件被误判为更名/删除的烦恼。有一个我实际碰到的坑在某个项目初始化husky时.husky/pre-commit没有执行权限导致提交不触发检查。macOS/Linux下可以给钩子加执行权限chmod x .husky/pre-commit这个问题在Windows下反而不容易出现但在Linux服务器集中部署CI时特别常见。6. 部署上线nginx 配置里的三个关键点以及 build 前的检查本地开发跑通、代码规范也都配置好之后最终目标是上线到服务器。Vue3工程构建出来的dist目录就是所有静态资源部署到nginx上比较简单但有不少细节会影响成败尤其是前端路由、静态资源路径、接口反向代理这几块。6.1 构建配置base路径和路由模式要一起考虑默认情况下npm run build生成的静态资源都是绝对路径比如/assets/index-xxxx.js。如果应用部署在域名根路径下没问题但如果部署在子路径下比如https://example.com/admin/就会出现找不到资源的问题。两种处置办法方法一保持相对路径修改vite.config.ts里的base: ./构建产物里的资源路径会变成相对路径对部署在任意子路径都更友好。方法二配合部署的实际路径用nginx的location /admin/做映射并设置base: /admin/。我更推荐第二个方法因为静态资源全用相对路径有时会遇到二级路由刷新后资源404的问题。把实际部署路径明确告诉Vite让它按绝对路径生成资源配合nginx逻辑更清晰。路由模式也需要一起决策。Vue Router默认的history模式createWebHistory没有URL中的#但要求服务器对所有前端路由都做回退到index.html的处理如果用了hash模式createWebHashHistory服务器压力小一些URL里带#会让后端获取URL参数时变得麻烦。后台管理系统如果更看重美观和标准URL建议用history模式并配好nginx回退。6.2 nginx 配置的通用写法与每一个指令的作用我给出一个生产环境常用的nginx站点配置直接把注释写在指令边上server { listen 80; server_name example.com; gzip on; gzip_min_length 1k; gzip_comp_level 5; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xmlrss image/svgxml; location / { root /var/www/admin/dist; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location ~* \.(js|css|png|jpg|jpeg|gif|svg|woff2?)$ { root /var/www/admin/dist; expires 30d; add_header Cache-Control public, no-transform; } }逐条解释为什么这么配try_files $uri $uri/ /index.html;是history模式路由能正常刷新的关键。用户直接访问https://example.com/dashboard或者刷新页面时nginx首先尝试找$uri这个文件如果不存在就尝试作为目录都找不到就回退到/index.html由前端路由接管。/api/做反向代理前端代码里的请求地址直接写/api/...nginx把请求转发到后端的http://127.0.0.1:8080这样解决了跨域问题。注意proxy_pass如果写成http://127.0.0.1:8080/带斜杠会把/api前缀去掉再转发如果不带斜杠则保留完整路径。这个细节极容易踩坑很多人发现部署后接口404多半就是这里的问题。静态资源做缓存JS、CSS、图片这类带哈希指纹的文件直接设置过期时间浏览器第二次访问时命中缓存明显提升加载速度。no-transform是防止CDN或代理擅自压缩、转化静态资源。6.3 build 前的检查清单部署前花10分钟检查这几项能避免不少上线事故vite.config.ts中base是否匹配实际部署路径。.env.production中的VITE_API_BASE_URL是否正确指向生产环境后端。后端接口是否已经允许生产环境域名跨域访问如果走nginx反代则通常不需要。控制台是否残留console.log或者debugger调试代码生产构建时部分项目会用插件删除。本地执行npm run build确认构建产物正常生成且没有TS类型报错或ESLint错误。把dist目录传到服务器先在服务器本机访问http://127.0.0.1验证服务和静态资源路径。把整个dist目录直接放到nginx配置的root路径下然后执行nginx -t检查配置语法通过后nginx -s reload让配置生效。如果浏览器打开还是404大概率是try_files没生效或者nginx进程没有reload成功。7. 我在实际创建和开发Vue3工程过程中踩过的几个高频坑这部分更像是闲聊但遇到的人不少。我捡三四个最常见的展开给你提前预警。7.1npm create vuelatest命令在旧终端里选不了选项如果你用Windows自带的cmd窗口跑交互式命令方向键上下选择时可能错乱。这不是create-vue的问题是cmd对ANSI转义序列支持不好。解决方式很简单用PowerShell、Windows Terminal或者VS Code集成终端来跑命令。macOS用户直接用系统自带Terminal或者iTerm2都没问题。7.2 修改了vite.config.ts之后热更新不生效Vite对vite.config.ts的修改会自动重启服务但偶尔有缓存残留的情况。比如改完别名或代理配置后DevServer没有真正重启成功。遇见的处理方式通常是CtrlC停掉服务再重新npm run dev。不要在那儿等它自己刷新大概率等不到。7.3 create-vue 生成的文件里.vscode/extensions.json的作用编辑器扩展统一在团队协作里很重要。create-vue 会自动生成.vscode/extensions.json里面声明了Vue官方推荐的扩展比如Vue.volar、dbaeumer.vscode-eslint、esbenp.prettier-vscode。VS Code打开项目时会弹出是否安装推荐的扩展建议全部安装。特别是Vue开发Volar是新的Vue3语言工具不是原来Vue2时代的Vetur。没有装VolarVue3单文件组件的类型推导、模板表达式补全基本等于摆设。7.4 关于Vue3嵌套iframe影响外层点击事件一个老生常谈的坑有时项目里需要嵌入第三方页面比如地图、报表、可视化面板会用到iframe。iframe天然是独立的文档上下文外部页面无法直接捕获iframe内部的事件。但更严重的问题是鼠标在iframe区域内交互时外层容器的click等事件完全接收不到。我的处理思路是如果能控制被嵌入的iframe页面用postMessage双向通信跨域也能用如果不能控制就在iframe外层加一个透明的遮罩层通过遮罩层捕获鼠标事件再手动触发外部交互。这个方法在业务中很常见并不管iframe内部是什么内容。7.5 Vue3 若依/JeecgBoot 这类低代码平台迁移时的TS报错后端框架通常配套了前端工程典型的有若依的RuoYi-Vue3和JeecgBoot的Vue3版本。遇到迁移或者从旧版升级到新版Vue3 TS的时候报错集中在组件参数类型不一致、Ref解包后类型不匹配、第三方UI组件缺少类型声明这几类。处理顺序一般是先升级依赖版本统一再补全局类型声明文件最后逐个解决报错。如果还在用defineComponent扩组件并出现defineComponent is not defined通常是语法解析配置没跟上检查vite.config.ts中的vitejs/plugin-vue和TSConfig的jsx配置。这些坑在我刚切Vue3的那一两年里基本都遇到过有的花了大半天才排查出来。希望你在动手创建Vue3工程之前先看到这些能少走不少弯路。
返回列表