技术上很多人跟我一样都是赶时髦的小伙子,但是也容易sao!断!腿!
有一天你架构好了前后端分离的框架,项目分离、ip分离、职能分离,环境搭好、git仓库搭好、接口完成、页面完成、联 调完成,仅仅只是做个官网(天哪)。准备上线的时候上面告诉你,要严格考虑SEO,哦吼。。。
严格考虑SEO知道意味着什么么?简单来说你的页面不能有初始化的ajax、甚至不能有js渲染页面,也就是说不能动态加载页面。在不分离的时期后 端初始化页面,完全不存在问题,前后端纯粹分离时期这就是一个棘手的问题。怎么解决?我已经趟好坑了!
在不增加前后端工作量、前后端完全分离的基础上解决方案有两个:预渲染与SSR。这两个概念百度一下,我的项目较小所以选择“预渲染”方案。预渲染其实就是在服务器上定时跑一遍页面,并且加载好动态渲染的结构后生成一个新的页面,然后让用户直接访问这个渲染后的页面。
这样的想法很不错,面临两道坎:1、shell脚本 2、使用无头浏览器读取动态内容。“无头浏览器”就是在命令行执行的浏览器,一个没有界面的浏览器。
步骤一、搭建前后端项目
1、假设你的服务器域名为x.com。将8000端口开放给前端,8888端口开放给后端。假设前后端项目分别为front和api。
2、front项目中将会通过定时脚本建立./dist目录,假设该定时脚本在预选染服务目录/web/preRender/shell/prerender.sh。该目录将会自动建立预渲染后的html目录,以及完全复制项目所需的依赖目录、文件。然后将80端口指向该目录,nginx配置如下
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /web/front/dist;
charset utf-8;
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
在前后端以及预选染服务项目各自的代码仓库的hooks下的post-receive中添加执行脚本命令
echo 'sh /web/preRender/shell/prerender.sh' >> /home/git/front.git/hooks/post-receive
echo 'sh /web/preRender/shell/prerender.sh' >> /home/git/api.git/hooks/post-receive
echo 'sh /web/preRender/shell/prerender.sh' >> /home/git/preRender.git/hooks/post-receive
步骤二、搭建预渲染服务
1、无头浏览器选用PhantomJs。下载安装流程可以参考:
2、搭建预渲染项目,比如命名为preRender。搭建过程参考:
3、进入preRender,我们来写爬虫脚本。可以选择.js模块、也可以用php-phantomjs...,pythone、java都有相应的封装类。
js模块如下,新建spider.js
// spider.js
"use strict";
// 单个资源等待时间,避免资源加载后还需要加载其他资源
var resourceWait =10000;
var resourceWaitTimer;
// 最大等待时间
var maxWait =11000;
var maxWaitTimer;
// 资源计数
var resourceCount =0;
// PhantomJS WebPage模块
var page = require('webpage').create();
// NodeJS 系统模块
var system = require('system');
// 从CLI中获取第二个参数为目标URL
var url = system.args[1];
// 设置PhantomJS视窗大小
page.viewportSize = {
width:1280,
height:1014
};
// 获取镜像
var capture =function(errCode){
// 外部通过stdout获取页面内容
console.log(page.content);
// 清除计时器
clearTimeout(maxWaitTimer);
// 任务完成,正常退出
phantom.exit(errCode);
};
// 资源请求并计数
page.onResourceRequested =function(req){
resourceCount++;
clearTimeout(resourceWaitTimer);
};
// 资源加载完毕
page.onResourceReceived =function (res) {
// chunk模式的HTTP回包,会多次触发resourceReceived事件,需要判断资源是否已经end
if (res.stage !=='end'){
return;
}
resourceCount--;
if (resourceCount ===0){
// 当页面中全部资源都加载完毕后,截取当前渲染出来的html
// 由于onResourceReceived在资源加载完毕就立即被调用了,我们需要给一些时间让JS跑解析任务
// 这里默认预留500毫秒
resourceWaitTimer =setTimeout(capture, resourceWait);
}
};
// 资源加载超时
page.onResourceTimeout =function(req){
resouceCount--;
};
// 资源加载失败
page.onResourceError =function(err){
resourceCount--;
};
// 打开页面
page.open(url, function (status) {
if (status !=='success') {
phantom.exit(1);
}else {
// 当改页面的初始html返回成功后,开启定时器
// 当到达最大时间(默认5秒)的时候,截取那一时刻渲染出来的html
maxWaitTimer =setTimeout(function(){
capture(2);
}, maxWait);
}
});
使用php-phantomjs写爬虫脚本,先composer,composer.json如下配置
"require": {
"jonnyw/php-phantomjs":"4.*"
}
然后,composer update。创建入口文件index.php用以编写爬虫业务,代码如下
<?php
require __DIR__.'/vendor/autoload.php';
use JonnyW\PhantomJs\Client;
$channel_pre = $argv[1] ?? '';
preg_match("/\/(.*)\.html/i", $channel_pre, $channel_arr);
$channel = $channel_arr[1] ?? $channel_pre;
if(empty($channel)) exit('');
$url = "http://***.*.***.***:8000/{$channel}.html"; // 写自己的域名
$client = Client::getInstance();
$client->getEngine()->setPath('/usr/local/src/phantomjs/bin/phantomjs'); //设置phantomjs位置
$client->getProcedureCompiler()->clearCache();
$client->setProcedure('global_variables');
$client->isLazy();
$client->getEngine()->addOption('--load-images=true');
$client->getEngine()->addOption('--ignore-ssl-errors=true');
$request = $client->getMessageFactory()->createRequest();
$response = $client->getMessageFactory()->createResponse();
$request->setTimeout(500000); //设置超时
$request->setDelay(10);
$request->setUrl($url);
$request->setMethod('GET');
$request->addHeader('userAgent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36');
$client->send($request, $response);
echo $response->getContent();
4、在preRender目录下新建shell目录,再添加定时任务脚本prerender.sh
#!/bin/bash
if [ -d "/web/front" ]; then
cd /web/front
if [ -f "/web/preRender/index.php" ]; then
if [ ! -d "./dist" ]; then
mkdir -p dist
chown -R git:git dist
fi
# 复制一级页面
for n in `find . -maxdepth 1 -name "*.html"`; do
if [ ! -f "./dist/"$n ]; then
touch ./dist/${n}
chown -R git:git dist
fi
echo `php /web/qjhPreRender/index.php $n` > ./dist/${n}
done
# 复制一级目录
for m in `find . -maxdepth 1 -type d`; do
if [ $m != './.idea' ] && [ $m != './.git' ] && [ $m != '.' ] && [ $m != './dist' ]
then
cp -arf ${m} ./dist/
fi
done
fi
fi
步骤三、设置定时任务
先不用守护进程的方式,就用普通的crontab。
# crontab -e
*/2 * * * * sh /web/preRender/shell/prerender.sh
# crontab -l
以上就完成了一个预渲染,这样前后端各忙各的,谁都不要做多余的工作就按自己一贯的节奏开发即可。这些工作建议架构师自己来完成!
网友评论