­

這個ssr 開發骨架有點帥

  • 2020 年 3 月 17 日
  • 筆記

寫在前面

前段時間一直在研究react ssr 技術,也很想自己寫一個 ssr開發骨架,有了自己的骨架後就不需要在用 ejs 模板了,直接用 jsx 就可以了,技術棧也就統一了,那真的很爽。

基於我之前了解的一點點ssr 原理就直開干,在實現的過程中的坑還真不少,但是也沒有什麼太難得東西,主要是我採用的是 react router5 ,對這個新版的路由使用不太熟悉,又和 react router3的差別較大,導致耗費的時間多了點。另外一個就是動態路由(路由分包)的處理,這個需要在 node 端和瀏覽器端都需要做處理,才能保證最終渲染的節點對比正確,不然會導致瀏覽器端會重新渲染。另外一個就是要自己實現工程化,這個也是比較繁瑣的。

其他的就是數據脫水注水,組件查找,以及一些基礎能力的支持(方便使用者開發)。

從開始想做到現在開發完,都是在不忙的時候寫寫,差不多過去了2個月了。現在這個 ssr 骨架基本完成,我也寫了一個 demo可以看看。

krs – ssr 骨架介紹

這個 ssr 開發骨架是基於 koa2 、react16、webpack4、babel7,所以我把這個項目命名為 krs。

krs幾個特性介紹

最清涼(輕量)的 react ssr 應用開發骨架

  • 上手快速: 都是你熟悉的事物,基於 koa2 react16 ssr 搭建
  • 雙模式無縫切換: 支持SSR/CSR兩種渲染模式,只需更改配置屬性即可,也可以對組件設置按需渲染模式
  • 路由分治管理: 你寫你的路由,我寫我的路由,krs 自動合併,不再需要維護整個路由表
  • 路由動靜結合: 支持組件的按需加載設置,A 路由動態吧,B 路由靜態
  • 偽 pwa 支持: 訪問過的路由中的 state 可按需設置本地緩存,頁面二次訪問可無接口請求
  • 開放: 代碼完全開放,純白盒,完全可以作為個人的 ssr 學習參考資源

快速開始

如何讓krs 在你的機器上快速的跑起來?

在這裡我提供了一個腳手架,方便你快速創建項目,並進入開發。

//創建項目  $ npm install mmkrs-cli -g  $ mmkrs -i ---> select project ---> <Your Project Name>  $ cd <Your Project Name>  $ npm i  $ npm run dev //本地開發監聽模式  $ open http://<Your local ip>:8808    //快速創建頁面  $ cd <Your Project Name>  $ mmkrs -i -----> select page ----> <Your  pageName>  $ open http://<Your local ip>:8808/<Your  pageName>    //結束

路由配置

如果你想配置一個頁面的路由地址,那該如何配置呢?

為了方便維護和擴展,krs 把路由進行了分治管理,每個頁面的路由都是獨立的,只需要單獨的配置即可,避免了多人維護帶來的衝突和一系列的問題。

  • src/pages 目錄下創建一個頁面目錄 如:detail
  • detail/內創建入口組件
  • detail/config內創建 route.js 這就是當前頁面的路由配置文件
import React from 'react';  import BaseBundle from '../../../routes/route-base-bundle';    const LazyPageCom = (props) => (      <BaseBundle load={() => import(/*webpackChunkName:"chunk-detail"*/'../index')}>          {(CompIndex) => <CompIndex {...props} />}      </BaseBundle>  );    export default [      {          path:'/detail',          component: LazyPageCom,          exact:true      },      {          path:'/detail/:id',          component: LazyPageCom      }  ]

你只需要修改 webpackChunkName 的名稱和 export 導出的參數即可,當然也可以對當前頁面配置多個路由,默認已經支持了路由按需,所以如果不需要的話可以直接指向原始組件即可。

數據預取

什麼是數據預取?

csr 模式下我們的數據都是在瀏覽器端請求和渲染的,但是 ssr 模式需要在 node 端進行數據的獲取和渲染,這個渲染是指生成 html 內容。這樣瀏覽器端拿到

html 後,直接渲染html,就不再需要瀏覽器自己去請求數據了。

