初識React服務端渲染——SSR

服務端渲染

Server Slide Rendering服務端渲染,又簡寫爲SSR,他通常被用在咱們的SPA(Single-Page Application),即單頁應用。

服務端渲染的模式下,當用戶第一次請求頁面時,由服務器把須要的組件或頁面渲染成 HTML 字符串(在Node環境已經跑了一遍JS拿到該拿的數據),而後把它返回給客戶端。客戶端拿到手的,是能夠直接渲染而後呈現給用戶的 HTML 內容,不須要爲了生成 DOM 內容本身再去跑一遍 JS 代碼。使用服務端渲染的網站,能夠說是「所見即所得」,頁面上呈現的內容,咱們在 html 源文件裏也能找到。css

爲何要用SSR

更好的SEO(Search Engine Optimization)

SEO是搜索引擎優化,簡而言之就是針對百度這些搜索引擎,可讓他們搜索到咱們的應用html

事實上,不少網站是出於效益的考慮才啓用服務端渲染,性能卻是在其次。
假設 A 網站頁面中有一個關鍵字叫「前端性能優化」,這個關鍵字是 JS 代碼跑過一遍後添加到 HTML 頁面中的。那麼客戶端渲染模式下,咱們在搜索引擎搜索這個關鍵字,是找不到 A 網站的——搜索引擎只會查找現成的內容,不會幫你跑 JS 代碼。A 網站的運營方見此情形,感到很頭大:搜索引擎搜不出來,用戶找不到咱們,誰還會用個人網站呢?爲了把「現成的內容」拿給搜索引擎看,A 網站不得不啓用服務端渲染。
但性能在其次,不表明性能不重要。服務端渲染解決了一個很是關鍵的性能問題——首屏加載速度過慢。在客戶端渲染模式下,咱們除了加載 HTML,還要等渲染所需的這部分 JS 加載完,以後還得把這部分 JS 在瀏覽器上再跑一遍。前端

提高首屏加載速度

更好的用戶體驗,對於緩慢的網絡狀況或運行緩慢的設備,加載完資源瀏覽器直接呈現,無需等待全部的 JavaScript 都完成下載並執行,才顯示服務器渲染的HTML。react

客戶端渲染和服務端渲染路線對比

客戶端渲染路線:

1.請求一個html
2.服務端返回一個html
3.瀏覽器下載html裏面的js/css文件
4.等待js文件下載完成
5.等待js加載並初始化完成
6.js代碼終於能夠運行,由js代碼向後端請求數據( ajax/fetch )
6.等待後端數據返回
7.react-dom( 客戶端 )從無到完整地,把數據渲染爲響應頁面webpack

服務端渲染路線:

1.請求一個html
2.服務端請求數據( 內網請求快 )
3.服務器初始渲染(服務端性能好,較快)
4.服務端讀取瀏覽器端打包好的index.html文件爲字符串,將渲染好的組件、樣式、數據塞入html字符串,返回給瀏覽器
5.客戶端請求js/css文件
6.等待js文件下載完成
7.等待js加載並初始化完成
8.瀏覽器直接渲染接收到的html內容,而且加載打包好的瀏覽器端js文件,進行事件綁定,初始化狀態數據,完成同構web

簡易的React服務端渲染

renderToString

React能夠將React元素渲染成它的初始化Html,而且返回html字符串,在express服務端生成html,返回給瀏覽器渲染ajax

const express = require('express');
const app = express();
const React = require('react');
const {renderToString} = require('react-dom/server');
const App = class extends React.PureComponent{
  render(){
    return React.createElement("h1",null,"Hello World");;
  }
};
app.get('/',function(req,res){
  const content = renderToString(React.createElement(App));
  res.send(content);
});
app.listen(3000);

同構

將上面的代碼加上JS的事件監聽服務器端渲染返給瀏覽器,你會發如今瀏覽器裏無能如何點擊都不會觸發事件
由於renderToString只是返回html字符串,元素對應的js交互邏輯並無返回給瀏覽器,所以點擊h1標籤是沒法響應的。express

const App = class extends React.PureComponent{
  handleClick=(e)=>{
    alert(e.target.innerHTML);
  }
  render(){
    return <h1 onClick={this.handleClick}>Hello World!</h1>;
  }
};

解決方法以前,咱們先講一下「同構」這個概念。何爲「同構」,簡單來講就是「同種結構的不一樣表現形態」。後端

同一份react代碼在服務端執行一遍,再在客戶端執行一遍。
同一份react代碼,在服務端執行一遍以後,咱們就能夠生成相應的html。在客戶端執行一遍以後就能夠正常響應用戶的操做。這樣就組成了一個完整的頁面。因此咱們須要額外的入口文件去打包客戶端須要的js交互代碼。
import express from 'express';
import React from 'react';
import {renderToString} from 'react-dom/server';
import App from  './src/app';
const app = express();

app.use(express.static("dist"))

app.get('/',function(req,res){
  const content = renderToString(<App/>);
  res.send(`
        <!doctype html>
        <html>
            <title>ssr</title>
            <body>
                <div id="root">${content}</div>
                <script src="/client/index.js"></script>
            </body> 
        </html>
    `);
});
app.listen(3000);

「/client/index.js」就是咱們用webpack打包出來的用於客戶端執行的js文件
如今點擊會出現彈窗瀏覽器

ReactDOM.hydrate()

咱們在服務端渲染時用ReactDOM.hydrate()來取代ReactDOM.render()
ReactDOM.render()會將掛載dom節點的全部子節點所有清空掉,再從新生成子節點。而ReactDOM.hydrate()則會複用掛載dom節點的子節點,並將其與react的virtualDom關聯上。
因此咱們客戶端入口文件調整一下,拿到剛纔從後臺返回HTML裏面的root節點,進行hydrate

import React from 'react';
import {hydrate} from 'react-dom';
import App from './app';
hydrate(<App/>,document.getElementById("root"));

路由

相關文章
相關標籤/搜索