目前服务端渲染的方案大致有
- vue-server-renderer ,prerender-spa-plugin ,等。
- Nuxt.js 、Next.js 的vue和react服务端渲染框架,开箱即用。
- rendertron、rendora(golang开发的)
这里介绍一下nuxt结合rendertron的服务端渲染方案。
nuxt是vue框架,本身支持自定义服务端渲染,通过asyncData结合node直接实现服务端渲染,可以自定义配置页面中那些数据需要进行SSR,集成了webpack,支持自定义loader,自定义服务端中间件,特点是采用更少的配置,让你把精力更多的投入到开发中。
export default {
async asyncData({ params }) {
const { data } = await axios.get(`https://my-api/posts/${params.id}`)
return { title: data.title }
}
}
它原理是从vue层面在服务端将数据render结束后返回给客户端,看似已经满足了服务端渲染,但是这种设置无法更加灵活的满足首屏优化的前提下实现SEO优化。比如说有一个页面有很多动态模块是需要做SSR,但是首屏只显示了一个或者两个模块,这个时候如果把整个页面都进行SEO优化,客户端需要等到所有数据都请求后才能看到页面,那么是不利于首屏优化的。
所以这时候就需要另一个框架可以区分是爬虫访问页面还是普通客户端的请求进行不同的处理,这个时候推荐rendertron ,它的原理是服务端通过Headless Chrome 无头浏览器请求页面,它会等待页码所有动态请求的数据,所以可以拿到JS执行后的页面然后返回给客户端。
rendertron有两种方法使用
- 通过rendertron-middleware,集成到express服务的中间件
- 启动单独的rendertron服务,作为一个读取页面源代码的接口
这里第一种方法需要配置服务端的headless,需要在项目中插入中间件,具体配置参考这里 。
个人更推荐第二种方法,他是作为一个单独的接口来使用,不会影响原有项目,任何项目都可以引用这个接口,而且配置起来更加方便。在nuxt需要SSR的页面中添加中间件,判断user-agent后调用rendertron服务接口后拿到渲染后的页面返回给客户端即可,这样就做到了nuxt的的SSR和rendertron的结合,满足首屏优化和绝大多数其他情况下的SSR优化。
// render项目启动方法
git clone https://github.com/GoogleChrome/rendertron.git
cd rendertron
npm install
npm run build
npm run start
//需要进行SSR的页面
export default {
middleware: 'rendertron',
}
// middleware/rendertron.js
const spiders = [
'Baiduspider',
'bingbot',
...
];
export default function ({ route , req , res }) {
const agent=req.headers?req.headers['user-agent']:'';
if(spiders.includes(agent)){
if(route.fullPath===route.path){//过滤其他无效请求,例如空图片的请求
return new Promise((resolve, reject)=>{
//URL规则:
//rendertron路径 /render/ 待读取页面路径
let url = `http://localhost:3000/render/http://localhost:4000${route.fullPath}`;
let http = require('http');
http.get(url, function (httpres) {
let html = '';
// 绑定data事件 回调函数 累加html片段
httpres.on('data', function (data) {
console.log('data');
html += data;
});
httpres.on('end', function () {
console.log('end');
res.write(html, 'utf-8')
res.end()
resolve()
});
}).on('error', function () {
reject()
console.log('获取数据错误');
});
})
}
}
}