快速在你的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數據可視化等前端知識和實戰,歡迎在公號《趣談前端》加入咱們的技術羣一塊兒學習討論,共同探索前端的邊界。

更多推薦

相關文章
相關標籤/搜索