SSR改造方案

一、背景

會員中心頁面上線后,經過需求的不停迭代,頁面的fsp和秒開率表現的并不理想,還有不少提升的空間。基于此,需要做一些性能優化來進行改造。

針對其中的勛章首頁頁面的特點(偏靜態的內容展示場景),非常適合在該頁使用SSR進行優化改造。同時勛章頁面低端機型的性能表現明顯差于高端機型,通過服務端渲染,可以抹平手機網絡請求和渲染性能造成的差距,有效的提升低端機型的性能表現。

勛章首頁:偏靜態的內容展示場景

機型性能表現差別:

機型網絡請求差別:

二、技術方案

SSR簡介

SSR(Server Side Rendering): 服務端渲染

React的 SSR :

? 也叫同構,是服務端渲染與客戶端渲染的一個整合

? 它是在不改變前端開發方式的情況下實現服務端渲染的

? 用來解決純客戶端渲染帶來的?屏渲染慢、SEO不友好的問題

SSR和CSR的對比

csr模式:

ssr模式:

從圖中可以看出SSR 渲染的大致過程是:請求 Node 服務 -> Node 服務請求后端接口 -> Node 服務獲取數據后渲染首屏HTML -> 瀏覽器解析 HTML -> 加載靜態資源(CSS/JS)-> 渲染首屏。

可以看到 SSR 相比于 CSR,將首屏接口請求與渲染移到計算能力更強的服務端。

從時序上看,SSR 不需要加載 JS 文件,即可完成首屏呈現;

從接口請求的環境來看,SSR 請求頁面的數據和首屏 HTML 的渲染是在服務端進行的,相對于 CSR,在接口請求上,尤其是首屏依賴多個接口時,具有內網/專線的可靠、低時延優勢,可極大降低客戶端網絡環境不確定性和不穩定性所造成的網絡耗時。

SSR使用場景

適?場景

? 需要在瀏覽器上做 SEO

? 已經有?個運行中的 React 應用,需要最佳的性能并愿意為增加的服務?資源付費

? 數據展示型??

不適?場景

? 不需要做SEO

? 資源短缺(服務器or開發?員)

? ??需要大量計算(包含大量圖表)

三、項目中的核心實現

  1. 通過 getSSRData獲取到數據,并使用store方法將數據直接更改,之后進行服務端渲染。
const getSSRData = async (ctx: Context): Store => {
  const { medalStore } = stores
  const medalService = new MedalService()
  const { token, uuid, userid } = ctx.query
  try {
    const res = await medalService.getMedals(ctx, {
      headers: { token },
      params: {
        userid,
        uuid,
      },
    })
    if (res?.data) {
      const {
        recentMedals = [],
      } = res?.data
      medalStore.setRecentMedals(recentMedals)
      medalStore.setIsSsrData(true)
    }
  } catch (e) {
    console.log(e)
  }
  return medalStore
}

const serverEntry =
  (App: () => JSX.Element, callback?: Function): TRender =>
  async (url: string, ctx?: Context) => {
    enableStaticRendering(true)
    const stores = callback ? await callback(ctx) : undefined
    return {
      html: ReactDOMServer.renderToString(
        <Provider {...stores}>
          <App />
        </Provider>
      ),
      data: stores,
    }
  }
export const render = serverEntry(App, getSSRData)

備注:renderToString

? 將 React Component 轉化為 HTML 字符串

? 生成的 HTML 的 DOM 會帶有額外屬性(最外層 DOM 會有 data-reactroot 屬性)

2. 前端合并store,并進行水合操作

export const clientEntry = (App: () => JSX.Element, Store?: Store): void => {
  const initialStores = window.__INITIAL_STATE__ ?? {}
  const mergeMedalStore = Object.assign(stores.medalStore, initialStores.medalStore)
  const hydrateDOM = (
    <Provider medalStore={mergeMedalStore}>
      <App />
    </Provider>
  )

  ReactDOM.hydrate(hydrateDOM, document.getElementById('app'))
}

clientEntry(App)

備注: ReactDOM.hydrate

? 將事件監聽器?掛載到現有的 DOM 元素,而不是掛載整個 DOM

? 會修復客戶端渲染與服務端渲染中的部分不一致的場景

  • 標簽不一致,直接使?用客戶端渲染結果

  • ?本內容不?致,替換文本內容

  • 不修復屬性不一致的情況

四、收益

整體

整體來看,勛章頁的秒開率從39%到了52%,提升了13%。首屏時間從2.7s到了2.9s,提升了200ms。

可以看到SSR明顯提升了秒開率,但是對于fsp并沒有相應的提升。

為啥呢?

React SSR 與 CSR ?屏渲染時間對?

  • Time To First Byte(TTFB): ?字節時間,用于衡量?絡和服務?響應性能

  • First Meaningful Paint(FMP):?面主要內容出現在屏幕上的時間點

  • Time To Interactive (TTI):布局已趨于穩定、關鍵的?網絡字體可?見、主要線程?以處理?戶輸入的時間點


從圖中可以看出SSR相比CSR的首屏時間是不一定會變好的。

所以整體效果算是符合預期的。

不同機型的表現分析

17和18號的數據

22和23號的數據

從數據中可以看到低端機型的秒開率由于ssr的改造,提升特別明顯。印證了之前說過的通過服務端渲染,可以抹平手機網絡請求和渲染性能造成的差距,有效的提升低端機型的性能表現。

而fsp(tp90)在高端機型上表現的反而差了,對低端機型的表現是提升的。這也可以理解。由于渲染主要放在node層做了,高端機型的網絡請求和渲染性能優勢體現的就不明顯了,而對低端機型來說反而變好了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容