博客SSR实践总结

由于百度半年都没有收录这个博客的缘故,很早就想尝试Vue的服务端渲染(Server Side Rendering)。上周末难得双休,决定实现这个功能,然后从vue-cli迁移到nuxt,以及后台从PHP迁移到NodeJS共花了两天时间,然后从Apache迁移到Nginx上又花了一天时间,一番折腾之后,有了下面的血泪史。

<!--more-->

项目传送门,参考:

1. SSR概念

这真的不是阴阳师里面的脱非入欧的那啥~

传统网站由后台提供数据,然后使用模板引擎渲染好页面,最后返回给客户端,因此浏览器初始拿到的就是一个完整的页面;用Vue搭的单页面应用,几乎所有的html代码都是在异步获得数据之后由JS渲染的,而浏览器初始拿到的页面只有一个孤零零的<div id="app"></div>根节点。

对于用户来说可能没啥感觉(由于页面体积极简可能速度更快),但是对于SEO来讲这就是灾难啊~解决这个问题,就必须要在服务端返回HTML文档。这仿佛进入了一个死胡同,幸运的是,大佬们为我们提供了Vue的SSR解决方案

文档里面有一句:

将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将静态标记"混合"为客户端上完全交互的应用程序

想到了之前使用过的一个叫做JUI的管理系统框架,不过实现完全不同~具体原理啥的后面再深入,需要注意的是,后台只能是 Node.js server环境(所以需要从PHP进行迁移)。

文档中推荐了一个Nuxt的框架,在Vue SSR解决方案的基础上进行进一步封装,帮助我们快速部署SSR的Vue项目。

2. 项目迁移

2.1. Nuxt

ShymeanV0.2.0的版本中使用的是vue-cli进行开发,最后将项目打包,由PHP托管和提供数据的。Nxut提供了与之类似的功能,包括了开发dev,打包build功能,与vue-cli不同的是,我们直接使用Nuxt内置的nodejs服务器托管项目。

安装啥的就不扯了(这里没遇见啥问题),下载完成之后运行npm run start,然后在浏览器输入localhost:3000预览即可。

当然通过浏览器我们发现不了SSR的效果,这里推荐使用Chrome应用Postman来进行调试。安装启动Postman,然后输入地址,就可以查看对应的响应报文了。

可以看见实际的HTML内容,是正儿八经的服务端渲染;如果采用相同的方式去爬去vue-cli的打包文件,得到的肯定只是一个根节点而已。

2.2. nuxt-express

nuxt的官方demo并没有提到如何处理数据的问题,应用肯定是需要调用服务器数据接口的,然而,端口号为3000的这个服务器是内置在nuxt框架中的,我们如何绑定请求路由和数据库操作呢?

nuxt文档中又推荐了nuxt-express来解决这个问题,OK知道用啥框架了,下一步就是项目迁移了。

2.3. 目录

nuxt为我们指定了项目的目录结构,文档里面有写,这里只是简单介绍一下

  • asstes,存放静态资源如图片,样式表,字体啥的
  • components,存放vue组件,感谢Vue组件系统,整个项目迁移成本非常低
  • layouts,布局文件,相当于vue-cli中的App.vue入口文件,这里还可以定制error.vue错误文件啥的
  • pages,nuxt通过该文件夹的目录结构自动生成对应的vue-router,规则见Routing
  • static,静态文件如favicon.icon
  • storevuex的相关对象如state,mutations
  • 所有组件的引入方式都推荐使用~的绝对路径形式,我发现貌似自己新建的目录引入的路径有点问题,暂时没有深入

上面这些是nuxt客户端相关的目录,与express后台相关的代码位于server目录

  • 已存在的index.js不需要我们更改,
  • 绑定路由需要在server/api/目录下进行,在组件中请求数据则需要带上/api/前缀
  • 可以在api的同级目录新建其他目录,如core服务器核心库文件,model数据库模型,config配置文件等

2.4. 配置

