React筆記五:使用Next.js實現React SSR的優雅降級

前言

React SSR最成熟的開源框架是Next.js,這么多年保持著強勁的生命力,它的創始團隊vercel(曾用名zeit),如今更關注于SSR和serverless的結合。隨著服務端的容器化技術以及serverless技術不斷完善,在國外可能SSR的降級已經不是一個必要命題。但是,考慮到國內的服務環境,今天我們還是有必要從前端的技術點討論一下如何去實現SSR的優雅降級。

舊版本的Next.js是利用getInitProps實現服務端渲染以及靜態站點生成。在Next.js 9.3版本后,getInitProps這個api被替換成為三個不同的api,分別是:

  • getStaticProps (靜態頁面生成SSG): 構建的時候生成頁面
  • getStaticPaths (靜態頁面生成SSG): 根據構建內容去生成動態路由
  • getServerSideProps (服務端渲染SSR): 在每個請求中在服務端獲取數據渲染頁面

這三個api的使用是對一個項目中不同頁面的更細程度的劃分,它可以有效區分哪些頁面走SSR、哪些頁面走CSR和SSG。高效的劃分了這三種不同的渲染模式。

What is JAMstack ?

靜態頁面生成SSG這種模式更加符合JAMstack的標準,所有的頁面都是提前預渲染的,靜態的頁面可以直接托管在CDN上,有效降低運維成本,有助于你“高效下班”。Next.js官方建議你優先使用靜態頁面生成,不得已才使用服務端渲染。但是靜態頁面不能滿足你的所有case。只有以下情況才比較適合靜態頁面生成:

  • 數據能通過CMS接口有效渲染
  • 數據能夠公開緩存,并且不能用戶特有的
  • 頁面必須預渲染,并且SEO敏感

Next.js已經能夠在一個項目不同路由支持不同的渲染模式。

源碼參考 brandonxiang/example-nextjs ,頁面的邏輯放在modules文件夾里面,用一個自定義的函數getPrerenderProps來保證頁面的預渲染邏輯。這個預渲染邏輯如下,即獲取數據傳遞到組件當中與Next.js的預渲染api類似。

// modules/Home.tsx
export const getPrerenderProps =  async (ctx) => {
  // SSG讀取環境變量,并作為兜底參數
  const defaultLimits =  process.env.limits || 0;
  // SSR和CSR動態渲染從URL上獲取參數
  const _limits =  (ctx?.query?._limits) || defaultLimits;
  // 獲取遠程動態數據
  const res = await axios.get(
   'https://jsonplaceholder.typicode.com/photos?_limit=' + _limits
  )
  // 傳遞給各種渲染模式
  return { props: { photos: res.data } }
}

自定義頁面渲染函數Page來保證頁面dom的渲染,這里的目標是“一份核心代碼,多種渲染模式”。數據photos則會在頁面中渲染。

// modules/Home.tsx
function Home({ photos }) {
  let _photos =  photos || []
  return (
    <div className="photos">
      {
        _photos.map((photo, index) => (
          <figure key={index}>
            <img src={photo.thumbnailUrl} alt={photo.title} />
            <figcaption>{photo.title}</figcaption>
          </figure>
        ))
      }
    </div>
  )
}

export const Page = Home;

然后將它渲染到三種不同的模式當中。由于 Next.js 的文件路由設定,頁面需要被設置成為三種:

  • index.js SSR模式
  • index_ssg.js SSG模式
  • index_csr.js CSR模式

Next.js如何實現SSR

SSR模式需要將自定義的getPrerenderProps 輸出到頁面級別Next.js API的getServerSideProps當中,獲取數據的邏輯將會提前在服務端完成。此時,服務端可以實現頁面的動態渲染。Page則返回給整個頁面的渲染函數。

// index.js
export { 
  Page as default, 
  getPrerenderProps as getServerSideProps 
} from '../modules/Home';

Next.js如何實現CSR

CSR模式則是自定義的getPrerenderProps 在useEffect中渲染,在頁面加載之后,重新對頁面進行渲染,達到一個客戶端渲染的效果。路由參數發生變化,頁面會重新進行渲染,保證的頁面的動態可用。這種模式頁面的渲染會比較慢,時長主要是請求時長。

// index_csr.js
export default () => {
    const router = useRouter();
    const [extraProps, setExtraProps] = useState({});

    useEffect(() => {
        getPrerenderProps(router).then(({props}) => {
            setExtraProps(props);
        }) 
    }, [router]);

    return <Page {...extraProps}/>
}

Next.js如何實現SSG

SSG則是靜態預渲染,參數不能動態從路由傳入,只能構建的時候以環境變量的形式傳入,所以頁面渲染需要采用特殊的兼容讀取方式。

將自定義的getPrerenderProps 輸出到頁面級別Next.js API的getStaticProps當中,實現靜態渲染。

// index_ssg.js
export { 
  Page as default, 
  getPrerenderProps as getStaticProps 
} from '../modules/Home';

如何將SSR降級成為CSR

SSR服務端渲染由于是依賴服務器資源,在流量過大的情況下,有可能會出現服務不可用的情況,返回特殊的錯誤碼例如500等。這時候我們可以實現優雅降級,利用 nginx 做對應的流量分發,當SSR頁面返回異常錯誤的時候,nginx會將流量導入到CSR頁面當中。

SSR頁面和CSR頁面基于Next.js采用同樣的業務邏輯編寫方式,有效保證頁面邏輯的一致性,一份代碼兩端復用。

SSR優雅的降級

總結

Next.js是非常成熟高效的服務端渲染框架,本文通過一些取巧的方式來實現“一份代碼,多種渲染方式”,既能提高頁面的性能,也能夠保證頁面的優雅降級。多種渲染模式采用同一份代碼,保證了邏輯的一致性,有效地為QA節省了回歸人力。在“質量”和“性能”上找到了一個很好的平衡點。

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

推薦閱讀更多精彩內容