
昨天尝试部署一个 Vue+Koa2 的先后端分离项目,没想到由于前端项目部署的问题,卡了一成天,今天才终于找到了问题所在,成功解决。这篇文章主要谈谈:css
-
线上部署项目的相关事宜 -
如何用 Nginx 实现同端口多项目部署
1. 项目结构说明
服务器上的项目结构大概是这样的:html
在 /home 路径下有两个以下的项目文件夹:
Vue-mall
MiniProgram-Admin
|--client
|--css
|--js
|--images
|--index.html
|--server
其中,Vue-mall 是以前部署在根目录下的项目,也就是输入域名后默认访问的项目,这个不用动它;而 MiniProgram-Admin 就是本次须要部署的项目,包括一个 client 前端项目文件夹和一个 server 后端项目文件夹,但愿达到的效果是,输入域名 + /admin/
后,能够访问这个项目。前端
2. 修改配置文件
以前的项目是直接部署在根目录下的,也即 Nginx 配置文件的 location /
下,因此不须要改动前端项目的配置文件,直接打包上传便可;但此次不是部署在根目录下,因此要修改两个地方:打包路径和路由配置vue
2.1 修改打包路径
默认状况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上,例如
https://www.my-app.com/
。若是应用被部署在一个子路径上,你就须要用这个选项指定这个子路径。例如,若是你的应用被部署在https://www.my-app.com/my-app/
,则设置publicPath
为/my-app/
。node这个值也能够被设置为空字符串 (
''
) 或是相对路径 ('./'
),这样全部的资源都会被连接为相对路径,这样打出来的包能够被部署在任意路径.nginx
vue.config.js
文件下的 publicPath
项,在 1. 开发环境 或者 2.生产环境但部署在根目录的状况下,直接使用默认的 /
便可,不须要特地去配置;但在生产环境且不是部署在根目录的状况下,则须要额外进行配置。web
具体的路径配置是任意的,根据你本身的须要选择 —— 由于咱们但愿输入域名 + /admin/
后,就能够访问这个项目,因此配置了 /admin/
路径。这里要注意,虽然路径能够任选,可是必须和后面 Nginx 的路径配置一致,不然会报错。chrome
publicPath: process.env.NODE_ENV === 'production' ? '/admin/' : '/',
2.2 修改路由配置
一样,须要配置一下路由:npm
const createRouter = () => new Router({
routes
mode: 'history',
base: process.env.NODE_ENV === 'production' ? '/admin' : '/',
})
这里必需要使用 history 模式,同时和上面同样区分好项目环境。后端
2.3 修改请求地址
以前都是本地开发,没有区分开发环境和生产环境下的请求地址,因此这里还得修改一下。
在项目的 src
文件夹下新建 config.js
文件,内容以下:
const host = process.env.NODE_ENV === 'production' ?
'http://mydomain.com:3000':'http://localhost:3000'
const config = {
host
}
export default config
以后在请求相关的 api.js
里再把 config
导入使用:
import config from '@/config'
const BASEURL = config.host
export function fetchData(){
return request({
method:'get',
url:`${BASEURL}/..../....`
})
}
这样就能够根据开发环境和生产环境向不一样的地址发送请求了。
3. 前端项目部署
3.1 文件结构一览
以后就能够 npm run build
打包了,打包后生成的静态资源结构是这样的:
dist
|--css
|--js
|--images
|--index.html
(固然,可能你的静态资源会出如今 static
文件夹里,这要看你是否配置了 assetsDir: 'static'
)
打开 index.html
文件看一下,大概是这样的:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1">
<meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<link rel=icon href=/admin/favicon.ico>
<title>Vue Admin</title>
<link href=/admin/css/app.6b18b5b0.css rel=preload as=style>
<link href=/admin/css/chunk-elementUI.43fc3011.css rel=preload as=style>
<link href=/admin/css/chunk-libs.5cf311f0.css rel=preload as=style>
<link href=/admin/js/app.8d30b2c2.js rel=preload as=script>
<link href=/admin/js/chunk-elementUI.cb459a4a.js rel=preload as=script>
<link href=/admin/js/chunk-libs.b0da50a4.js rel=preload as=script>
<link href=/admin/css/chunk-elementUI.43fc3011.css rel=stylesheet>
<link href=/admin/css/chunk-libs.5cf311f0.css rel=stylesheet>
<link href=/admin/css/app.6b18b5b0.css rel=stylesheet>
</head>
<body>
<noscript>
<strong>
We're sorry but Vue Admin Template doesn't work properly without JavaScript enabled. Please enable it to continue.
</strong>
</noscript>
<div id=app></div>
<script>............</script>
<script src=/admin/js/chunk-elementUI.cb459a4a.js></script>
<script src=/admin/js/chunk-libs.b0da50a4.js></script>
<script src=/admin/js/app.8d30b2c2.js></script>
</body>
</html>
注意看这里的资源引用路径,都是以 /admin/
开头的,后面跟上的是静态资源文件夹的名字。
可能你会在本地开个服务器看看效果,可是呢,这时候的页面必定会是空白的,毕竟资源引用路径不对嘛,本地并无 admin
文件夹。因此不用管本地预览效果了,直接上传到服务器便可。
3.2 上传文件并修改 Nginx 配置
我这里使用 MobaXterm (顺便安利一下,这软件挺全能的,惟一缺点就是有点卡)将文件上传到服务器,最后的结构就像文章开头那样:
在 /home 路径下有两个以下的项目文件夹:
Vue-mall
MiniProgram-Admin
|--client
|--css
|--js
|--images
|--index.html
接着就是最关键的地方了,配置 Nginx 的文件。你能够直接在 nginx.conf
文件配置,也能够引入单独的 .conf
文件。但不管如何,必定要弄清本身引入了哪些 .conf
文件,防止发生配置被覆盖的问题。
为了方便项目管理,我这里引入了额外的 MyProject.conf
文件,配置好以后的内容以下:
# MyProject.conf
server {
listen 80;
server_name localhost;
location / {
root /home/vue-mall/;
index index.html index.htm;
try_files $uri $uri/ @fallback;
}
location /admin/ {
alias /home/MiniProgram-Admin/client/;
try_files $uri $uri/ @fallback;
}
location @fallback {
root /home/vue-mall/;
index index.html index.htm;
}
}
下面详细介绍各个配置:
1. server
:
全部配置都是写在 server
模块下的,而且经过配置 listen 80
监听 80 端口,server_name
主要用于配置基于名称的虚拟主机,就本项目而言,能够不写
2. location
:
在 server
模块下有多个 location
块,一个 location
对应一个项目。能够看到,第一个块的路径是 /
,这决定了咱们输入域名后将访问该 location
块下的项目;同理,第二个块的路径是 admin
,因此输入域名 + /admin/
以后,访问的是如今部署的这个项目,注意,正如前面所说的,这里的 location 路径务必和以前前端项目配置的路径保持一致;第三个块是作重定向用的,稍后再解释
3. location
下的各个配置:
-
root
和alias
:这两个指令后面都跟着路径,路径指示了 对应项目的入口文件(一般是index.html
)的绝对路径,它们的区别是: -
由于要在同端口部署多项目,因此给根目录的项目使用的是 root
指令,而给非根目录的项目使用的则是alias
指令。 -
直接输入域名访问的时候,会根据 root 路径 + location 路径来寻找入口文件;输入域名 + /admin/
访问的时候,会用整个 alias 路径去替换 location 路径,从而寻找入口文件 -
index
:说是寻找入口文件,不过怎么指定入口文件是哪个呢?一般 Vue-Cli 打包后生成的入口文件都是index.html
,因此这里对应的配置就是index index.html index.htm;
。记住,不须要携带路径,由于它仅仅是指明入口文件的名称,并根据该名称去寻找 -
try_files
:前面的root
,alias
,index
都只是指明了路径,那么具体是用哪一个指令来完成“寻找文件”这个动做的呢?就是try_files
,顾名思义就是“尝试读取文件”,它做用的过程是这样的: -
就像咱们看到的,这个指令接受三个参数,其中 $uri
是用户访问的路径。假如用户输入域名 +/admin/
进行访问,那么$uri
就会等于/admin/
,就会去/admin/
下寻找资源,而/admin
又是被 alias 路径替换的,所以就会去 alias 路径下寻找资源,最后就找到了这个路径下面的index.html
入口文件。 -
若是按照这样找,找不到怎么办呢?那么就会用第二个选项 $uri/
尝试再次寻找,而若是仍是找不到呢,就只能使用备选的@fallback
啦,它表示重定向到这个fallback
指向的页面,而fallback
具体指向哪一个页面,咱们能够在下面经过location
块进行配置。
确保配置正确且成功引入以后,就能够重启 Nginx 了:
nginx -t // 测试配置是否经过
nginx -s reload
不出意外的话,经过域名 + /admin/
就能访问咱们的项目了。固然,有可能打开以后遇到页面空白的状况,这种状况基本就是配置错误了,须要回过头再仔细检查一遍各类路径的配置。
这样,前端项目就部署好了,接下来部署后端项目
4. 后端项目部署
4.1 修改文件
后端项目的部署就比较简单了,基本不须要额外的配置。这里主要是解决跨域问题,其实咱们用 Nginx 的话直接经过反向代理就能够解决跨域,但以前本地开发的时候,我是经过 koa2-cors
解决跨域的,所以仍是继续用这个方案吧,安装模块后,在app.js
使用,大体配置以下:
const cors = require('koa2-cors')
app.use(cors({
origin: function(ctx) {
const whiteList = ['http://mydomain.com'] //可跨域白名单
let currentUrl = ctx.request.origin
let changeUrl = currentUrl.substring(0,currentUrl.length - 5)
if(whiteList.includes(changeUrl)){
return changeUrl
}
return 'http://localhost:9528' // 开发环境用的,默认容许本地端口跨域
},
credentials: true
}))
须要设置的是 origin
和 credentials
。origin
能够是函数或者字符串,指示可信任的域名。这里的话我准备了一个白名单,前端发送请求的时候会判断域名是否在白名单里,不在的话就拒绝这次请求。最后,默认返回的是本地开发用的端口。
须要改动的就是这里,以后直接把后端项目文件夹上传到服务器便可(node_modules 就不要拖过去了,直接在服务器那边安装好),所以最后的结构是这样的:
在 /home 路径下有两个以下的项目文件夹:
Vue-mall
MiniProgram-Admin
|--client
|--css
|--js
|--images
|--index.html
|--server
|--app.js
|-- .........
同时,还须要再次修改 Nginx 的配置文件,在开头添加:
upstream koa.server {
server localhost:3000;
}
和本地同样,服务器监听的是 3000 端口。
4.2 安装依赖
接下来安装后端项目的依赖。但个人服务器并无 node 环境,因此还要安装一下 node:
apt install nodejs
同时安装 npm:
apt install npm
最后安装项目依赖:
cd /home/MiniProgram-Admin/server
npm install
安装速度可能很慢,换个源就行了。最后 npm run server
(也多是 npm run dev
,看本身的配置)开启后端服务,访问域名 + 3000 端口,显示以下的页面就说明成功了:

