首頁  >  文章  >  web前端  >  詳解React服務端渲染實例

詳解React服務端渲染實例

零下一度
零下一度原創
2017-07-02 10:12:581793瀏覽

當我們要求渲染時間盡量快、頁面回應速度快時就會用到服務端渲染,本篇文章主要介紹了React服務端渲染,有興趣的可以了解一下

#一、前言

為什麼需要服務端渲染?什麼情況下進行服務端渲染?筆者認為,當我們要求渲染時間盡量快、頁面回應速度快時(優點),才會採用伺服器渲染,並且應該「按需」對頁面進行渲染 ——「首次載入/首屏」。即服務端渲染的優點在於:由中間層( node端 )為客戶端請求初始資料、並由node渲染頁面。那客戶端渲染和服務端渲染有什麼差別?服務端渲染究竟快在哪裡呢?

二、原因與想法

客戶端渲染路線:1. 請求一個html -> 2. 服務端回傳一個html - > 3. 瀏覽器下載html裡面的js/css檔-> 4. 等待js檔下載完成-> 5. 等待js載入並初始化完成-> 6. js程式碼終於可以運行,由js程式碼向後端請求資料( ajax/fetch ) -> 7. 等待後端資料回傳-> 8. react-dom( 客戶端)從無到完整地,把資料渲染為回應頁面

服務端渲染路線:2. 請求一個html -> 2. 服務端請求資料( 內網請求快) -> 3. 伺服器初始渲染(服務端效能好,較快) -> 4. 服務端回傳已經有正確內容的頁面-> 5. 客戶端請求js/css檔案-> 6. 等待js檔案下載完成-> 7. 等待js載入並初始化完成-> 8. react -dom( 客戶端)把剩下一部分渲染完成( 內容小,渲染快)

說明:對同一個元件,服務端渲染「可視的」一部分( render/componentWillMount部分程式碼  ),為確保元件有完善的生命週期事件處理,客戶端需要再次渲染。即:服務端渲染,其實也是需要客戶端進行 再次地、但開銷很小的二次渲染。

時間耗時比較:

1. 資料請求:由服務端請求資料而不是客戶端請求數據,這是「快」的一個主要原因。服務端在內網進行請求,資料回應速度快。客戶端在不同網路環境進行資料請求,且外網http請求開銷大,導致時間差(主要原因)。

2. 步驟:服務端是先請求資料然後渲染「視覺」部分,而客戶端是等待js程式碼下載、載入完成再請求資料、渲染。即:服務端渲染不用等待js程式碼下載完成再請求數據,並會傳回一個已經有內容的頁面。

3. 渲染效能:服務端效能比客戶端高,渲染速度快( 猜測,該項目資料不詳 )。

4. 渲染內容:服務端渲染會把」視覺「部分先渲染,再交給客戶端再作部分渲染。而客戶端渲染,則是從無到有,需要經歷完整的渲染步驟。

  

三、注意事項與問題

##0. 專案依賴什麼?答:node端:express、react-dom/server、webpack。前端:React、mobx(一個更好的redux)、React-router、webpack

#1. 前端/node端共用那部分程式碼?答:node端/前端有各自的

入口檔,server.js/client.js,透過react-router的路由設定檔routes.js作中間層


// routes.js
module.exports = (
  <Route path="/" component={ IComponent } >
    <Route path="/todo" component={ AComponent }>
    </Route>
  </Route>
)

2. 程式碼是由前後端共享,那如何分平台地操作不同程式碼?答:透過webpack。對共享程式碼,進行不同平台的,webpack(babel)編譯,透過在webpack.config.js中加入


 // webpack.client.config.js
plugins: [
   new webpack.DefinePlugin({
     &#39;isServer&#39;: false,
     &#39;isClient&#39;: true
   })
 ]
// webpack.server.config.js
plugins: [
   new webpack.DefinePlugin({
     &#39;isServer&#39;: true,
     &#39;isClient&#39;: false
   })
 ]
// xxx.js
if( isServer ) {
  ...
}else { ... }

##4 . 組件的生命週期是如何的呢?答:componentWillMount( node端 ) -> render( node端 ) -> 客戶端生命週期和以前一樣

5. 拉取資料後如何處理呢?答:先在node端根據資料渲染好,再把資料隨頁面返回至前端,再由React根據資料進行渲染校對( 若前後端渲染結果不一致將報錯 )。應該在componentWillMount讓元件進行本地的資料同步

// 组件.js
componentWillMount() {
  if( isClient ) {
     this.todoStore.todos = window.initTodos; 
  }
}  
// node端返回
`
<!doctype html>
<html lang="utf-8">
    <head>
    <script> window.initTodo = ${...}</script>
    </head>
    <body> ... </body>
    <script src="/static/vendor.js"></script>
    <script src="/static/client.bundle.js"></script>

6. 前端/node端“入口文件”通过webpack构建有什么不同?答:前端是为了解析JSX与es6代码(包括mobx的es6 decorator),node端除了以上,还需要加入babel-plugin-transform-runtime,是为了在node良好地运行es7 async / awatit

7. 如何保证node端能够先请求数据然后再渲染?答:es7的async / await语法  

8. 前端的react-router路由与node端路由如何配合?node如何知道该路由是渲染哪个数据呢?答:前端是以前的react-router配置,node端是react-router的match/RouterContext// 共享文件routes.js


const routes = (
  <Route path="/" component={ IComponent } >
    <Route path="/todo" component={ AComponent }>
    </Route>
  </Route>
)
// 前端入口文件client.js
render(
  <Router routes={ routes } history={ browserHistory } />,
  ele
)
// node端入口文件server.js
let app = express();
app.get(&#39;/todo&#39;, (req, res) => {

  match({ routes: routes, location: req.url }, async (err, redirect, props) => {
     // match会帮我们找到要渲染的组件链,注:上面一行使用了async语法,因此可以在render之前使用await运行拉取数据的代码
     let html = renderToString(<RouterContext {...props} />)
     res.send( indexPage(html) )
  }
}) 
// node端返回   
let indexPage = (html)=>{
  return `
  <!doctype html>
    <html lang="utf-8">
      <head>
        <script>
        </script>
      </head>
      <body>
        <section id="hzpapp" >${html}</section>
      </body>
      <script src="/static/vendor.js"></script>
      <script src="/static/client.bundle.js"></script>
    </html>
}

9. client.js中是否还能继续使用webpack的require.ensure ? 答:可以。但闪白明显,且node端返回html后会有报错,在加载脚本后该错误能忽略。 

10. 若我使用的是mobx,该如何实例化store ? 答:每一个node请求,都应该返回一个新的独立的store实例,而不是每个node请求共用一个store实例(笔者易犯)。

        

本demo地址( 前端库React+mobx+ReactRouter ):github.com/Penggggg/react-ssr

以上是詳解React服務端渲染實例的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn