自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試官:說說React服務(wù)端渲染怎么做?原理是什么?

開發(fā) 前端
在SSR中,我們了解到Server-Side Rendering ,簡稱SSR,意為服務(wù)端渲染。指由服務(wù)側(cè)完成頁面的 HTML 結(jié)構(gòu)拼接的頁面處理技術(shù),發(fā)送到瀏覽器,然后為其綁定狀態(tài)與事件,成為完全可交互頁面的過程。

[[415238]]

本文轉(zhuǎn)載自微信公眾號(hào)「JS每日一題」,作者灰灰。轉(zhuǎn)載本文請(qǐng)聯(lián)系JS每日一題公眾號(hào)。

一、是什么

在SSR中,我們了解到Server-Side Rendering ,簡稱SSR,意為服務(wù)端渲染

指由服務(wù)側(cè)完成頁面的 HTML 結(jié)構(gòu)拼接的頁面處理技術(shù),發(fā)送到瀏覽器,然后為其綁定狀態(tài)與事件,成為完全可交互頁面的過程

其解決的問題主要有兩個(gè):

  • SEO,由于搜索引擎爬蟲抓取工具可以直接查看完全渲染的頁面
  • 加速首屏加載,解決首屏白屏問題

二、如何做

在react中,實(shí)現(xiàn)SSR主要有兩種形式:

  • 手動(dòng)搭建一個(gè) SSR 框架
  • 使用成熟的SSR 框架,如 Next.JS

這里主要以手動(dòng)搭建一個(gè)SSR框架進(jìn)行實(shí)現(xiàn)

首先通過express啟動(dòng)一個(gè)app.js文件,用于監(jiān)聽3000端口的請(qǐng)求,當(dāng)請(qǐng)求根目錄時(shí),返回HTML,如下:

  1. const express = require('express'
  2. const app = express() 
  3. app.get('/', (req,res) => res.send(` 
  4. <html> 
  5.    <head> 
  6.        <title>SSR demo</title> 
  7.    </head> 
  8.    <body> 
  9.        Hello world 
  10.    </body> 
  11. </html> 
  12. `)) 
  13.  
  14. app.listen(3000, () => console.log('Exampleapp listening on port 3000!')) 

然后再服務(wù)器中編寫react代碼,在app.js中進(jìn)行應(yīng)引用

  1. import React from 'react' 
  2.  
  3. const Home = () =>{ 
  4.  
  5.     return <div>home</div> 
  6.  
  7.  
  8. export default Home 

為了讓服務(wù)器能夠識(shí)別JSX,這里需要使用webpakc對(duì)項(xiàng)目進(jìn)行打包轉(zhuǎn)換,創(chuàng)建一個(gè)配置文件webpack.server.js并進(jìn)行相關(guān)配置,如下:

  1. const path = require('path')    //node的path模塊 
  2. const nodeExternals = require('webpack-node-externals'
  3.  
  4. module.exports = { 
  5.     target:'node'
  6.     mode:'development',           //開發(fā)模式 
  7.     entry:'./app.js',             //入口 
  8.     output: {                     //打包出口 
  9.         filename:'bundle.js',     //打包后的文件名 
  10.         path:path.resolve(__dirname,'build')    //存放到根目錄的build文件夾 
  11.     }, 
  12.     externals: [nodeExternals()],  //保持node中require的引用方式 
  13.     module: { 
  14.         rules: [{                  //打包規(guī)則 
  15.            test:   /\.js?$/,       //對(duì)所有js文件進(jìn)行打包 
  16.            loader:'babel-loader',  //使用babel-loader進(jìn)行打包 
  17.            exclude: /node_modules/,//不打包node_modules中的js文件 
  18.            options: { 
  19.                presets: ['react','stage-0',['env', {  
  20.                                   //loader時(shí)額外的打包規(guī)則,對(duì)react,JSX,ES6進(jìn)行轉(zhuǎn)換 
  21.                     targets: { 
  22.                         browsers: ['last 2versions']   //對(duì)主流瀏覽器最近兩個(gè)版本進(jìn)行兼容 
  23.                     } 
  24.                }]] 
  25.            } 
  26.        }] 
  27.     } 

接著借助react-dom提供了服務(wù)端渲染的 renderToString方法,負(fù)責(zé)把React組件解析成html

  1. import express from 'express' 
  2. import React from 'react'//引入React以支持JSX的語法 
  3. import { renderToString } from 'react-dom/server'//引入renderToString方法 
  4. import Home from'./src/containers/Home' 
  5.  
  6. const app= express() 
  7. const content = renderToString(<Home/>) 
  8. app.get('/',(req,res) => res.send(` 
  9. <html> 
  10.    <head> 
  11.        <title>SSR demo</title> 
  12.    </head> 
  13.    <body> 
  14.         ${content} 
  15.    </body> 
  16. </html> 
  17. `)) 
  18.  
  19. app.listen(3001, () => console.log('Exampleapp listening on port 3001!')) 

上面的過程中,已經(jīng)能夠成功將組件渲染到了頁面上

但是像一些事件處理的方法,是無法在服務(wù)端完成,因此需要將組件代碼在瀏覽器中再執(zhí)行一遍,這種服務(wù)器端和客戶端共用一套代碼的方式就稱之為「同構(gòu)」

重構(gòu)通俗講就是一套R(shí)eact代碼在服務(wù)器上運(yùn)行一遍,到達(dá)瀏覽器又運(yùn)行一遍:

  • 服務(wù)端渲染完成頁面結(jié)構(gòu)
  • 瀏覽器端渲染完成事件綁定

瀏覽器實(shí)現(xiàn)事件綁定的方式為讓瀏覽器去拉取JS文件執(zhí)行,讓JS代碼來控制,因此需要引入script標(biāo)簽

通過script標(biāo)簽為頁面引入客戶端執(zhí)行的react代碼,并通過express的static中間件為js文件配置路由,修改如下:

  1. import express from 'express' 
  2. import React from 'react'//引入React以支持JSX的語法 
  3. import { renderToString } from'react-dom/server'//引入renderToString方法 
  4. import Home from './src/containers/Home' 
  5.   
  6. const app = express() 
  7. app.use(express.static('public')); 
  8. //使用express提供的static中間件,中間件會(huì)將所有靜態(tài)文件的路由指向public文件夾 
  9.  const content = renderToString(<Home/>) 
  10.   
  11. app.get('/',(req,res)=>res.send(` 
  12. <html> 
  13.    <head> 
  14.        <title>SSR demo</title> 
  15.    </head> 
  16.    <body> 
  17.         ${content} 
  18.    <script src="/index.js"></script> 
  19.    </body> 
  20. </html> 
  21. `)) 
  22.  
  23.  app.listen(3001, () =>console.log('Example app listening on port 3001!')) 

然后再客戶端執(zhí)行以下react代碼,新建webpack.client.js作為客戶端React代碼的webpack配置文件如下:

  1. const path = require('path')                    //node的path模塊 
  2.  
  3. module.exports = { 
  4.     mode:'development',                         //開發(fā)模式 
  5.     entry:'./src/client/index.js',              //入口 
  6.     output: {                                   //打包出口 
  7.         filename:'index.js',                    //打包后的文件名 
  8.         path:path.resolve(__dirname,'public')   //存放到根目錄的build文件夾 
  9.     }, 
  10.     module: { 
  11.         rules: [{                               //打包規(guī)則 
  12.            test:   /\.js?$/,                    //對(duì)所有js文件進(jìn)行打包 
  13.            loader:'babel-loader',               //使用babel-loader進(jìn)行打包 
  14.            exclude: /node_modules/,             //不打包node_modules中的js文件 
  15.            options: { 
  16.                presets: ['react','stage-0',['env', {      
  17.                     //loader時(shí)額外的打包規(guī)則,這里對(duì)react,JSX進(jìn)行轉(zhuǎn)換 
  18.                     targets: { 
  19.                         browsers: ['last 2versions']   //對(duì)主流瀏覽器最近兩個(gè)版本進(jìn)行兼容 
  20.                     } 
  21.                }]] 
  22.            } 
  23.        }] 
  24.     } 

這種方法就能夠簡單實(shí)現(xiàn)首頁的react服務(wù)端渲染,過程對(duì)應(yīng)如下圖:

在做完初始渲染的時(shí)候,一個(gè)應(yīng)用會(huì)存在路由的情況,配置信息如下:

  1. import React from 'react'                   //引入React以支持JSX 
  2. import { Route } from 'react-router-dom'    //引入路由 
  3. import Home from './containers/Home'        //引入Home組件 
  4.  
  5. export default ( 
  6.     <div> 
  7.         <Route path="/" exact component={Home}></Route> 
  8.     </div> 

然后可以通過index.js引用路由信息,如下:

  1. import React from 'react' 
  2. import ReactDom from 'react-dom' 
  3. import { BrowserRouter } from'react-router-dom' 
  4. import Router from'../Routers' 
  5.  
  6. const App= () => { 
  7.     return ( 
  8.         <BrowserRouter> 
  9.            {Router} 
  10.         </BrowserRouter> 
  11.     ) 
  12.  
  13. ReactDom.hydrate(<App/>, document.getElementById('root')) 

這時(shí)候控制臺(tái)會(huì)存在報(bào)錯(cuò)信息,原因在于每個(gè)Route組件外面包裹著一層div,但服務(wù)端返回的代碼中并沒有這個(gè)div

解決方法只需要將路由信息在服務(wù)端執(zhí)行一遍,使用使用StaticRouter來替代BrowserRouter,通過context進(jìn)行參數(shù)傳遞

  1. import express from 'express' 
  2. import React from 'react'//引入React以支持JSX的語法 
  3. import { renderToString } from 'react-dom/server'//引入renderToString方法 
  4. import { StaticRouter } from 'react-router-dom' 
  5. import Router from '../Routers' 
  6.   
  7. const app = express() 
  8. app.use(express.static('public')); 
  9. //使用express提供的static中間件,中間件會(huì)將所有靜態(tài)文件的路由指向public文件夾 
  10.  
  11. app.get('/',(req,res)=>{ 
  12.     const content  = renderToString(( 
  13.         //傳入當(dāng)前path 
  14.         //context為必填參數(shù),用于服務(wù)端渲染參數(shù)傳遞 
  15.         <StaticRouter location={req.path} context={{}}> 
  16.            {Router} 
  17.         </StaticRouter> 
  18.     )) 
  19.     res.send(` 
  20.    <html> 
  21.        <head> 
  22.            <title>SSR demo</title> 
  23.        </head> 
  24.        <body> 
  25.        <div id="root">${content}</div> 
  26.        <script src="/index.js"></script> 
  27.        </body> 
  28.    </html> 
  29.     `) 
  30. }) 
  31.  
  32.  
  33. app.listen(3001, () => console.log('Exampleapp listening on port 3001!')) 

這樣也就完成了路由的服務(wù)端渲染

三、原理

整體react服務(wù)端渲染原理并不復(fù)雜,具體如下:

node server 接收客戶端請(qǐng)求,得到當(dāng)前的請(qǐng)求url 路徑,然后在已有的路由表內(nèi)查找到對(duì)應(yīng)的組件,拿到需要請(qǐng)求的數(shù)據(jù),將數(shù)據(jù)作為 props、context或者store 形式傳入組件

然后基于 react 內(nèi)置的服務(wù)端渲染方法 renderToString()把組件渲染為 html字符串在把最終的 html進(jìn)行輸出前需要將數(shù)據(jù)注入到瀏覽器端

瀏覽器開始進(jìn)行渲染和節(jié)點(diǎn)對(duì)比,然后執(zhí)行完成組件內(nèi)事件綁定和一些交互,瀏覽器重用了服務(wù)端輸出的 html 節(jié)點(diǎn),整個(gè)流程結(jié)束

參考文獻(xiàn)

  • https://zhuanlan.zhihu.com/p/52693113
  • https://segmentfault.com/a/1190000020417285
  • https://juejin.cn/post/6844904000387563533#heading-14

 

責(zé)任編輯:武曉燕 來源: JS每日一題
相關(guān)推薦

2024-08-22 10:39:50

@Async注解代理

2024-03-05 10:33:39

AOPSpring編程

2024-11-19 15:13:02

2025-04-16 00:00:01

JWT客戶端存儲(chǔ)加密令

2024-08-29 16:30:27

2023-12-27 18:16:39

MVCC隔離級(jí)別幻讀

2024-08-12 17:36:54

2024-02-29 16:49:20

volatileJava并發(fā)編程

2024-09-20 08:36:43

零拷貝數(shù)據(jù)傳輸DMA

2024-03-22 06:56:24

零拷貝技術(shù)數(shù)據(jù)傳輸數(shù)據(jù)拷貝

2021-05-20 08:34:03

CDN原理網(wǎng)絡(luò)

2024-03-11 18:18:58

項(xiàng)目Spring線程池

2021-06-30 07:19:36

React事件機(jī)制

2021-06-29 09:47:34

ReactSetState機(jī)制

2024-03-14 14:56:22

反射Java數(shù)據(jù)庫連接

2024-03-28 10:37:44

IoC依賴注入依賴查找

2024-07-31 08:28:37

DMAIOMMap

2024-12-06 07:00:00

2021-05-06 14:34:12

Webpack熱更新程序

2021-09-14 07:06:13

React項(xiàng)目TypeScript
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)