styled-components 服務端渲染

2020-07-24 18:01 更新

服務端渲染

styled-components 通過樣式注水(with stylesheet rehydration)支持并發(fā)服務端渲染. 其核心思想是,每當在服務器上渲染應用時, 為 React 樹創(chuàng)建一個ServerStyleSheet 和一個 provider ,通過 context API 來接收樣式.

這不會影響全局樣式,例如 keyframes 或者 createGlobalStyle ,并且允 styled-components 與 React DOM 的 SSR API 共同使用.

設置

為了可靠的執(zhí)行 SSR,正確的生成客戶端 bundle,請使用 babel plugin. 它通過為每個 styled component 添加確定的 ID 來防止校驗錯誤. 更多信息請參考 tooling documentation .

對于 TypeScript 用戶, TS 大師 Igor Oleinikov 整合了webpack ts-loader / awesome-typescript-loader 工具鏈 TypeScript plugin 來完成類似的任務.

If possible, we definitely recommend using the babel plugin though because it is updated the most frequently. It's now possible to compile TypeScript using Babel, so it may be worth switching off TS loader and onto a pure Babel implementation to reap the ecosystem benefits.

示例

基本 API 的使用如下:

import { renderToString } from 'react-dom/server'
import { ServerStyleSheet } from 'styled-components'

const sheet = new ServerStyleSheet()
const html = renderToString(sheet.collectStyles(<YourApp />))
const styleTags = sheet.getStyleTags() // or sheet.getStyleElement();

collectStyles 方法將元素包裝進了 provider.也可以選擇直接使用 StyleSheetManager provider.確保不要再客戶端使用即可.

import { renderToString } from 'react-dom/server'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'

const sheet = new ServerStyleSheet()
const html = renderToString(
  <StyleSheetManager sheet={sheet.instance}>
    <YourApp />
  </StyleSheetManager>
)

const styleTags = sheet.getStyleTags() // or sheet.getStyleElement();

sheet.getStyleTags() 方法返回多個字符串的 <style> 標簽. 當向 HTML 輸出增加 CSS 時需要考慮這一點.

作為另一種選擇, ServerStyleSheet 實例也提供 getStyleElement() 方法,返回一個 React 元素的數(shù)組.

注意sheet.getStyleTags() 和sheet.getStyleElement() 只能在元素渲染和調用. 所以sheet.getStyleElement()中的組件不能與 合并為一個更大的組件.

Next.js

首先添加一個自定義的 pages/_document.js. 然后 復制這段邏輯 

import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const sheet = new ServerStyleSheet()
    const originalRenderPage = ctx.renderPage

    try {
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: (App) => (props) =>
            sheet.collectStyles(<App {...props} />),
        })

      const initialProps = await Document.getInitialProps(ctx)
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        ),
      }
    } finally {
      sheet.seal()
    }
  }
}

將服務端你渲染的樣式注入 <head>.


Streaming Rendering

styled-components 提供一個與ReactDOMServer.renderToNodeStream()搭配使用的流式 API . 一個流式實現(xiàn)需要以下兩部分:

在服務器上:

ReactDOMServer.renderToNodeStream 發(fā)出一個 styled-components 包裝過的"可讀"流. 當整個 HTML 塊被推到流上時,如果任何相應的樣式已經(jīng)可以渲染,一個樣式塊會被附加到 React HTML 并發(fā)送給客戶端瀏覽器.

import { renderToNodeStream } from 'react-dom/server'
import styled, { ServerStyleSheet } from 'styled-components'

// if you're using express.js, you'd have access to the response object "res"

// typically you'd want to write some preliminary HTML, since React doesn't handle this
res.write('<html><head><title>Test</title></head><body>')

const Heading = styled.h1`
  color: red;
`

const sheet = new ServerStyleSheet()
const jsx = sheet.collectStyles(<Heading>Hello SSR!</Heading>)
const stream = sheet.interleaveWithNodeStream(renderToNodeStream(jsx))

// you'd then pipe the stream into the response object until it's done
stream.pipe(
  res,
  { end: false }
)

// and finalize the response with closing HTML
stream.on('end', () => res.end('</body></html>'))

在客戶端里:

import { hydrate } from 'react-dom'

hydrate()
// your client-side react implementation

當客戶端注水完畢后, styled-components 將接管重新定位后的流式樣式并且注入動態(tài)樣式.


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號