Vue+Koa2 前后端分离项目线上部署

Easter79
• 阅读 716

Vue+Koa2 前后端分离项目线上部署

昨天尝试部署一个 Vue+Koa2 的前后端分离项目,没想到因为前端项目部署的问题,卡了一整天,今天才终于找到了问题所在,成功解决。这篇文章主要谈谈:

  • 线上部署项目的相关事宜

  • 如何用 Nginx 实现同端口多项目部署

1. 项目结构说明

服务器上的项目结构大概是这样的:

`在 /home 路径下有两个如下的项目文件夹:

Vue-mall
MiniProgram-Admin
    |--client
        |--css
  |--js
  |--images
        |--index.html
    |--server
`

其中,Vue-mall 是之前部署在根目录下的项目,也就是输入域名后默认访问的项目,这个不用动它;而 MiniProgram-Admin 就是本次需要部署的项目,包括一个 client 前端项目文件夹和一个 server 后端项目文件夹,希望达到的效果是,输入域名 + /admin/ 后,可以访问这个项目。

2. 修改配置文件

之前的项目是直接部署在根目录下的,也即 Nginx 配置文件的  location / 下,所以不需要改动前端项目的配置文件,直接打包上传即可;但这次不是部署在根目录下,所以要修改两个地方:打包路径和路由配置

2.1 修改打包路径

默认情况下,Vue CLI 会假设你的应用是被部署在一个域名的根路径上,例如 https://www.my-app.com/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.my-app.com/my-app/,则设置 publicPath/my-app/

这个值也可以被设置为空字符串 ('') 或是相对路径 ('./'),这样所有的资源都会被链接为相对路径,这样打出来的包可以被部署在任意路径.

vue.config.js 文件下的 publicPath 项,在 1. 开发环境 或者 2.生产环境但部署在根目录的情况下,直接使用默认的 / 即可,不需要特意去配置;但在生产环境且不是部署在根目录的情况下,则需要额外进行配置。

具体的路径配置是任意的,根据你自己的需要选择 —— 因为我们希望输入域名 + /admin/ 后,就可以访问这个项目,所以配置了 /admin/ 路径。这里要注意,虽然路径可以任选,但是必须和后面 Nginx 的路径配置一致,否则会报错

publicPath: process.env.NODE_ENV === 'production' ? '/admin/' : '/',

2.2 修改路由配置

同样,需要配置一下路由:

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 文件看一下,大概是这样的:

`

              Vue Admin                                
            `

注意看这里的资源引用路径,都是以 /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 下的各个配置:

  • rootalias:这两个指令后面都跟着路径,路径指示了 对应项目的入口文件(通常是 index.html)的绝对路径,它们的区别是:

  • 因为要在同端口部署多项目,所以给根目录的项目使用的是 root  指令,而给非根目录的项目使用的则是 alias  指令。

  • 直接输入域名访问的时候,会根据 root 路径 + location 路径来寻找入口文件;输入域名 + /admin/ 访问的时候,会用整个 alias 路径去替换 location 路径,从而寻找入口文件

  • index:说是寻找入口文件,不过怎么指定入口文件是哪一个呢?通常 Vue-Cli 打包后生成的入口文件都是 index.html,所以这里对应的配置就是 index index.html index.htm;。记住,不需要携带路径,因为它仅仅是指明入口文件的名称,并根据该名称去寻找

  • try_files:前面的 rootaliasindex 都只是指明了路径,那么具体是用哪个指令来完成“寻找文件”这个动作的呢?就是 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
}))
`

需要设置的是 origincredentialsorigin  可以是函数或者字符串,指示可信任的域名。这里的话我准备了一个白名单,前端发送请求的时候会判断域名是否在白名单里,不在的话就拒绝此次请求。最后,默认返回的是本地开发用的端口。

需要改动的就是这里,之后直接把后端项目文件夹上传到服务器即可(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 端口,显示如下的页面就说明成功了:

Vue+Koa2 前后端分离项目线上部署

你的页面内容可能不是这个,具体看你给 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

Vue+Koa2 前后端分离项目线上部署

这里通过 --name 参数可以自己指定一个项目名字,后面的 run server 对应此前给后端项目配置的 npm 启动指令。

当然,也可以通过直接指定文件的方式启动 node 服务:

cd /home/MiniProgram-Admin/server pm2 start --name byFile app.js

Vue+Koa2 前后端分离项目线上部署

可以看到这里有两个开启同一个 node 服务的项目。下面是一些日常会用到的 pm2 指令:

监听文件改动,重启服务器:

pm2 start app.js --watch

终止某个项目:

pm2 stop 项目名

彻底删除某个项目:

pm2 delete 项目名

查看项目列表:

pm2 list

重启项目:

pm2 restart 项目名

当然,这个过程还可能会遇到端口冲突的问题,解决方法参考下面。

5. 错误说明

5.1 端口占用

在开启后端服务的时候,可能会遇到下面的报错:

Vue+Koa2 前后端分离项目线上部署

这是因为 3000 端口被占用了,所以我们先找出占用端口的进程:

netstat -tunlp|grep 3000

Vue+Koa2 前后端分离项目线上部署

可以看到,这里占用端口的是之前没有正确关闭的 node 进程,找到进程号 17821,直接 kill 即可:

kill -9 17821

5.2 无法访问端口

如果在访问 3000 端口的时候,页面显示无法连接,可能是因为服务器的安全组没有开放 3000 端口,去控制台看看,配置一下入口规则即可。

Vue+Koa2 前后端分离项目线上部署

5.3  Uuexpected token < / MIME 类型错误

Vue+Koa2 前后端分离项目线上部署

Vue+Koa2 前后端分离项目线上部署

这两个报错都是因为数据返回格式不对。前面我们在 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/ 的时候出现了问题,那么可以看一下错误日志:

Vue+Koa2 前后端分离项目线上部署

注意这句话: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

Vue+Koa2 前后端分离项目线上部署

留言板

本文分享自微信公众号 - 漫游前端世界(gh_6ac344b74a01)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k