根目录下存在和两个配置文件,其中

  • nuxt.config.jsnuxt的配置,指定页面的meta标签,favicon图标,样式表啥的
  • backpack.config.jsbackpack的配置文件,这里暂时不需要进行修改

由于项目采用scss,因此记得在nuxt配置文件中添加scss入口文件,否则会报错

css: [
      {
          src: '~assets/scss/blog.scss',
          lang: "scss"
      },
      '~assets/fonts/iconfont.css'
  ],

2.5. 开发

在页面迁移的过程中,重新设计了部分组件和路由,对着文档在具体开发的时候倒没遇见啥大问题。

改动比较大的是后台,从PHP迁移到NodeJS需要修改接口,数据库方面暂时使用之前随便写的一个mysql模型类,由于之前设计的数据库很不合理,后面可能会进行调整。

前后端的网络请求使用的是axios,配合express用起来还是很简单的。

2.6. 开发问题

后台热更新

后台采用的backpack可以实现服务器热加载(跟supervisor类似),但是启动过程有些缓慢~不知道是不是电脑性能的问题。如果开发时发现接口不正常,记得检查后台服务器是否更新了。

修改文件名

整个项目在WebStorm开发,这里遇见了一个很大的坑:在修改文件名时,webstorm会自动检查项目中的资源引用并自动修改;在其他项目中这是一个很智能的功能,但是在nuxt-express中,比如我只是修改了pages目录下某个路由名称(把index.vue修改为home/index.vue),导致backpack.config.js中的入口文件被修改~整个项目都崩溃了,错误定位还很麻烦。因此建议在修改文件名称时取消自动检索功能。

接口路径

后台路由存放在/server/api目录中,接口统一以/api开头,因为前端和后台都使用3000接口,所以使用api前缀区分后台数据接口和前端路由,这里坑了两次,遇见404错误却发现后台路由貌似没毛病的时候,记得检查请求路径

vue拓展

nuxt在vue基础上拓展了几个常用的属性,使用方式见文档,注意在dev模式下,asyncData的catch错误是在服务端控制台显示的,调试的话请注意。

3. 项目部署

项目从vue-cli迁移完毕之后,就可以使用npm run buildnpm run start进行打包和部署了。先在VMware虚拟机ubuntu14.04 LTS进行模拟,然后再部署到阿里云,这个过程中遇见了无数的坑。

下面的步骤是我现在回忆的,可能会有一些问题,但大致方向是没错的~不要打我。使用的远程服务器连接工具是putty

3.1. node

由于后台使用了backpack,因此需要nodeV6.0以上的版本,之前的ubuntu下node版本太低了(V0.10)需要重新升级,但是发现使用n stable升级无效,最后重新卸载安装node然后就可以用了

apt-get remove nodejs
apt-get install nodejs
node -v

但是!!在阿里云上面使用apt-get,死活只能安装v0.10的版本,此外无法使用node -v的命令(貌似node在阿里云上面是另外一个程序)。无奈只能通过源码编译的方式进行安装

# 建议下载nodejs中文网上面的源码,不然速度太慢了~
wget https://npm.taobao.org/mirrors/node/v8.1.3/node-v8.1.3.tar.gz
# 解压
tar xvf node-v8.1.3.tar.gz
# 切换目录
cd node-v8.1.3

# 配置
./configure 
# 编译,这一步非常漫长,大概一二十分钟,可以玩把游戏
make
# 安装
make install

# 环境变量
cp /usr/local/bin/node /usr/sbin/ 
node -v

终于大功告成了~但是!我发现特么没有npm(不是说好node现在默认安装npm吗啊喂),然后只能使用curl进行安装

# 前面可能需要用PPA
curl http://npmjs.org/install.sh | sudo sh
# 更新npm
npm install -g npm

记得修改npm的镜像地址,不然下载比较坑

npm config set registry https://registry.npm.taobao.org 

在阿里云上装环境有点麻烦啊~好吧是我不熟悉。

3.2. nginx

