React-AMD標準、動態加載資源的Web APP如何做服務器端渲染

背景介紹

需要做服務器端渲染的app是:可以點擊添加組件,保存以後生成一個獨立的web app。就是一個生成web app的web app。因爲不一定每個組件都被添加到用戶創建的web app中,所以,組件的資源是動態加載的,只有選擇某個組件的時候,纔會去加載這個組件的資源。前端資源使用AMD標準進行加載。

困難點

首先說明,這裏不分析SSR中常見的問題,比如路由匹配、css loader處理、常見的一些webpack配置的不同、SSR的一些打包工具(例如:universal-webpack、webpack-isomorphic-tools)這裏不做分析。
以下是開發過程中遇到的幾個困難點,或者說比一般SSR有特點的地方。不分析解決過程,過程中有多少坑,開發同學們都懂,看起來很簡單的結論,其實經過了各種小問題、大問題,各種方案對比。今天這裏只總結解決方案。

資源加載

首先,這裏說的是資源,指的的js文件、css文件、圖片等,不是服務器端返回的、用於前端渲染的數據。
如背景中介紹的,前端的資源相當於是按需加載的,當資源沒有返回時,首屏會顯示類似Config is loading這種提示,無法和服務器端返回的markup匹配,造成頁面閃爍一下。目前的解決辦法是,首屏的資源同步加載,也就是隻有首屏是服務器端渲染的,進而就沒有了一般服務器端渲染需要解決的路由匹配問題。

require的重寫

同構的好處就是一份代碼瀏覽器端和服務器端都可以使用。這個項目中,前端使用AMD標準的require方法進行資源加載,例如:

amdRequire('path/to/fille', function(src){
   //do something with src
});

這個require方法,是前端通過javscript標籤引用一個庫所帶來的,直接用webpack打包爲commjs會報一系列錯誤,解決這些錯誤並且保證其穩定性是比較難的。服務器端使用commjs的require方法進行了重寫:

amdRequire = function(m, cb){
  if(typeof m === 'string'){
    let _m = require(m);
    _m = _m.__esModule ? _m.default : _m;
    cb(_m);
  }else{
    let _mArr = m.map((ele) => {
      let _ele = require(ele).__esModule ? require(ele).default : require(ele);
      return _ele;
   });
    cb(_mArr);
  }
}

webpack編譯

在瀏覽器端,使用webpack將js文件編譯爲libraryTarget: 'amd'的模塊,每個組件爲一個入口,打一個包。常用公共模塊,比如react,打到一個vendor中。框架資源,也就是頁面主體打一個包。頁面除了首屏外(首屏所需資源全部一次返回),服務器首先返回vendor和框架資源包。根據用戶的操作(添加組件)再去請求其他組件包。

在服務器端,編譯爲libraryTarget: 'commonjs2',所有資源打一個包,根據前端請求的id,找到app對應的config文件,直接去這個大資源包中找模塊,最終返回一個html字符串(renderToString返回結果),前端接受到這個字符串以後進行水合(hydrate)。

具體config文件涉及到項目代碼,不再展示。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章