第5章 运维和发布Vue.js项目
一般传统公司特别喜欢一个角色用一个人,如在生产流水线上,一个工人只负责拧螺丝,另一个工人只负责喷漆。很多软件公司也是这样,有人专门负责编写代码,有人专门负责运维。但是这样的弊端是:出了问题,程序员接触不到服务器,不知道问题出在哪里,运维可以接触服务器,却看不懂代码,也没有办法解决问题。所以传统公司往往怕出问题,因为解决起来特别慢。
现在,越来越多的人意识到,让程序员懂得运维知识特别重要。最好的运维就是程序员自己。在2011年,国外就开始流行一个词汇:DevOps(Developer + Operations),也叫作敏捷运维,就是对既懂编程又懂运维的人的称呼。
我们要有追求,做一个会运维的编程高手,做一个DevOps。
5.1 打包和部署
我们平时都是在本地做开发,每次打开的都是http://localhost:8080/#/,而在真实的项目中,需要把项目部署到某个地方,对项目进行打包和编译。
另外,在产品正式上线之前,也要在测试服务器上进行发布,这样才能发现一些平时在localhost上看不到的问题。
5.1.1 打包
直接使用下面的命令就可以把vue项目打包。
$ npm run build
该命令的运行过程如下:
可以看到:
(1)整个过程耗时18.658s。
(2)使用的Webpack版本是2.6.1。
(3)对CSS文件进行了优化(优化的比率是52.11%)。
(5)所有的.vue文件都被打包编译成了下面的文件。
$ find ./dist . ./static ./static/css ./static/css/app.32ddfe6eea5926f8e3c760d764fef3fa.css ./static/css/app.32ddfe6eea5926f8e3c760d764fef3fa.css.map ./static/js ./static/js/vendor.33c767135f1684f458a7.js.map ./static/js/app.d8b9f437c302a7070fe7.js.map ./static/js/manifest.75e2ba037e0bc6934514.js ./static/js/manifest.75e2ba037e0bc6934514.js.map ./static/js/app.d8b9f437c302a7070fe7.js ./static/js/vendor.33c767135f1684f458a7.js ./index.html
其中包括js、css、map和index.html。
我们需要将其放到HTTP服务器上,如nginx、apache。
5.1.2 部署
1. 上传代码到远程服务器
使用scp或ftp的方式,可以把代码上传到服务器。假设服务器是linux:
- 路径是/opt/app/test_vue;
- 服务器IP是123.255.255.33;
- 服务器ssh端口是6666;
- 服务器用户名是root。
$ scp -P 6666 -r dist [email protected]:/opt/app index.html 100% 528 0.5KB/s 00:00 app.32ddfe6eea5926f8e3c760d764fef3fa.css 100% 74 0.1KB/s 00:00 app.32ddfe6eea5926f8e3c760d764fef3fa.css.map 100% 623 0.6KB/s 00:00 vendor.33c767135f1684f458a7.js.map 100% 927KB 927.3KB/s 00:00 app.d8b9f437c302a7070fe7.js.map 100% 63KB 62.6KB/s 00:00 manifest.75e2ba037e0bc6934514.js 100% 1511 1.5KB/s 00:00 manifest.75e2ba037e0bc6934514.js.map 100% 14KB 14.3KB/s 00:00 app.d8b9f437c302a7070fe7.js 100% 9323 9.1KB/s 00:00 vendor.33c767135f1684f458a7.js 100% 119KB 118.7KB/s 00:00
这样就把本地的dist目录,上传到了远程的/opt/app目录上。
2. 配置远程服务器
(1)登录远程服务器。
$ ssh [email protected] -p 6666 (输入密码,回车)
Welcome to Ubuntu 14.04.4 LTS (GNU/Linux 3.13.0-86-generic x86_64)
[email protected]_server:~#
(2)把刚才上传的文件夹重命名为vue_demo。
# mv /opt/app/dist /opt/app/vue_demo
(3)配置nginx,使域名:vue_demo.siwei.me指向该位置。把下面的代码加入到nginx的配置文件中(/etc/nginx/nginx.conf):
server {
listen 80;
server_name vue_demo.siwei.me;
client_max_body_size 500m;
charset utf-8;
root /opt/app/vue_demo;
}
(4)重启nginx之前测试一下刚才加入的代码是否有问题。
# nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
(5)可以看到代码没问题,然后重启nginx:
# nginx -s stop # nginx
3. 修改域名配置
nginx运行之后,接下来就是配置域名,否则无法访问。我们需要增加一个二级域名:vue_demo.siwei.me。
不同的域名服务商提供的操作界面也不一样,根本思路就是增加A地址。我的域名服务商是dnspod,登录后,可以看到我的域名列表,如图5-1所示的域名为siwei.me的记录管理页面。
图5-1 域名为siwei.me的记录管理页面
单击“添加记录”按钮,就可以看到该域名的编辑页面,如图5-2所示的增加vue_demo二级域名的A记录。
图5-2 增加vue_demo二级域名的A记录
输入对应的二级域名(vue_demo),选择记录类型为A,记录值为siwei.me的主机IP地址,然后单击“保存”按钮。再回到命令行,输入ping命令。
$ ping vue_demo.siwei.me PING vue_demo.siwei.me (123.57.235.33) 56(84) bytes of data. 64 bytes from 123.57.235.33: icmp_seq=1 ttl=54 time=5.79 ms 64 bytes from 123.57.235.33: icmp_seq=2 ttl=54 time=6.38 ms 64 bytes from 123.57.235.33: icmp_seq=3 ttl=54 time=9.25 ms
上面的结果说明二级域名vue_demo.siwei.me已经可以正常指向到服务器123.57.235.33(这个就是我们本次demo的后端服务器的IP地址)了。
4. 完成部署
打开浏览器,访问http://vue_demo.siwei.me就可以看到结果了,如图5-3所示。
图5-3 项目可以通过vue_demo.siwei.me访问
5.2 解决域名问题与跨域问题
我们在部署之后会发现Vue.js遇到js的经典问题:远程服务器地址不对,或者跨域问题。还是以本书中的“4.6发送http请求”(显示博客的列表)为例。
真正的后台接口是http://siwei.me/interface/blogs/all,如图5-4所示。
图5-4 后台接口
5.2.1 域名404问题
(1)使用浏览器打开http://vue_demo.siwei.me/#/blogs页面,提示页面出错,如图5-5所示。
图5-5 页面出错
(2)可以看到出错的原因是404,打开http://vue_demo.siwei.me/api/interface/blogs/all页面,如图5-6所示接口地址返回404错误。
图5-6 接口地址返回404错误
(3)这个问题是由于源代码中访问/interface/blogs/all接口引起的。
在文件src/components/BlogList.vue中的第41行,定义了远程访问的URL,如图5-7所示。
this.$http.get('/api/interface/blogs/all')...
图5-7 代码中访问URL
这是因为在开发时,Vue.js会通过$npm run dev命令来运行“本地开发服务器”。这个服务器中有一个代理,可以把所有的以'/api'开头的请求,如:
localhost:8080/api/interface/blogs/all
转发到:
siwei.me/interface/blogs/all
“本地开发服务器”的配置如下:
proxyTable: {
'/api': {
target: 'http://siwei.me',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
},
所以,在开发环境下一切正常。但是在生产环境中发起请求时,就不存在“代理服务器”“开发服务器”(dev server)了,因此会出错。
5.2.2 跨域问题
跨域是js的经典问题。比如,有的读者在解决上面的问题时,会问:我们直接把图5-7中41行的:
this.$http.get('/api/interface/blogs/all')
改成:
this.$http.get('http://siwei.me/interface/blogs/all')
不就可以了吗?答案是否定的。
动手试一下我们就会发现,如果vue_demo.siwei.me直接访问siwei.me域名下的资源,就会报错。因为它们是两个不同的域名。
代码如:
this.$http.get('http://siwei.me/api/interface/blogs/all')...
就会得到报错:
XMLHttpRequest cannot load http://siwei.me/api/interface/blogs/all. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://vue_demo.siwei.me' is therefore not allowed access.
如图5-8所示的跨域问题的报错信息。
图5-8 跨域问题的报错信息
5.2.3 解决域名问题和跨域问题
其实,上面提到的两个问题的根源都是一个,也就是说,解决办法是相同的。
(1)在代码端,处理方式不变,访问/api+原接口url。(无变化)
this.$http.get('/api/interface/blogs/all')...
(2)在开发时继续保持Vue.js的代理存在。配置代码如下:(无变化)
proxyTable: {
'/api': {
target: 'http://siwei.me',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
},
(3)在nginx的配置文件中加入代理(详细说明参见代码中的注释,这个是新增的)。
也就是说,上面的配置把:
http://vue_demo.siwei.me/api/interface/blogs/all
在服务器端的nginx中做了变换,相当于访问了:
http://siwei.me/interface/blogs/all
重启nginx,就会发现已经生效了。恢复正常的文章列表页如图5-9所示。
图5-9 恢复正常的文章列表页
5.3 如何Debug
浏览器环境下的JavaScript,实际上有两个天生的缺陷。
- 不严谨。不同浏览器的js实现上会略有不同,这个问题在Android、iOS上也是一样的。
- 不是严格意义上的计算编程语言,有语法漏洞,如"=="。
因此,我们要想驾驭好JS语言,就要知道如何有效的Debug。
5.3.1 时刻留意本地开发服务器
开发时的命令如下:
$ npm run dev
会开启“本地开发服务器”,要时刻留意该服务器的后台输出。有时候我们把代码编写错了,导致Vue.js无法编译,浏览器页面就会一片空白,并且没有任何出错提示。
其实浏览器页面的一片空白,是由于Vue.js的代码无法运行导致的。此时服务器会报错,如图5-10所示的Vue.js本地开发服务器报错。
图5-10 Vue.js本地开发服务器报错
上面的错误提示很好理解,提示“编译时出现错误”,并给出了错误的详细位置。
5.3.2 看developer tools提出的日志
无论是chrome、safari还是firefox,以及IE 7+,都带有developer tools。任何时候页面空白了,都要先看它,而不是问别人:“页面怎么不动了?”
由于JS代码不是特别严谨,因此给出的错误提示也都很概括。我们可以做个对比:
- JSP错误可以精确到某行。
- PHP错误可以精确到某行。
- Rails错误可以精确到某行。
- Vue.js、Angular、Titanium等JS框架错误可以精确到某个文件。
这是由于所有的JS框架的表现层都是“框架怪胎”,是一种与js语言环境妥协的代码,出了问题很难定位到最底层的根源。而JSP、PHP、Rails ERB则是“正常框架”,出了问题可以直接找到最底层。
因此我们要有一定的经验Debug,来理解错误日志。如图5-11所示的Vue.js框架报错信息。
图5-11 Vue.js框架报错信息
vue.esm.js?65d7:434 [Vue warn]: Property or method "博客详情页" is not defined
on
the instance but referenced during render. Make sure to declare reactive data
properties in the data option.
found in
---> <Blog> at /workspace/test_vue_0613/src/components/Blog.vue
<App> at /workspace/test_vue_0613/src/App.vue
<Root>
- vue.esm.js?65d7:434表示错误的来源。这个文件一般人不知道来自哪里,我们暂且认为它来自临时产生的文件或虚拟js机。
- Property or method "博客详情页" is not defined ...这句话提示了错误的原因。
- found in ... <Blog> at ...这里则是调用栈,可以看出文件是从下调用到最上面的,问题出在最上面的文件,但是并没有给出错误的行数。
5.3.3 查看页面给出的错误提示
页面给出的错误提示如图5-12所示。
图5-12 本地开发服务器的报错信息
Error compiling template:
<div class='logo'> <img src='http://siweitech.b0.upaiyun.com//image/570/siwei.me_header.png'/> </div> <template> <script> </script> - Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead. - Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as <script>, as they will not be parsed. - tag <template> has no matching end tag.
这里Error compiling template:给出了提示,错误是在模板被编译时产生的。
下面给出的HTML代码是出错的点。
@ ./src/components/Logo.vue 6:2-177 @ ./~/babel-loader/lib!./~/vue- loader/lib/selector.js?type=script&index=0!./src/components/BlogList.vue @ ./src/components/BlogList.vue @ ./src/router/index.js @ ./src/main.js @ multi ./build/dev-client ./src/main.js
这里是调用栈,可以看到@./src/components/Logo.vue 6:2-177,错误在Logo.vue,第6行第2列。
5.4 基本命令
接下来要讲解的命令都是vue-cli提供的,可以认为是Webpack+Vue.js的结合。
5.4.1 建立新项目
$ vue init webpack my_blog
vue init命令会创建一个文件夹。具体的说明可参见3.6 Webpack下的Vue.js项目文件结构。
5.4.2 安装所有的第三方包
$ npm install
该命令是根据"package.json"文件中定义的内容来安装所有用到的第三方js库。所有的文件都会安装到"node_module"目录下。
还可以输入--verbose命令查看运行细节。
$ npm install --verbose
运行结果如下:
$ npm install --verbose npm info it worked if it ends with ok npm verb cli [ 'D:\\nodejs\\node.exe', npm verb cli 'D:\\nodejs\\node_modules\\npm\\bin\\npm-cli.js', npm verb cli 'install', npm verb cli '--verbose' ] npm info using [email protected] npm info using [email protected] npm verb npm-session d1e752145cbb60ba npm info lifecycle [email protected]~preinstall: [email protected] npm timing stage:loadCurrentTree Completed in 1609ms npm timing stage:loadIdealTree:cloneCurrentTree Completed in 12ms npm timing stage:loadIdealTree:loadShrinkwrap Completed in 681ms ...
这样,出现问题时就很容易知道“卡”在哪里了。
5.4.3 在本地运行
使用下列命令:
$ npm run dev
默认会在localhost的8080端口启动一个小型的Web服务器,性能可以完全满足开发使用,还具备代理转发等功能。
源代码发生改变时,服务器也会自动重启(偶尔需要手动重启)。
代码如下所示:
[email protected] MINGW64 /d/workspace/vue_js_lesson_demo (master) $ npm run dev > [email protected] dev D:\workspace\vue_js_lesson_demo > node build/dev-server.js [HPM] Proxy created: /api -> http://siwei.me [HPM] Proxy rewrite rule created: "^/api" ~> "" > Starting dev server... DONE Compiled successfully in 2373ms10:55:18
> Listening at http://localhost:8080
WAIT Compiling...11:02:14
DONE Compiled successfully in 213ms11:02:14
WAIT Compiling...11:06:09
DONE Compiled successfully in 117ms11:06:09
WAIT Compiling...11:06:26
DONE Compiled successfully in 103ms11:06:26 ...
5.4.4 打包编译
$ npm run build
该命令用于把src目录下的所有文件打包成Webpack的标准文件,供部署使用。具体内容可参见5.1打包和部署。