但是本地服务器运行在3000端口上,因此需要使用反向代理。Apache的反向代理之前没有接触过,因此转向使用nginx做代理

安装

# 安装
sudo apt-get install nginx 

# 修改默认端口号 80->88 ,防止与apache2冲突,这一步不是必须的,反正我们后面还是要关闭apache
sudo vim /etc/nginx/sites-available/default

# 启动nginx
sudo service nginx start
# 查看状态,如果显示not running则可能需要关闭apache
sudo service nginx status

然后在浏览器访问localhost:88就可以看见ngnix欢迎界面了,由于我这边开的是vmware虚拟机,也可以通过ip地址访问虚拟主机

# 获取虚拟主机ip
ifconfig -a

由于需要频繁修改配置,记得在修改之后重新加载配置文件

nginx -s reload

反向代理

nginx的配置方式要比Apache简洁不少,找到nginx的安装目录

cd /etc/nginx/sites-enabled
vim default

然后添加一项server配置即可,这里指定的站点根目录位于/usr/share/nginx/ShyMean

server {
        listen 80;
        server_name www.ssr.com;
        root ShyMean;
        index index.html index.htm;

        location / {
                proxy_pass http://127.0.0.1:3000;
        }
}

转发请求头

由于需要统计访客人数,所以在后台记录用户ip(天地良心我真的不会干啥坏事),但是测试发现记录的都是127.0.0.1,这就蛋疼了。造成这个问题的原因是没有配置nginx转发请求头,修改上面的location配置

location / {  
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;  
    proxy_set_header X-Real-IP $remote_addr;  
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  
}  

这样就算完成了,总体来说nginx这里并没有遇见啥特别的问题。

3.3. mysql

由于之前是连接的远程数据库,部署到线上可能需要修改对应的数据库配置文件(位于server/config/db.json中,github上面的代码只有db.example.json,需要自己新建一个)。

3.4. 部署

环境搭好后,就可以编译文件然后开启服务器了。我还是先将代码传到github上面然后再克隆到/usr/share/nginx/ShyMean,等到学了shell后再尝试自动化部署。

# 安装时最好使用root,否则node-sass会报错
npm i

# 编译
npm run build

# 启动服务器
npm run start

接下来就可以进行测试了,因为是在虚拟机上运行的,所以需要修改windows的hosts文件

192.168.1.6     www.ssr.com

此时通过浏览器HTTP连接连接虚拟机,就可以看见3000端口的服务器展示的首页了。现在,我们的整个任务基本完成了~

3.5. forever

但!是!当关闭虚拟机或远程服务器时,整个进程就停止了~返回就会返回502错误,我们需要为nuxt开启守护进程,这里使用的是forever

# 全局安装
npm install forever -g

官方文档的介绍使用方法是

forever start app.js

由于nuxt的npm run start命令配置了cross-envNODE_ENV等环境变量,虽然使用--help命令找到了配置环境参数的配置-c,但仍旧不能使用上面的方法,百度查了半天没找到解决办法,最后用谷歌forever start npm scripts第一条搜索结果就找到了答案

forever start -c "npm run start" ./

呃~论科学上网的重要性。这样整个项目才算是正儿八经的完成了。

等等,最后还有一个坑:使用forever stop 0可以关闭守护进程,但是再次启动时整个守护进程不会生效,貌似是因此3000端口仍被占据的缘故

# 查看3000端口运行的进程
lsof -i:3000

果然是之前的node进程没有结束的缘故~找到对应的pid,比如这次是1002

kill 1002

然后重新使用forever start即可

3.6. ## 小结

这次部署真的是处处踩坑,庆幸的是每个问题都能找到解决方案。现在总算是完成了seo的第一步,也有了继续折腾博客的动力;此外对于Linux终于不那么敬畏了,打心底也比较喜欢命令行的操作模式,打算下半年买台mbp折腾,哈哈~~

《同构JavaScript应用开发》读书笔记 正儿八经地写JavaScript之代码格式