ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Vue 项目打包部署与 Nginx 上线实战:路由、缓存与回滚

Vue 项目打包部署与 Nginx 上线实战:路由、缓存与回滚 1. Vue 项目打包部署的整体链路拆解很多人第一次把 Vue 项目往服务器上搬的时候都会经历这么一个阶段本地npm run dev跑得好好的页面丝滑热更新秒响应结果npm run build出来的东西丢到服务器上打开浏览器一片白控制台红字飘一片。这中间的落差不是因为你的代码写错了而是因为开发环境和生产环境根本就是两套完全不同的运行模型——一个是 Node 起的开发服务器在内存里编译、按需返回模块另一个是一堆静态文件被扔进 Nginx 的目录里等着浏览器自己去请求。搞清楚这条链路上每一步到底发生了什么比背十条 Nginx 配置命令有用得多。1.1 从源码到线上一次部署到底走了哪几步把整条链路拆开看其实只有四段本地构建、产物校验、文件传输、服务器接管。本地构建这一步Vite 和 webpack 做的事情本质上是一样的——把你的.vue单文件组件、.ts、.scss、静态资源全部编译、转换、压缩、打成一堆浏览器能直接跑的.js、.css、图片和字体。区别在实现方式和速度上Vite 生产构建走的是 Rollup冷启动构建通常比 webpack 快一截webpack 生态老、插件多配置可调空间大但构建一个中型项目动辄一两分钟起步。产物校验这一步最容易被跳过但它恰恰是省时间的关键。dist目录出来之后别急着传服务器先在本地起一个静态服务器跑一遍比如npx serve dist或者在 IDE 里用内置的静态预览。这一步能提前暴露 80% 的路径问题、路由问题、静态资源 404 问题。传到服务器上再发现你还得来回排错、重新上传一来一回十几分钟就没了。文件传输和服务器接管是最后两步。传输方式有scp、rsync、SFTP 工具、CI/CD 流水线自动发布各有适用场景。服务器接管则是 Nginx、Caddy、Node 服务或者对象存储决定了你的页面怎么被访问、怎么处理路由回退、怎么设置缓存。我自己的习惯是本地构建 本地静态预览 打一个 tar 包 scp上传 解压覆盖 reload Nginx。整个流程五条命令两分钟内搞定比任何可视化工具都快而且可复现、可脚本化。1.2 静态托管还是后端服务先想清楚你的项目属于哪一类不是所有 Vue 项目都适合直接丢静态服务器。判断标准很简单这个项目在运行期还需要 Node 吗绝大多数后台管理系统、官网、活动页、H5 都是纯静态的——构建完就是一堆 HTML/CSS/JS浏览器拿到就能跑接口全靠fetch/axios打后端 API。这类项目用 Nginx 托管是性价比最高的方案一台最低配的云服务器能扛住相当可观的并发因为静态文件几乎不吃 CPU瓶颈只在带宽和磁盘 IO。另一类是需要在服务器端渲染或者做接口聚合的比如 Nuxt、SSR 项目或者你想在同一个服务里顺手把/api转发到后端。这种就得跑一个 Node 进程用pm2或systemd守护前面再挂一层 Nginx 做反向代理。好处是灵活代价是内存占用和运维复杂度都上去了。还有一种介于两者之间的项目本身是静态的但团队希望部署过程标准化、环境一致那就用 Docker。把构建产物和 Nginx 一起打进镜像服务器上只跑容器谁都不用关心服务器上装了什么版本的 Nginx。这套方案在多环境、多项目共存的时候特别香但单项目小团队用起来就有点重。部署方式适合场景优点代价Nginx 静态托管后台系统、官网、H5资源占用低、配置简单、并发强需要自己维护服务器Node 进程托管SSR、接口聚合灵活、可处理动态逻辑内存占用高、需要进程守护容器化部署多环境、多项目环境一致、发布可回滚学习成本、镜像体积静态托管平台个人项目、演示站零运维、自动 HTTPS自定义能力受限选型的核心不是哪个更先进而是哪个跟你的团队能力、项目生命周期、预算匹配。给一个只有三个页面的官网配一套 K8s那不叫专业叫折腾。1.3 环境变量与多环境打包别等到上线才发现接口地址写死了我见过太多项目把接口地址硬编码在axios的baseURL里本地是http://localhost:3000上线之后忘了改页面能打开但所有请求全挂。这类问题完全可以在工程层面规避。Vue CLI 和 Vite 都支持基于.env文件的环境区分。Vite 用import.meta.env.VITE_XXXVue CLI 用process.env.VUE_APP_XXX前缀是强制的防止你把服务器上的敏感变量无意中打进前端产物。典型的文件布局是这样.env # 所有环境共享 .env.development # npm run dev 时加载 .env.staging # 预发布 .env.production # npm run build 时加载# .env.production VITE_API_BASEhttps://api.example.com VITE_APP_TITLE运营后台代码里统一走一层封装// src/utils/request.js import axios from axios const service axios.create({ baseURL: import.meta.env.VITE_API_BASE, timeout: 15000 }) service.interceptors.response.use( res res.data, err Promise.reject(err) ) export default service注意所有以VITE_或VUE_APP_开头的变量都会被打进最终的 JS 文件里任何人打开浏览器都能看到。Token 密钥、数据库密码这类东西绝对不能放进去前端没有秘密可言。多环境打包命令也可以分开写package.json里加一行build:staging: vite build --mode staging部署时按环境选命令避免手改配置出错。这一步做好了后面切环境、加环境都只是加一个文件的事。2. 打包前的关键配置与参数取舍部署出问题十次里有六次根因在打包配置上。配置不对服务器上的 Nginx 写得再漂亮也救不回来。这一节把最容易踩的几个配置点挨个说透。2.1 Vite 与 webpack 两套构建工具的配置要点先看 Vite。它的配置文件是vite.config.js或.ts核心就几个字段import { defineConfig } from vite import vue from vitejs/plugin-vue import path from path export default defineConfig({ base: /, build: { outDir: dist, assetsDir: assets, sourcemap: false, chunkSizeWarningLimit: 1500, rollupOptions: { output: { manualChunks: { vue-vendor: [vue, vue-router, pinia], ui-lib: [element-plus] } } } }, resolve: { alias: { : path.resolve(__dirname, src) } } })sourcemap生产环境建议关掉产物能小一大截同时避免源码暴露。manualChunks是拆包的关键把体积大且长期不动的依赖单独拆出来浏览器可以长期缓存它们改业务代码时只更新业务 chunk用户二次访问几乎不用重新下载第三方库。再看 Vue CLIwebpack那套const { defineConfig } require(vue/cli-service) module.exports defineConfig({ transpileDependencies: true, publicPath: /, outputDir: dist, assetsDir: static, productionSourceMap: false, configureWebpack: { performance: { hints: false }, optimization: { splitChunks: { chunks: all, cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: vendors, priority: -10 } } } } } })两者的base和publicPath是同一个东西的不同叫法作用都是告诉构建工具我的静态资源最终会被部署在哪个路径下。这个值填错就是白屏的头号元凶。2.2 publicPath 与 base 到底该怎么填这个参数的本质是一道除法浏览器请求资源的完整地址 部署路径 构建时写死的资源前缀。假设你的index.html里被写入了script src/assets/index-abc123.js而这个文件实际放在https://example.com/assets/index-abc123.js那就正常。但如果你的站点挂在子目录下比如https://example.com/admin/而文件实际在https://example.com/admin/assets/...浏览器却跑去请求https://example.com/assets/...结果必然是 404页面白屏。所以规则很清晰部署在域名根目录https://example.com/base填/部署在子目录https://example.com/admin/base填/admin/用相对路径部署产物放在哪都能跑base填./前两种是绝对路径第三种是相对路径。相对路径看着方便但它有个坑如果页面用的是 history 路由访问https://example.com/admin/user/list时浏览器会以当前路径为基准去解析相对资源请求变成https://example.com/admin/user/assets/...直接 404。所以 history 模式必须用绝对路径./只适合 hash 模式或者单层页面。提示base末尾的斜杠不能省。写/admin和写/admin/出来的资源路径完全不一样前者会拼成/adminassets/...找半天找不到问题。2.3 路由模式对服务器配置的决定性影响Vue Router 有两种主流模式createWebHashHistory和createWebHistory。hash 模式下 URL 长这样https://example.com/#/user/list。井号后面的内容浏览器不会发给服务器服务器永远只看到https://example.com/返回index.html前端自己解析路由。这种模式下服务器随便配甚至不需要任何特殊处理。history 模式下去掉了井号https://example.com/user/list。这时候用户刷新页面浏览器会真的向服务器请求/user/list这个路径。如果服务器上没有这个真实文件或目录Nginx 默认返回 404。解决办法就是在 Nginx 里加一条回退规则把所有找不到的路径都交回index.html让前端路由接管location / { try_files $uri $uri/ /index.html; }这行配置的意思是先找有没有同名文件再找有没有同名目录都没有就把index.html返回去。这是 history 模式的标配漏了它用户一刷新就 404。两种模式没有绝对优劣。hash 兼容性好、服务器零配置但 URL 里带井号部分场景比如微信分享、SEO不太好看。history 美观但需要服务器配合且构建时要留意base路径。新项目我一般直接用 history配好 Nginx 就一劳永逸了。2.4 构建产物体积优化与拆包策略产物越大首屏越慢尤其是网络条件一般的用户。优化前先看清体积分布Vite 可以用rollup-plugin-visualizerwebpack 用webpack-bundle-analyzer跑一次就能看到哪个包最肥。npm i -D rollup-plugin-visualizerimport { visualizer } from rollup-plugin-visualizer export default defineConfig({ plugins: [vue(), visualizer({ open: true, gzipSize: true })] })构建完会自动弹出页面方块越大代表体积越大。常见的几个「重灾区」UI 库整包引入。Element Plus、Ant Design Vue 这种如果没用按需引入随便就是几百 KB。图表库。ECharts 全量引入体积很可观改成按需引入能砍掉一半以上。工具库整包。lodash引lodash-es配合 tree-shaking或者直接按方法引入。大图片直接 import。几十 MB 的图片打进产物构建慢、加载慢应该放 CDN 或对象存储。按需引入 Element Plus 的配置大致是这样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: [ vue(), AutoImport({ resolvers: [ElementPlusResolver()] }), Components({ resolvers: [ElementPlusResolver()] }) ] })这套插件会在构建时自动引入你用到的组件和 API不需要手写 import体积也跟着瘦下来。实测一个中型后台从 2.3MB 降到 800KB 左右是常见的。拆包的另一个收益是缓存利用率。把vue、vue-router、pinia拆成一个 chunk把 UI 库拆成另一个业务代码拆成第三个。业务代码一天发三次第三方库半年不动一次用户只需要重复下载那几百 KB 的业务 chunk体验完全不一样。3. 服务器环境准备与 Nginx 部署实操配置对了接下来就是把产物安全送上去、让服务器稳定地吐出来。这一节按顺序走一遍完整流程命令都是可以直接抄的。3.1 服务器基础环境与目录规划先假设你拿到了一台全新的 Linux 服务器系统是 Ubuntu 22.04 或 CentOS 7/8 那一类。第一件事不是装软件是规划目录。我一般用这套/www/ ├── apps/ │ └── my-vue-app/ # 当前版本 │ ├── index.html │ ── assets/ ├── releases/ │ └── my-vue-app/ │ ├── 20250101-120000/ # 历史版本 │ ── 20250110-153000/ └── logs/ └── my-vue-app/apps放当前生效的版本releases存历史包用于回滚logs放日志。这套结构看着多余等你某次上线出问题需要三十秒内退回上一版的时候就明白它的价值了。创建目录和用户sudo mkdir -p /www/apps /www/releases /www/logs sudo chown -R $USER:$USER /www服务器上通常还需要这些基础工具# Ubuntu / Debian sudo apt update sudo apt install -y nginx unzip curl # CentOS / RHEL sudo yum install -y nginx unzip curl装完 Nginx 后确认服务状态sudo systemctl enable nginx sudo systemctl start nginx sudo systemctl status nginx浏览器访问服务器 IP看到 Nginx 默认欢迎页说明基础环境通了。这一步不通后面全都白搭先排查防火墙和安全组——很多云服务器的 80 端口在控制台的安全组里默认是关的本地curl通、外部访问不通八成就是这个原因。3.2 Nginx 站点配置逐行解读配置文件的放置位置各发行版不同Ubuntu 一般在/etc/nginx/sites-available/和/etc/nginx/sites-enabled/CentOS 在/etc/nginx/conf.d/。我习惯统一放conf.d加个my-vue-app.confserver { listen 80; server_name app.example.com; root /www/apps/my-vue-app; index index.html; access_log /www/logs/my-vue-app/access.log; error_log /www/logs/my-vue-app/error.log warn; location / { try_files $uri $uri/ /index.html; } location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot)$ { expires 30d; add_header Cache-Control public, max-age2592000, immutable; access_log off; } location /index.html { add_header Cache-Control no-cache, no-store, must-revalidate; } gzip on; gzip_min_length 1k; gzip_comp_level 6; gzip_types text/plain text/css application/javascript application/json image/svgxml; gzip_vary on; gzip_disable msie6; }逐块解释一下因为这几行每一行都有讲究。root指向产物目录index指定默认首页。try_files $uri $uri/ /index.html;是 history 路由的生命线前面说过不重复。静态资源那条location用正则匹配扩展名给带 hash 的文件名设了 30 天强缓存。Vite 和 webpack 产出的文件名里自带内容哈希比如index-a1b2c3d4.js内容一变文件名就变所以可以放心大胆地设长缓存。immutable告诉浏览器这个资源在有效期内绝对不会变连条件请求都省了。index.html单独拎出来设成不缓存。原因很直白HTML 是入口它里面引用了哪几个带哈希的 JS 文件是每次发版都可能变的。如果 HTML 被缓存住用户拿到的还是旧的 HTML里面引用的旧 JS 可能已经被删了页面直接白屏。这种「HTML 不缓存 静态资源长缓存」的组合是目前前端部署缓存策略的最优解。gzip 那几行是压缩开关。gzip_comp_level 6是压缩比和 CPU 消耗之间的平衡点调到 9 收益很小但 CPU 会明显上去调到 1 又压不干净。gzip_min_length 1k避免小文件压缩反而变大。gzip_types里没写text/html因为 Nginx 默认就会压缩 HTML写了反而会报重复定义的警告。3.3 文件上传的几种方式与实操命令本地构建完成后产物在dist目录里。上传方式按推荐度排序。方式一rsync 增量同步推荐rsync -avz --delete \ ./dist/ \ useryour-server:/www/apps/my-vue-app/-a保留权限和时间戳-v输出详情-z传输压缩--delete删除目标目录里源目录没有的文件——这个参数很重要因为带哈希的旧文件如果不清掉目录会越堆越大。rsync 只传变化的部分第二次发布通常几秒钟就完事。方式二scp 全量上传tar -czf dist.tar.gz -C dist . scp dist.tar.gz useryour-server:/tmp/ ssh useryour-server rm -rf /www/apps/my-vue-app/* tar -xzf /tmp/dist.tar.gz -C /www/apps/my-vue-app/打包上传的好处是文件完整性有保障缺点是全量传输产物大了就慢。方式三本地构建服务器只负责托管如果服务器配置够也可以把源码传上去在服务器上构建。但我不太推荐这种做法——服务器上要装 Node、装依赖、跑构建内存小的机器构建时容易 OOM而且每次发版都在生产机上跑npm install风险不小。发布完成后 reload 一下 Nginxsudo nginx -t sudo systemctl reload nginxnginx -t是语法检查先测再 reload避免配置写错导致服务起不来。这个习惯一定要养成尤其是在生产环境上。3.4 HTTPS 与 gzip 之外的几项优化HTTPS 现在基本是标配浏览器对 HTTP 页面会标记「不安全」接口调用也可能被限制。用 Lets Encrypt 的证书是免费且自动化的sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d app.example.comcertbot 会自动改写你的 Nginx 配置加上 443 监听、证书路径和 HTTP 到 HTTPS 的跳转并且注册一个定时任务自动续期。证书有效期 90 天自动续期省心。HTTPS 之外还有几项值得做的HTTP/2。在listen 443 ssl后面加上http2多路复用能明显改善首屏加载时的并发请求表现。现在 Nginx 新版本写法是listen 443 ssl;加独立的http2 on;具体看版本。Brotli 压缩。比 gzip 压得更狠一般能再省 15% 到 20%。需要额外编译模块Ubuntu 上有现成的包可以装。安全响应头。加上X-Content-Type-Options nosniff、X-Frame-Options SAMEORIGIN这类头能挡掉一部分常见的注入和点击劫持问题。访问日志按天切割。日志不切割跑几个月就是一个几 G 的文件排查问题时光是打开就够呛。用 logrotate 配一下保留 14 天就够。3.5 Docker 方式部署把环境一起打包带走如果你手上项目多、环境杂或者团队希望「本地跑通就等于线上跑通」Docker 值得投入。核心思路是多阶段构建第一阶段用 Node 镜像构建产物第二阶段只把产物拷进 Nginx 镜像最终镜像里没有 Node、没有源码、没有 node_modules体积能控制在 50MB 以内。# ---------- 构建阶段 ---------- FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --registryhttps://registry.npmmirror.com COPY . . RUN npm run build # ---------- 运行阶段 ---------- FROM nginx:1.25-alpine COPY --frombuilder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD [nginx, -g, daemon off;]配套的nginx.confserver { listen 80; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location ~* \.(js|css|png|jpg|svg|woff2?)$ { expires 30d; add_header Cache-Control public, max-age2592000, immutable; } gzip on; gzip_types text/css application/javascript application/json; }构建和运行docker build -t my-vue-app:1.0.0 . docker run -d --name my-vue-app -p 8080:80 --restartalways my-vue-app:1.0.0--restartalways保证服务器重启后容器自动拉起不用手动干预。要更新版本就构建一个新 tag停旧容器起新容器回滚就是把旧 tag 再跑一遍比文件覆盖式发布干净得多。注意.dockerignore一定要写把node_modules、dist、.git、.env.local排除掉否则构建上下文能到几百 MB构建慢不说还可能把本地敏感配置打进镜像。4. 上线自检、缓存策略与回滚方案部署完成不等于任务结束。真正让人睡不好觉的是「上线之后才发现有问题」。4.1 上线后必须走一遍的自检清单发版之后我会按这个顺序快速过一遍全程不超过三分钟打开首页看有没有白屏。白屏第一件事是开 DevTools 的 Network看 JS 是不是 404。直接刷新当前路由比如https://app.example.com/user/list看会不会 404。会的话就是try_files没配。强刷一次CtrlShiftR确认缓存策略生效新版本能拿到。登录一遍走一遍核心业务流确认接口地址正确、跨域没问题。看响应头curl -I https://app.example.com/index.html确认Cache-Control是no-cache静态资源的Cache-Control带了max-age。看 Nginx 错误日志tail -f /www/logs/my-vue-app/error.log有报错当场处理。这六步能覆盖九成以上的上线事故。养成习惯之后每次发布心里都有底。4.2 缓存策略踩过的坑缓存这事配对了是性能加速器配错了是故障放大器。我踩过的两次坑值得说一下。第一次是index.html被 CDN 缓存了。用户访问到的还是旧 HTML里面引用的 JS 文件名已经不存在页面白屏而且因为 CDN 缓存没过期怎么刷新都没用只能等缓存超时用户体验极差。教训是HTML 永远不缓存CDN 上也要针对 HTML 单独配置不缓存规则。第二次是静态资源没设immutable浏览器每次都用 If-None-Match 去问服务器「这个文件变了没」服务器返回 304。虽然 304 响应很小但请求数一多首屏加载还是能拖慢几百毫秒。加上immutable之后浏览器在缓存期内根本不发请求直接读本地。还有一点是 CDN 和源站的缓存要一致。源站设了 30 天CDN 设了 1 天实际生效的是 CDN 那边的 1 天回源频率比预期高很多。两边配置得对齐或者干脆让 CDN 完全遵循源站的Cache-Control。4.3 回滚方案出问题时的最后一道保险没有回滚方案的部署流程是不完整的。文件覆盖式发布的回滚很简单发布前先把当前版本备份到releases目录。#!/bin/bash set -e APPmy-vue-app DEPLOY_DIR/www/apps/$APP RELEASE_DIR/www/releases/$APP/$(date %Y%m%d-%H%M%S) # 1. 备份当前版本 if [ -d $DEPLOY_DIR ] [ $(ls -A $DEPLOY_DIR) ]; then mkdir -p $RELEASE_DIR cp -r $DEPLOY_DIR/. $RELEASE_DIR/ echo 已备份到 $RELEASE_DIR fi # 2. 同步新版本 rsync -avz --delete ./dist/ $DEPLOY_DIR/ # 3. 校验并 reload sudo nginx -t sudo systemctl reload nginx echo 发布完成$(date)回滚就是反向操作cp -r /www/releases/my-vue-app/20250101-120000/. /www/apps/my-vue-app/ sudo systemctl reload nginxDocker 方案更简单docker run直接指定旧 tag 就行容器本身就是不可变的版本快照。提示releases目录别无限堆积加一个清理逻辑保留最近 10 个版本就够再多就是占磁盘。5. 常见问题与排查技巧实录这一节是我这些年攒下来的问题清单按出现频率排序遇到问题可以直接对照着查。5.1 白屏、404 与样式异常的排查路径白屏控制台报 JS 404。九成是base/publicPath配错了。打开dist/index.html看一眼里面引用的路径和你实际部署的路径对不上改配置重新构建即可。白屏控制台报Unexpected token 。这个报错的意思是浏览器请求 JS 文件服务器返回的却是 HTML。通常是因为try_files把不存在的 JS 请求也回退到index.html了。检查一下静态文件是否真的上传完整或者 Nginx 的静态资源location优先级被覆盖了。刷新页面 404。history 模式没有配try_files。加上就好。样式全乱布局异常。这个情况在打包后比开发环境更容易出现原因通常有三个。一是 CSS 被打包工具重新排序了某些依赖加载顺序的样式失效解决办法是把关键样式显式import到入口文件里别依赖隐式顺序。二是第三方组件的样式没被按需引入插件识别出来比如 Element Plus 的一些弹层、抽屉组件样式丢失检查Components插件的resolvers配置是否完整覆盖。三是用了scoped但样式选择器写得太深打包压缩后被重写这种情况用:deep()显式穿透。图片、字体 404。检查是不是用了绝对路径引用public目录之外的资源或者路径里带了构建时的临时 hash。图片建议放public目录或用new URL(..., import.meta.url)的方式引用。页面能打开但接口全部跨域报错。开发环境有 Vite/webpack 的 proxy 挡着生产环境没有了。两种解法后端在响应头里加 CORS 允许你的域名或者 Nginx 加一层反向代理location /api/ { proxy_pass https://api.example.com/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }代理之后前端请求/api/xxx就是同源的跨域问题自然消失。注意proxy_pass末尾那个斜杠——带了斜杠/api/user会被代理成/user不带就是/api/user少一个斜杠路径就多一段这个坑很多人踩过。5.2 常见问题速查表现象最可能的原因快速定位方法解决方式打开就白屏base / publicPath 路径不对看 index.html 里的资源路径改成正确的部署路径刷新子路由 404未配置 history 回退直接访问子路由看状态码加try_files $uri $uri/ /index.htmlJS 返回 HTML静态文件缺失或被回退Network 看响应体内容检查上传完整性样式错乱CSS 顺序或按需引入问题对比开发与生产 DOM 的 class显式引入样式、用:deep()接口跨域生产无代理看控制台 CORS 报错后端配 CORS 或 Nginx 反代更新后还是旧页面HTML 被缓存看响应头 Cache-ControlHTML 设 no-cache静态资源每次重新下载未设长缓存看响应头是否有 max-age加 expires 和 immutable首次加载慢产物太大用 visualizer 分析体积拆包、按需引入、CDN部署后 502后端进程挂了或端口不通看 Nginx error.log检查上游服务状态部分用户访问异常CDN 节点缓存不一致换网络环境复现刷新 CDN 缓存、对齐策略5.3 几个能省下大量时间的实操心得用脚本代替手工操作。发布流程一旦写成 shell 脚本就再也不会有「忘了执行某一步」这种事。哪怕只有五条命令也值得写成脚本。构建日志要留痕。在index.html里注入一个版本标识比如meta namebuild-version content1.2.3-20250110出问题时打开控制台就知道用户当前跑的是哪一版比问用户「你刷新了吗」高效一百倍。先在预发布环境试一遍。预发布环境用和线上完全一致的 Nginx 配置、一致的域名结构只是指向不同的目录。所有配置改动先在预发布验证确认没问题再同步到线上。别在生产机上直接改文件。所有变更都走「本地改动 - 提交 - 发布」的流程生产机上手工改的每一行配置都会成为未来某次事故的伏笔——因为没人知道它是什么时候加的、为什么加。备份永远在下一次操作之前。这是我做了几年运维之后最深刻的体会。删目录、覆盖文件、reload 配置之前先备份、先nginx -t养成条件反射能避免绝大多数不可挽回的失误。给静态资源上 CDN。如果用户分布在全国各地把assets目录的内容推一份到 CDN源站只留index.html首屏加载能快上不少。做法是在构建时把base指向 CDN 域名产物上传到 CDNHTML 里引用的就是 CDN 地址了。注意发版时 CDN 和源站的更新要同步别出现 HTML 引用了还没上传到 CDN 的 JS 这种情况。整套流程跑通之后你会发现 Vue 项目的部署其实没那么玄乎——构建配置管好路径和拆包Nginx 管好路由回退和缓存脚本管好发布和回滚三件事各司其职。真正常见的故障基本都集中在路径和缓存这两块把这两块吃透剩下的就是熟练度问题了。
返回列表