ARTICLE DETAIL

资讯详情

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

Vue项目部署到Linux服务器:Nginx配置、路由回退与自动化实战

Vue项目部署到Linux服务器:Nginx配置、路由回退与自动化实战 Vue项目打包部署到Linux服务器这件事听起来好像只是“把dist丢上去”但实际做下来你会发现它串联了打包配置、文件传输、Web服务器托管、路由回退、接口转发、缓存策略整整一条链路。我第一次独立部署时就因为少配一个history回退刷新页面直接404当时整个人是懵的。这篇文章我把从Vue打包到Linux服务器上线的完整流程按我的实操顺序整理出来。内容适合几类人刚接触前端部署、第一次买服务器想自己挂项目的开发者已经在用Nginx但总是遇到刷新404、跨域、白屏问题的同学还有准备把项目交给运维、却不知道自己在整个流程里该干什么的前端朋友。这篇文章不会只给命令我会把每个关键步骤“为什么这么干”也讲清楚。1. 部署方案选型为什么是Nginx 静态托管1.1 静态资源为什么不能“双击打开”一个最常见的误解是Vue打包出来的dist文件夹扔到服务器上就能被访问。但如果真有人试过在本地直接双击dist里的index.html大概率会看到一个白屏页面打开控制台会发现一堆资源加载失败。原因是Vue项目构建出来的东西本质上是一堆静态文件一个index.html以及一堆js、css、图片。这个index.html本身不包含业务逻辑它只是通过相对路径或绝对路径去加载js和css。浏览器打开本地文件时使用的是file://协议没有HTTP服务器来根据路径返回资源模块加载、路由跳转、接口请求全都无法正常工作。这也是部署的第一个核心认知静态资源本身不会自己变成可访问的网站它需要一个Web服务器来响应HTTP请求。这个服务器可以读到index.html也能把js、css等资源作为MIME类型正确的响应返回给浏览器。1.2 Nginx、Apache、Node.jsWeb服务器怎么选既然需要一个Web服务器那用什么常见选择有三个Nginx、Apache、Node.js自建服务。Nginx目前前端部署场景事实上的标准。处理静态文件性能高配置文件清晰反向代理能力强占用资源小。学习成本集中在location匹配和配置语法上一次学会到处能用。Apache老牌Web服务器配置偏重。如果项目本身没有历史包袱我不建议新项目从Apache开始。Node.js自建服务比如用Express写一个静态文件中间件来托管dist或者在项目里启动一个SSR服务。这种方式适合需要服务端渲染、需要自定义接口的场景。如果只是把一个纯前端SPA挂上去用Node自建属于重复造轮子还要考虑进程守护、端口管理这些额外问题。所以大多数Vue项目部署到Linux服务器的推荐方案就是Nginx托管静态文件 反向代理后端接口。1.3 一条请求到底经历了什么把整个请求链路说清楚后面排查问题会轻松很多。用户在浏览器输入域名或IP加端口后请求到达Linux服务器。如果这个请求是访问静态资源比如https://example.com/js/app.jsNginx就直接从服务器的磁盘上找到对应文件返回给浏览器。如果这个请求是访问后端接口比如https://example.com/api/userNginx会把请求转发给运行在服务器某个端口上的后端服务再把后端返回的数据透传给浏览器。理解这一点很重要前端资源和后端接口可以通过同一个域名访问由Nginx做按路径区分。这样做的直接好处是生产环境几乎没有跨域问题因为浏览器看到的请求始终是同源的。这也是为什么很多前端项目在本地开发时需要配置代理而上线后反而不需要前端代码里写完整的后端地址的原因。2. 服务器初始配置从SSH登录到Nginx跑起来2.1 获得服务器后的第一步登录与安全组服务器拿到手第一步不是装环境而是解决谁能登录的问题。云厂商提供的Linux服务器默认会有一个root密码首次登录建议立刻改成密钥登录。具体流程在本地生成密钥对ssh-keygen -t ed25519 -C 你的邮箱。把公钥追加到服务器的~/.ssh/authorized_keys文件里。修改/etc/ssh/sshd_config把PasswordAuthentication设为no重启SSH服务。这一步会直接影响到服务器安全性。很多人图省事一直用密码登录服务器放在公网上几天就会被各种脚本扫到每天几十上百次尝试登录是很正常的。改成密钥登录之后这种扫描基本失效。另外要注意云厂商的安全组。很多时候你在服务器里装好了Nginx本机curl能返回内容但浏览器访问不了第一件事就该去云控制台看安全组规则。比如我的腾讯云服务器默认只开放了22端口如果要在浏览器访问8080或80端口必须在安全组里加一条入站规则。2.2 系统基础环境与Nginx安装系统我以Ubuntu 22.04为例CentOS命令有些区别但思路一致。登录后的第一件事是更新系统软件包sudo apt update sudo apt upgrade -y然后安装Nginxsudo apt install nginx -yUbuntu用apt装的Nginx安装完成后会自动注册为系统服务。确认它的状态systemctl status nginx如果是active (running)说明已经起来了。如果没起来用systemctl start nginx启动再执行systemctl enable nginx设置开机自启。这里多说一句很多新手在这一步会去下载Nginx源码然后手动编译安装。除非你有模块定制的需求否则完全没必要。用系统的包管理器装Nginx后续升级维护都方便配置文件路径也规范。2.3 验证Nginx工作并打开防火墙端口装好后先在本机验证curl http://localhost:80如果返回了一段HTML里面有Welcome to nginx!字样说明Nginx本体没问题。接下来要确保防火墙和云安全组都放行了80端口。Ubuntu自带的ufw防火墙如果之前开启了要放行端口sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw allow 22/tcp如果服务器上还有其他服务比如后端跑在3000端口也要一并放行。但要注意后端端口不应该直接暴露到公网更合理的做法是让Nginx代理到它然后把3000端口仅允许服务器本机访问或者干脆不加入安全组。3. Vue打包细则publicPath决策与产物体检3.1 打包前先确认环境Node版本和依赖Vue项目打包前我会先在本地确认Node版本和项目依赖是否正常。node -v npm -vVue 2项目通常适用于Node 14到16Vue 3 Vite项目需要Node 16以上的版本。如果Node版本过低构建时会直接报错版本过高也可能遇到某些依赖不兼容。建议直接用nvm管理Node版本一个项目对应一个版本需要切换时非常方便。然后安装依赖npm install这一步看着简单但实际上有一半的构建问题都出在这里。如果项目里有lock文件package-lock.json或yarn.lock务必注意不要随意删除lock文件重新生成依赖树否则可能引入一些依赖版本差异导致打包结果和你同事本地不一样。3.2 publicPath与路由模式这份决策影响上线后的每一个URL打包之前必须确认两个配置publicPath和router mode。publicPath是Vue CLI项目中配置的静态资源基础路径。默认值是/表示打包后的js、css引用路径是/js/app.js这样的绝对路径。这种情况适合部署到域名根目录。如果项目要部署到服务器的一个子路径下比如https://example.com/myapp/就必须把publicPath改成/myapp/否则资源会去根目录找结果404。Vite项目对应的配置是base字段同理。另一个关键配置是路由的mode。Vue Router默认是hash模式URL里带#比如https://example.com/#/home。这种模式下刷新不会404因为#后面的内容不会发给服务器。但很多项目上线时会改成history模式URL更干净比如https://example.com/home。这时刷新页面浏览器会把/home这个路径发给Nginx而Nginx在服务器上找不到这个路径对应的物理文件就会返回404。这个问题的解法在第五章Nginx配置里但配置决策要在这里做如果要用history模式你不仅要改前端路由配置还必须同时改Nginx配置两条腿走路。3.3 执行构建与产物体检配置确认完后执行构建npm run build构建完成后项目目录下会生成一个dist文件夹。我习惯先给它做个“体检”打开dist/index.html看一眼里面引用的js、css路径是否符合预期是绝对路径/js/xxx.js还是相对路径./js/xxx.js。检查dist/js和dist/css目录下是否有对应的打包产物文件名通常带hash值比如app.8f3a2b.js。看是否有sourcemap文件。如果项目里开了sourcemapdist/js下除了js文件还会有.map文件。这些文件会让别人轻易看到源码结构生产环境建议关闭配置里把productionSourceMap设为false。体检之后推荐在本地先预览一下dist内容。可以用一个简单的静态服务器npx serve dist在本地浏览器访问提示的地址快速确认打包产物没有问题再上传服务器。这一步能省掉很多“传上去了才发现构建有问题”的返工。4. 上传部署实操rsync、目录规划与权限管理4.1 服务器目录规划别把项目丢在root家目录服务器上的项目存放位置建议遵循Linux惯例统一放在/var/www/下。比如sudo mkdir -p /var/www/your-project为什么要放在这里而不是放在/home/ubuntu/project或者/root/project原因有三点一是/var/www是Web资源的常规目录语义明确二是避免把项目文件放到用户家目录后面配置Nginx的root路径更清晰三是权限控制更规范Web服务需要读取的文件不该放在权限过高的用户目录里。目录建好后要把所有权交给你的部署用户。如果你平时用ubuntu或www-data用户操作就执行sudo chown -R $USER:$USER /var/www/your-project这样后面上传文件不需要老是加sudo也不会有文件所有权限混乱的问题。4.2 两种上传方式scp与rsync的取舍上传dist目录到服务器常见方式有三种方式优点缺点适用场景scp简单直接传输大文件慢不能断点续传一次性小文件传输rsync增量同步、压缩传输、断点续传命令参数稍多反复更新的项目部署宝塔面板等文件管理器图形化上传方便依赖面板环境不够纯粹新手快速部署如果只是部署一两次scp完全够用scp -r dist/* useryour-server-ip:/var/www/your-project/但如果是经常要更新的项目我更推荐rsync。它最大的优势是增量同步只传输有变化的文件不会每次都把整个dist重新传一遍。对于带有hash文件名的前端项目这个优势特别明显——你只需要更新改变的js、css和一个index.html其他没变化的文件全部跳过。rsync -avz --delete dist/ useryour-server-ip:/var/www/your-project/参数解释-a归档模式保留文件属性和权限。-v详细输出能看到哪些文件被同步了。-z传输时压缩对文本类资源效果明显。--delete删除目标目录里源目录没有的文件。这个参数要特别注意它能保证服务器上的文件和dist完全一致但如果你服务器上还有别的配置文件放在同一个目录会被删掉。所以不要把Nginx配置和其他额外文件混在项目目录里。4.3 文件权限与更新回滚流程文件上传后权限方面最容易踩坑。Nginx工作进程通常以www-data用户运行它需要有权读取静态文件。如果文件权限是700或者文件被放在一个完全没有执行权限的目录里Nginx访问时会报403。建议给项目目录和文件设置如下权限sudo chown -R www-data:www-data /var/www/your-project sudo chmod -R 755 /var/www/your-project如果让部署用户比如ubuntu也参与后续更新也可以让目录属主是ubuntu但目录权限保持755其他用户可读可执行Nginx也能正常访问。更新流程上我的习惯永远是先备份再覆盖cd /var/www cp -r your-project your-project-backup-$(date %Y%m%d%H%M%S)备份这一步看起来很笨但关键时刻能救命。特别是做前端项目的灰度发布或者临时回滚时你只需要把备份目录改回项目目录就行。没有备份的话一旦新包有问题你手头可能连旧版本都没有。5. Nginx核心配置搞定刷新404、跨域与缓存5.1 最小可用配置一个server块的点读Nginx的站点配置放在/etc/nginx/sites-available/下然后通过软链接到/etc/nginx/sites-enabled/启用。在CentOS上则通常是直接编辑/etc/nginx/conf.d/下的.conf文件。一个最基础的Vue静态站点配置长这样server { listen 80; server_name your-domain.com; root /var/www/your-project; index index.html; location / { try_files $uri $uri/ /index.html; } }逐行拆解listen 80监听80端口也就是HTTP默认端口。server_name域名。如果暂时没有域名也可以用服务器IP或者用_作为兜底匹配所有请求。root告诉Nginx去哪个目录找文件。这里填的就是你上传dist内容的目录路径。index访问根路径时默认加载的文件。location /匹配所有以/开头的请求。try_files $uri $uri/ /index.html这一行是Vue历史路由项目的灵魂。配置写完后先测试配置是否正确sudo nginx -t出现syntax is ok和test is successful就可以重载配置了sudo systemctl reload nginx5.2 刷新404的解决办法try_files原理如果你把路由模式改成了history但在Nginx里只配置了root和index没有写try_files那会出现一个让人很崩溃的问题从首页点进/home没问题但按F5刷新页面变成404。原因是这样的浏览器访问https://example.com/home时Nginx会在/var/www/your-project/目录下查找名为home的文件。这个文件显然不存在Nginx就直接返回404了。但前端SPA的逻辑是不管用户访问哪个路径服务器都返回同一个index.html然后由前端路由接管URL渲染对应页面。try_files $uri $uri/ /index.html;的作用就是先去磁盘上找这个路径对应的文件找到就返回找不到对应的文件或目录就回退到/index.html。这样刷新/home时Nginx返回的其实是index.htmlVue Router接手后解析/home路由页面就正常显示出来了。如果项目部署在子路径下比如https://example.com/myapp/对应的try_files要写成location /myapp/ { alias /var/www/your-project/; try_files $uri $uri/ /myapp/index.html; }这里用alias而不是root是另一个高频踩坑点。用root /var/www/your-project/配合location /myapp/Nginx会去找/var/www/your-project/myapp/index.html这明显不对。用alias才会让/myapp/这个路径直接映射到/var/www/your-project/目录。5.3 API反向代理顺手解决跨域前端项目调后端接口最常见的方式是在Nginx里做反向代理。配置如下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; }这样处理前端代码里的接口请求可以直接写相对路径/api/user浏览器发出的请求落到Nginx后被转发给本机的8080端口。proxy_pass末尾的斜杠有讲究http://127.0.0.1:8080/带斜杠时请求路径中匹配/api/的部分会被替换成/。比如请求/api/user实际后端收到的是/user。如果后端接口本身有/api前缀那proxy_pass就不该加后面的斜杠。同源策略下前端页面和接口请求都走同一个域名自然不存在跨域问题。我之前见过不少项目在开发时配了代理部署时反而把前端代码里的接口地址改成完整域名加端口然后继续在Nginx里配跨域头属于绕了远路。5.4 性能优化三件套gzip、缓存与HTTPS站点能跑通之后紧接着做三件事压缩、缓存、加密。gzip压缩js和css文件都是纯文本压缩率非常高。Nginx启用gzip可以显著减少传输体积。配置gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xmlrss image/svgxml; gzip_min_length 1k;开启后浏览器下载的js文件体积能减少60%以上。静态资源缓存前端打包后的文件名自带hash值内容变了文件名就变所以可以放心给静态资源设置长缓存。配置location /js/ { expires 30d; add_header Cache-Control public, immutable; } location /css/ { expires 30d; add_header Cache-Control public, immutable; }注意index.html不要设置长缓存否则用户更新版本后浏览器还是用旧的index.html加载的还是旧资源。常见做法是给index.html设置no-cachelocation /index.html { add_header Cache-Control no-cache, no-store; }HTTPS现在新项目直接上HTTPS已经是基本要求。用certbot申请免费证书sudo apt install certbot python3-certbot-nginx sudo certbot --nginx -d your-domain.comcertbot会自动修改Nginx配置加上证书和HTTP跳转HTTPS的规则这一步可以说是性价比最高的安全升级。6. 常见故障排查从日志定位到修复验证6.1 排查白屏的正确姿势按请求链路走部署后打开页面如果白屏很多人第一反应是“是不是Nginx配置错了”。但我的排查习惯是按请求链路逐步走先不要猜。第一步打开浏览器控制台的Network面板刷新页面看请求是成功了还是失败了。如果看到多个请求处于failed状态点开看具体URL基本能定位是资源路径问题还是服务器响应问题。第二步用curl直接在服务器上测curl -I http://localhost/ curl -I http://localhost/js/app.js看响应状态码和Content-Type。如果js文件的Content-Type是text/html那就说明Nginx没有找到静态文件而是返回了index.html这种通常是路径配置错误比如root路径拼多了或拼少了。第三步直接看日志tail -f /var/log/nginx/error.log6.2 路径拼接类问题root与alias的关键差异一个非常典型的错误配置里写了root /var/www/your-project;而dist内容直接放在了/var/www/your-project下面。此时请求/js/app.jsNginx实际查找的路径是/var/www/your-project/js/app.js没问题。但如果dist内容上传到了/var/www/your-project/dist目录而root还写的是/var/www/your-projectNginx就会去找/var/www/your-project/dist/js/app.js找不到就404。这种问题的特征是首页能打开但js和css全部404样式全丢页面白屏。这时候有两种改法要么传dist内容时直接传到项目根目录要么把root改成/var/www/your-project/dist。我的建议是第一种保持目录结构简单上传时直接同步到目标目录。需要注意的是try_files配合root时路径拼接逻辑是一样的。如果root指错位置即使写了try_files也没用因为根本找不到index.html。6.3 接口类问题502、504与跨域报错页面打开正常但数据加载不出来看Network里接口请求报502 Bad Gateway。502的含义是Nginx成功收到请求但转发后拿不到后端响应。常见原因有两种后端服务没启动或者proxy_pass指向的端口不对。先确认后端服务是否在运行ss -tlnp | grep 8080如果端口没有监听说明后端进程挂了去后端服务日志排查。如果端口有监听再用curl直接请求后端接口curl http://127.0.0.1:8080/api/user如果本机能通但浏览器通过Nginx访问时502大概率是Nginx配置里的proxy_pass地址写错了或者转发到域名/IP时网络不通。跨域报错在部署阶段反而不常见因为前端资源和接口都走同一个域名和端口。如果出现CORS错误优先检查是否是浏览器缓存了旧代码其次检查Nginx是否把请求错误地转发到了其他域名。6.4 看日志Nginx和前端自己的日志排查Nginx问题日志是最好的老师。错误日志默认在/var/log/nginx/error.log访问日志在/var/log/nginx/access.log。排查时建议先清空日志再复现sudo truncate -s 0 /var/log/nginx/error.log然后在浏览器复现问题再回来查看新增的错误日志。如果日志里有Permission denied说明文件权限有问题如果open() failed时路径多了一层或多了一层缺失就是root或alias配置不对。前端项目自己如果有sentry或其他前端日志监控部署后也要留意报错。很多白屏其实不是Nginx的问题而是Vue应用在运行时抛了异常比如某个接口返回的数据结构变了渲染组件时报错。这种情况Network里看到的请求都是成功的但页面就是白的控制台会有一堆报错信息。7. 从手工到自动化Docker与CI/CD的进阶方向7.1 为什么要上Docker手工部署跑通后你会感觉到一个痛点每次发布都要自己打包、上传、改配置一旦项目多了或者服务器需要迁移这套流程就会变得很繁琐。这时候就该考虑用Docker把Nginx和静态文件一起打包成镜像。好处很明显环境一致性解决了在本地跑通的配置在服务器上一样能跑通项目更新时不需要在服务器上手动装Nginx、改配置只需要拉新镜像重启容器服务器之间迁移也简单一个镜像到处跑。更重要的是Docker部署能把前面所有手工操作固化成一个Dockerfile一劳永逸。7.2 一个可直接参考的Dockerfile与compose配置以一个Vue项目为例前端构建用node镜像生产运行用nginx镜像两步构建# 构建阶段 FROM node:18-alpine AS build WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build # 运行阶段 FROM nginx:stable-alpine COPY --frombuild /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD [nginx, -g, daemon off;]这个Dockerfile里最终镜像只包含Nginx和dist文件体积很小部署速度非常快。配合docker-compose使用version: 3 services: frontend: build: . ports: - 80:80 restart: always在服务器上执行docker-compose up -d --build项目就起来了。下次更新时重复执行这一条命令就行不用再担心有没有装Nginx、有没有改对配置文件这类问题。如果需要一并部署后端服务可以在同一个compose文件里加上后端服务和数据库让整个项目一键启动。这也是当前中小型项目比较成熟的部署形态。7.3 再进一步CI/CD与多环境发布Docker解决的是环境问题CI/CD解决的是流程自动化问题。搭建一套简单的自动部署链路核心流程是代码推送到Git仓库触发流水线流水线里执行依赖安装、测试、构建然后通过SSH或Docker Registry把产物发布到服务器。GitHub Actions是一个很好上手的工具。一个基础的工作流文件大概是name: Deploy Frontend on: push: branches: [ main ] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - uses: actions/setup-nodev3 with: node-version: 18 - run: npm install - run: npm run build - name: Deploy to server uses: appleboy/scp-actionmaster with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.KEY }} source: dist/* target: /var/www/your-project配置好之后往后每次合并代码到main分支线上就自动更新了。这里要注意自动部署的前提是你前面的手工流程已经非常稳定。如果项目连手工部署都会出问题直接上自动化只会把问题放大。多环境发布上我习惯用环境变量区分。在Vue项目里配置.env.production和.env.staging打包时用--mode staging区分。服务器上则准备不同的目录和Nginx配置域名或端口不同互不干扰。这样测试环境、预发布环境、生产环境都有一套清晰的流程。7.4 我的个人体会部署这件事看起来只是前端开发流程的最后一步但它考察的是对整个发布链路完整性的理解。从publicPath到try_files从rsync到权限管理从手工上传到Docker自动化每过一道坎都会对“一个网站是怎么运行起来的”有更深一层的认识。我自己的感受是真正复杂的问题往往不是那些需要搜索引擎才能查到的冷门报错而是那些看似正确的细节路径多了一个斜杠、目录权限少了一个x、缓存配置让用户永远加载到旧版本。这些坑一次两次踩过之后最好把它们写进项目的部署文档里下次直接参考执行效率会高很多。如果你刚接触部署建议先用最简单的手工方式把流程完整走通一遍亲眼看到每一个环节的作用再考虑上Docker和CI/CD。自动化是给已经稳定运转的流程提速的不是用来弥补流程缺失的。先把手工流程走扎实后续升级到自动化部署会顺理成章得多。
返回列表