上一步已經創建了一個頁面的入口組件和路由的配置,那頁面入口組件也沒什麼奇怪的,和平時創建組件差不多。

  • 需要繼承一個 krs 的基礎組件,為我們封裝了一些基礎數據獲取和存儲功能

需要設置 static contextType = RootContext 為的是讓組件可以獲得全局的數據

  • 聲明靜態數據預取方法 static async getInitialProps,數據的獲取就是從這個方法拿到的,這是一個同構方法 node 端和瀏覽器端都會調用
  • 設置 static async getInitialProps 的返回數據,返回數據有一個固定的格式,下面代碼會說明
  • componentDidMount內是否需要做數據的更新,如果需要更新可以調用getInitialProps方法
import React,{useContext} from 'react';  import { Link } from 'react-router-dom';  import RootContext from '../../app/route-context';//自定義 context  import KrsPageBase from '../../krs-base/common/components/krs-page-base';//基礎組件 頁面組件都需要繼承  import fetch from '../../common/fetch';//內置的 fech 模塊      export default class Index extends KrsPageBase{        constructor(props,context){          super(props,context);      }        enableSpaDataCache=true;//開啟 偽 pwa 數據緩存        //得到 context 對象      static contextType = RootContext;        //基礎參數的帶入      //opt={query:{},params:{}}      static async getInitialProps(krsOpt){//數據預取              if(__SERVER__){              //如果是服務端渲染的話  可以做的處理          }           const fetch1= fetch.postForm('/fe_api/a', {              data: { a: 4000 }          });           const fecth2= fetch.postForm('/fe_api/b', {              data: { c: 2000 }          });            const resArr =await fetch.multipleFetch(fetch1, fecth2);            //返回數據固定格式  page 代表頁面信息,支持 seo 的設置          //fetchData是接口返回的數據          return {              page:{                  tdk: {                      title: 'ksr 框架',                      keyword: 'ssr react',                      description: '我是描述'                  }              },              fetchData: resArr          }      }        componentDidMount(){           //數據更新 參考         //this.isSSR 標識當前頁面是否是 ssr 輸出         //this.hasSpaCacheData標識是否有偽 pwa 的緩存數據            if (!this.isSSR && !this.hasSpaCacheData){// 頁面如果是客戶端的需要重新獲取數據              Index.getInitialProps(this.props.krsOpt).then(data=>{                  this.setState({                      ...data                  },()=>{                      document.title=this.state.page.tdk.title;                  });              });          }      }        render(){            const {page,fetchData}=this.state;//獲得數據            //參考代碼,需要對數據做邊界容錯處理            return <div className="detailBox">               <div>             {                      page && <div><span>title:{page.tdk.title}</span>                      <span>ky:{page.tdk.keyword}</span>                      </div>             }             </div>             {                res && res.data.map(item=>{                     return <div key={item.id}>{item.keyId}:{item.keyName}---{item.setContent}</div>                 })             }          </div>      }  }

快捷鍵

上面已經將兩個非常重要的內容說完了。但是每次手動需要創建這麼多文件夾和頁面也是很浪費時間的。所以這裡在腳手架工具里提供了一個快捷命令,方便我們創建頁面,通過命令代替手動創建.

cd 項目目錄  mmkrs-cli -i --->select page ---> 輸入 pagename

操作完後就可以看到你配置的頁面路由已生效。

生產環境構建

npm run build

然後可以本地模擬查看:npm run build:start

生產環境部署

這個很簡單,只需要運行 根目錄的 app.js 即可

pm2 start app.js

更多自定義配置

更多的配置,比如靜態資源的 cdn 路徑配置,開發端口的配置,是否開啟 ssr 等

都可以在 /src/config/project-config.js內進行配置

Demo 演示(有點丑,別介意)

http://demo.krs.bigerfe.com 最好在 pc 上訪問

更多使用幫助和實現原理後續文章會發出,同時也會在 github 更新。

krs 做到了現在,基本的功能已完成,但是仍然可能存在一些問題和待改善的空間,所以我會長期的進行維護和更新這個項目。

看到的小夥伴如果有興趣可以幫助一起改進,提建議。

項目 github 地址:

https://github.com/Bigerfe/koa-react-ssr