你的页面内容可能不是这个,具体看你给 ctx.body
返回什么。
4.3 Node常驻后台运行
最后还有一个问题,如今是经过 npm run server
开启后端服务的,一旦关闭终端或者断开 ssh 链接,后端服务就中止了。怎么才能让它常驻后台运行呢?
能够安装 forever 或者 pm2 解决这个问题,这里我用的是 pm2。
先安装 pm2:
npm install pm2 -g
安装完 pm2 -v
查看一下,确认安装正确,接着启动 node 服务:
cd /home/MiniProgram-Admin/server
pm2 start npm --name byNpm -- run server

这里经过 --name
参数能够本身指定一个项目名字,后面的 run server
对应此前给后端项目配置的 npm
启动指令。
固然,也能够经过直接指定文件的方式启动 node 服务:
cd /home/MiniProgram-Admin/server
pm2 start --name byFile app.js

能够看到这里有两个开启同一个 node 服务的项目。下面是一些平常会用到的 pm2 指令:
监听文件改动,重启服务器:
pm2 start app.js --watch
终止某个项目:
pm2 stop 项目名
完全删除某个项目:
pm2 delete 项目名
查看项目列表:
pm2 list
重启项目:
pm2 restart 项目名
固然,这个过程还可能会遇到端口冲突的问题,解决方法参考下面。
5. 错误说明
5.1 端口占用
在开启后端服务的时候,可能会遇到下面的报错:

这是由于 3000 端口被占用了,因此咱们先找出占用端口的进程:
netstat -tunlp|grep 3000

能够看到,这里占用端口的是以前没有正确关闭的 node 进程,找到进程号 17821,直接 kill 便可:
kill -9 17821
5.2 没法访问端口
若是在访问 3000 端口的时候,页面显示没法链接,多是由于服务器的安全组没有开放 3000 端口,去控制台看看,配置一下入口规则便可。

5.3 Uuexpected token < / MIME 类型错误


这两个报错都是由于数据返回格式不对。前面咱们在 Nginx 的文件里配置过 try_files
—— 若是找不到入口文件,就会使用 fallback,返回一个默认的 index.html
(或者是 404.html
),可是由于向服务端请求的是 css 和 js 文件,而且对于返回的资源也是按照 css 或者 js 去解析的,因此在遇到 html
文件的 <
时就会出现解析出错的问题。
5.4 排查方法
要学会多经过 network 和日志去进行排错。能够配置 Nginx 的文件,开启访问日志和错误日志,看看能不能从日志中找出什么问题。
在 nginx.conf
中配置:
# nginx.conf
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # 配置访问日志
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
.......
.....
}
在 MyProject.conf
中配置:
# MyProject.conf
upstream koa.server {
server localhost:3000;
}
server {
listen 80;
server_name localhost;
location / {
.......
}
location /admin/ {
.......
}
location @fallback {
.....
}
access_log /ect/nginx/logs/access_.log main; # 开启访问日志
error_log /etc/nginx/logs/error_.log error; # 开启错误日志
}
好比,在访问 location /MiniProgram-Admin/
的时候出现了问题,那么能够看一下错误日志:

注意这句话:rewrite or internal redirection cycle while internally redirecting to "/MiniProgram-Admin/client/index.html"
它所说的 internally redirecting
就是以前用 try_files
指令配置的重定向,而 cycle
指的是陷入了循环。那么为何会陷入循环呢?以前不是已经配置好了,若是找不到入口文件,就将 /MiniProgram-Admin/client/index.html
做为入口文件吗?惟一的解释就是这个路径自己就是错的,由于找不到这个路径下的 html
文件,因此又再次发生了重定向,最后陷入了循环。通过检查,确实是路径的问题,这里应该用绝对路径,前面少了一个 /home
。
固然,能够直接给个 404 的 fallback:
try_files $uri $uri/ =404;
6. 最后
以上就是本文的所有内容了。总的来讲仍是踩了很多坑的,并且也很差排查。尤为是静态资源引用错误的问题卡了一成天,后面才发现是 Nginx 的路径配置有问题。所幸最后算是都顺利解决了,更重要的是在这个过程当中学到了不少新的东西,感受仍是挺不错的。
参考:
https://cli.vuejs.org/zh/config/#publicpath https://juejin.im/post/5ee59b0af265da76c4245e39 https://www.nginx.cn/doc/standard/httpcore.html
本文分享自微信公众号 - 漫游前端世界(gh_6ac344b74a01)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。