1. 同一个域名/端口下部署多个前端项目的核心挑战
在Web应用部署实践中,我们经常遇到这样的需求:需要在同一个域名和端口下托管多个独立的前端项目。比如企业门户网站(www.example.com)需要同时承载主站、管理后台、客户中心等多个子系统。传统做法是为每个项目分配不同子域名或端口,但这会带来诸多不便:
- 增加DNS解析复杂度
- 可能触发浏览器跨域限制
- 需要记忆多个访问地址
- 不利于统一管理SSL证书
Nginx作为高性能的Web服务器和反向代理,通过其强大的location匹配规则和rewrite能力,可以完美解决这个问题。下面通过一个典型场景说明实现方案:假设我们需要在example.com域名下同时部署主站(/)、后台管理系统(/admin)和移动端H5(/mobile)三个Vue/React项目。
关键提示:这种部署方式要求各项目路由模式必须采用hash模式(带#),否则浏览器直接访问深层路由时会触发404错误。如果必须使用history模式,需要额外配置fallback规则。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Nginx配置方案详解
2.1 基础目录结构与项目部署
推荐的项目目录结构如下:
code复制/var/www/
├── main/ # 主站项目
│ ├── dist/ # 构建输出目录
│ └── ...
├── admin/ # 后台项目
│ ├── dist/
│ └── ...
└── mobile/ # 移动端项目
├── dist/
└── ...
每个项目通过各自的构建命令(如npm run build)生成静态文件到对应的dist目录。此时基础的Nginx配置如下:
nginx复制server {
listen 80;
server_name example.com;
root /var/www/main/dist; # 默认主站
location / {
try_files $uri $uri/ /index.html;
}
location /admin {
alias /var/www/admin/dist;
try_files $uri $uri/ /admin/index.html;
}
location /mobile {
alias /var/www/mobile/dist;
try_files $uri $uri/ /mobile/index.html;
}
}
2.2 关键配置解析
-
alias与root的区别:
root会将location路径拼接到指定目录后查找文件alias会直接将location路径替换为指定目录- 对于子项目必须使用alias,否则Nginx会错误地拼接路径
-
try_files指令:
nginx复制try_files $uri $uri/ /mobile/index.html;表示依次尝试:
- 查找精确匹配的文件($uri)
- 查找目录索引($uri/)
- 最后fallback到/index.html(前端路由接管)
-
history模式特殊处理:
如果项目使用history模式,需要确保所有路径都返回index.html:nginx复制location /admin { alias /var/www/admin/dist; try_files $uri $uri/ /admin/index.html; # 处理history模式API请求 if ($request_uri ~* ^/admin/(.*)$) { rewrite ^/admin/(.*)$ /admin/index.html last; } }
2.3 高级优化配置
2.3.1 静态资源缓存策略
为提升性能,可对静态资源设置长期缓存:
nginx复制location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
# 防止项目间资源冲突
location ~ ^/admin/.*\.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
alias /var/www/admin/dist;
}
}
2.3.2 跨项目共享资源
如果有公共库需要共享,可以单独配置:
nginx复制location /shared {
alias /var/www/shared;
expires 1y;
}
各项目通过<script src="/shared/vue.js">引用
2.3.3 安全隔离措施
为防止项目间越权访问:
nginx复制location /admin {
# 禁止直接访问其他项目的资源
location ~ ^/admin/\.\./ {
return 403;
}
...
}
3. 实战中的疑难问题解决
3.1 静态资源404问题
现象:页面能打开但CSS/JS加载失败
原因:项目配置了错误的publicPath
解决方案:
- Vue项目在vue.config.js中设置:
js复制module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/admin/' // 必须与nginx的location匹配
: '/'
}
- React项目在package.json中配置:
json复制{
"homepage": "/admin"
}
3.2 路由冲突问题
现象:不同项目有相同路由路径
解决方案:
- 为每个项目添加路由前缀:
js复制// 以vue-router为例
const router = new VueRouter({
mode: 'history',
base: '/admin/', // 关键配置
routes: [...]
})
- 所有链接使用绝对路径:
html复制<!-- 错误 -->
<a href="/user">用户中心</a>
<!-- 正确 -->
<a href="/admin/user">用户中心</a>
3.3 代理API请求处理
当需要代理后端API时,避免路径冲突:
nginx复制location /api {
proxy_pass http://backend;
}
location /admin/api {
proxy_pass http://admin-backend;
# 重写路径去掉/admin前缀
rewrite ^/admin(/.*)$ $1 break;
}
4. 性能优化与监控
4.1 开启Gzip压缩
nginx复制gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_min_length 1k;
gzip_comp_level 6;
4.2 浏览器缓存策略
通过内容哈希实现精确缓存控制:
nginx复制location ~* \.(js|css)$ {
try_files $uri =404;
# 匹配带哈希的文件名如app.3a7b2c8e.js
if ($uri ~* \.[0-9a-f]{8}\.(js|css)$) {
expires max;
add_header Cache-Control "public, immutable";
}
}
4.3 访问日志分离
为每个项目创建独立日志:
nginx复制location /admin {
access_log /var/log/nginx/admin.access.log main;
...
}
location /mobile {
access_log /var/log/nginx/mobile.access.log main;
...
}
5. 容器化部署方案
对于使用Docker的环境,推荐以下部署结构:
code复制├── docker-compose.yml
├── nginx
│ ├── Dockerfile
│ └── nginx.conf
├── main
│ └── Dockerfile
├── admin
│ └── Dockerfile
└── mobile
└── Dockerfile
nginx.conf配置示例:
nginx复制server {
listen 80;
location / {
proxy_pass http://main-app;
}
location /admin {
proxy_pass http://admin-app;
}
}
docker-compose.yml片段:
yaml复制services:
nginx:
build: ./nginx
ports:
- "80:80"
depends_on:
- main-app
- admin-app
main-app:
build: ./main
expose:
- "3000"
admin-app:
build: ./admin
expose:
- "3000"
这种架构下,每个前端项目运行在独立的容器中,Nginx作为反向代理统一对外提供服务。
