快速在你的vue/react应用中实现ssr(服务端渲染)
前言
我们都知道, Vue和React是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出自定义组件,进行生成 DOM 和操作 DOM, 也就是我们常说的客户端渲染, 并且我们大部分主流的场景都是SPA(单页面)应用, 而随着 SPA尤其是 React、Vue、Angular 为代表的前端框架的流行,越来越多的 Web App 使用的是客户端渲染。
使用客户端渲染的优势在于节省后端资源、局部刷新、前后端分离等,但随着应用的日益复杂, 首屏渲染时间不断变长, 并且存在严重的SEO问题。
服务端渲染(ssr),是指由服务器端完成页面的HTML 结构拼接,并且直接将拼接好的HTML发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的处理技术。
对于服务端渲染的页面,服务端可以直接将带数据的内容通过 HTML 文本的形式返回,搜索引擎爬虫可以轻易的获取页面内容,而对于客户端渲染的应用,客户端必须执行服务器返回的 Javascript 才能得到正确的网页内容。目前,除 Google、Bing 支持 Javascript 外(也会有一些限制),其他的大部分搜索引擎都不支持 Javascript,也就无法获取正确的网页内容。而本文要讲的技术方案,正是为了解决SPA下的SSR技术困境.接下来我们看看常用的ssr技术实现方案.
摘要
ssr(服务端渲染)技术实现方案
接下来笔者将列举几个常用的基于vue/react的服务端渲染方案,如下:
使用next.js/nuxt.js的服务端渲染方案 使用node+vue-server-renderer实现vue项目的服务端渲染 使用node+React renderToStaticMarkup实现react项目的服务端渲染 传统网站通过模板引擎来实现ssr(比如ejs, jade, pug等) 使用rendertron实现SPA项目的服务端渲染
以上是笔者之前实践过的方案, 最后一种方案笔者将在下面一节详细介绍, 因为next/nuxt是已有的服务端渲染解决方案,文档写的比较详细,这里笔者就不再做过多介绍了,这里我们简单介绍一下第二种和第三种方案.
1.使用node+vue-server-renderer实现vue项目的服务端渲染
npm install vue vue-server-renderer --save
const Vue = require('vue')
const server = require('express')()
const renderer = require('vue-server-renderer').createRenderer()
server.get('*', (req, res) => {
const app = new Vue({
data: {
url: req.url
},
template: `<div>趣谈前端:{{ url }}</div>`
})
renderer.renderToString(app, (err, html) => {
if (err) {
res.status(500).end('Internal Server Error')
return
}
res.end(`
<!DOCTYPE html>
<html lang="en">
<head><title>Hello</title></head>
<body>${html}</body>
</html>
`)
})
})
server.listen(8080)
2.使用node+React renderToStaticMarkup实现react项目的服务端渲染
var express = require('express');
var app = express();
var React = require('react'),
ReactDOMServer = require('react-dom/server');
var App = React.createFactory(require('./App'));
app.get('/', function(req, res) {
var html = ReactDOMServer.renderToStaticMarkup(
React.DOM.body(
null,
React.DOM.div({id: 'root',
dangerouslySetInnerHTML: {
__html: ReactDOMServer.renderToStaticMarkup(App())
}
})
)
);
res.end(html);
});
app.listen(80, function() {
console.log('running on port ' + 80);
});
renderToString:将 React Component 转化为 HTML 字符串,生成的 HTML 的 DOM 会带有额外属性:各个 DOM 会有data-react-id属性,第一个 DOM 会有data-checksum属性。 renderToStaticMarkup:将 React Component 转化为 HTML 字符串,但是生成 HTML 的 DOM 不会有额外属性,从而节省 HTML 字符串的大小。
所以这里我们一般使用renderToStaticMarkup函数. 同理在实际业务场景中我们也会写2套代码来实现ssr.
使用谷歌rendertron实现服务端渲染
Render 用于渲染网站内容 Screenshot 用于将网站内容截图
在 SEO 场景下我们使用的是 Render 接口。
比如当客户端请求我们的网站时,我们服务端可以根据请求头 User Agent 发现是否包含了 Baiduspider/2.0 关键字,如果是, 那么可以认定为当前的客户端是一个百度爬虫此时可以将这个请求代理 Rendertron 服务的 /render/客户端请求地址
路由,让 Rendertron 帮助执行网页内的 Javascript,并将最终内容返回给搜索引擎爬虫。
具体实现
1.本地运行
npm install -g rendertron
rendertron
之后控制台会打印本地服务启动的地址,比如localhost:3000
这个时候我们只需要在地址后面输入我们想渲染的网站即可:localhost:3000:render/你的网站地址
,
如下图所示:
const koa = require('koa');
const app = new koa();
app.use(async (ctx, next) => {
ctx.type = "html";
if(/Baiduspider\/2\.0/g.ctx.header['user-agent']) {
// 是百度爬虫,则转发到rendertron服务中
ctx.redirect(`http://localhost:3000/render/${ctx.url}`)
}else {
// 渲染正常的路由页面
}
await next();
})
app.listen('80');
const express = require('express');
const rendertron = require('rendertron-middleware');
const app = express();
app.use(rendertron.makeMiddleware({
proxyUrl: 'http://your-rendertron-instance/render',
}));
// 正常的路由和页面渲染逻辑
app.use(...);
app.listen(81);
后期展望
后期笔者将会继续带大家探索大前端相关内容, 基本框架如下: