快速在你的vue/react应用中实现ssr(服务端渲染)

前言

咱们都知道, VueReact是构建客户端应用程序的框架。默认状况下,能够在浏览器中输出自定义组件,进行生成 DOM 和操做 DOM, 也就是咱们常说的客户端渲染, 而且咱们大部分主流的场景都是SPA(单页面)应用, 而随着 SPA尤为是 ReactVueAngular 为表明的前端框架的流行,愈来愈多的 Web App 使用的是客户端渲染。 javascript

使用 客户端渲染的优点在于 节省后端资源局部刷新先后端分离等,但随着应用的日益复杂, 首屏渲染时间不断变长, 而且存在严重的 SEO问题。

因此为了解决SPA应用遇到的这些问题, 咱们必须考虑SSR:css

服务端渲染(ssr),是指由服务器端完成页面的HTML 结构拼接,而且直接将拼接好的HTML发送到浏览器,而后为其绑定状态与事件,成为彻底可交互页面的处理技术。html

对于服务端渲染的页面,服务端能够直接将带数据的内容经过 HTML 文本的形式返回,搜索引擎爬虫能够轻易的获取页面内容,而对于客户端渲染的应用,客户端必须执行服务器返回的 Javascript 才能获得正确的网页内容。目前,除 GoogleBing 支持 Javascript 外(也会有一些限制),其余的大部分搜索引擎都不支持 Javascript,也就没法获取正确的网页内容。而本文要讲的技术方案,正是为了解决SPA下的SSR技术困境.接下来咱们看看经常使用的ssr技术实现方案. 前端

摘要

ssr(服务端渲染)技术实现方案

接下来笔者将列举几个经常使用的基于vue/react的服务端渲染方案,以下:vue

  • 使用next.js/nuxt.js的服务端渲染方案
  • 使用node+vue-server-renderer实现vue项目的服务端渲染
  • 使用node+React renderToStaticMarkup实现react项目的服务端渲染
  • 传统网站经过模板引擎来实现ssr(好比ejs, jade, pug等)
  • 使用rendertron实现SPA项目的服务端渲染

以上是笔者以前实践过的方案, 最后一种方案笔者将在下面一节详细介绍, 由于next/nuxt是已有的服务端渲染解决方案,文档写的比较详细,这里笔者就再也不作过多介绍了,这里咱们简单介绍一下第二种和第三种方案.java

1.使用node+vue-server-renderer实现vue项目的服务端渲染

首先vue-server-renderer依赖node的api,因此只能运行在node环境, 咱们须要先安装它:node

npm install vue vue-server-renderer --save
复制代码

在node中使用,代码以下:react

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)
复制代码

固然实际状况比上面的案例要复杂不少, 咱们能够专门写一个template.html,而后经过模板差值的方式导入后端数据,进而实现服务端渲染. 在使用这种方式的时候咱们仍然要维护两套代码.webpack

2.使用node+React renderToStaticMarkup实现react项目的服务端渲染

使用这种方案和vue的方案相似, 只不过这里咱们用了react自带的api来实现ssr,简单的实现代码以下:css3

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);
});
复制代码

以上使用了renderToStaticMarkup, 咱们都知道react-dom提供了两种服务端渲染函数,以下:

  1. renderToString:将 React Component 转化为 HTML 字符串,生成的 HTML 的 DOM 会带有额外属性:各个 DOM 会有data-react-id属性,第一个 DOM 会有data-checksum属性。

  2. renderToStaticMarkup:将 React Component 转化为 HTML 字符串,可是生成 HTML 的 DOM 不会有额外属性,从而节省 HTML 字符串的大小。

因此这里咱们通常使用renderToStaticMarkup函数. 同理在实际业务场景中咱们也会写2套代码来实现ssr.

使用谷歌rendertron实现服务端渲染

Google 推出的 Rendertron 使得 SPA 也可以被不支持执行 Javascript 的搜索引擎爬取渲染后的内容。其原理主要是经过使用 Headless Chrome 在内存中执行 Javascript,并在获得完整内容后,将内容返回给客户端。

咱们一般会将 Rendertron 部署为一个独立的 HTTP 服务,而后为 Web 应用框架配置 Google 官方提供的中间件或者在反向代理上添加相应路由规则,使得可以在检测到搜索引擎爬虫的 UA 时,能够将请求代理给 Rendertron 服务。笔者总结了一下其基实现本原理图,方便你们理解:

Rendertron 提供了两个主要 API:

  • Render 用于渲染网站内容
  • Screenshot 用于将网站内容截图

在 SEO 场景下咱们使用的是 Render 接口。

好比当客户端请求咱们的网站时,咱们服务端能够根据请求头 User Agent 发现是否包含了 Baiduspider/2.0 关键字,若是是, 那么能够认定为当前的客户端是一个百度爬虫此时能够将这个请求代理 Rendertron 服务的 /render/客户端请求地址 路由,让 Rendertron 帮助执行网页内的 Javascript,并将最终内容返回给搜索引擎爬虫。

使用Rendertron的好处在于咱们能够不用考虑服务端渲染的部分,彻底按照SPA的模式开发项目,也不用为了兼容服务端渲染而写多余的兼容代码.

具体实现

首先咱们须要安装Rendertron, 能够在github中找到其安装和使用方法,在安装前最好先安装docker, 目前docker的最新版本以支持傻瓜式安装,因此安装启动都很是方便.

1.本地运行

在安装好docker以后, 咱们先全局安装rendertron:

npm install -g rendertron
复制代码

而后咱们须要安装谷歌浏览器(做为合格的开发都应该有谷歌浏览器~),而后就能够用它的cli来启动服务了,咱们只须要在命令行执行以下命令:

rendertron
复制代码

以后控制台会打印本地服务启动的地址,好比localhost:3000 这个时候咱们只须要在地址后面输入咱们想渲染的网站便可: localhost:3000:render/你的网站地址, 以下图所示:

此时咱们的 rendertron服务已经搭建完成, 接下来咱们能够在服务端来实现ssr了,代码以下:

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');
复制代码

固然若是咱们后端技术栈采用的是express, rendertron有专门的中间件可使用, 不只仅能够拦截百度的爬虫,具体用法以下:

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);
复制代码

因此为了下降开发成本笔者建议能够采用rendertron的方案, 单独部署一套服务器用来实现ssr. 可是咱们须要考虑当网站流量增长时的扩容问题,以及配置搭建反向代理或负载均衡等配套服务。

后期展望

后期笔者将会继续带你们探索大前端相关内容, 基本框架以下:

最后

若是想学习更多H5游戏, webpacknodegulpcss3javascriptnodeJScanvas数据可视化等前端知识和实战,欢迎在公号《趣谈前端》加入咱们的技术群一块儿学习讨论,共同探索前端的边界。

更多推荐

相关文章
相关标签/搜索