React-Router v5文檔翻譯之服務端渲染

本項目Github地址,歡迎star

目錄

由于服務端是無狀態(tài)的,所以服務端渲染和客戶端渲染并不相同. 最基本的就是我們封裝app時, 使用無狀態(tài)的 < StaticRouter >來代替< BrowserRouter >, 使用來自于服務端的請求url來匹配路由。接下來我們會討論 context 屬性

// client
<BrowserRouter>
  <App/>
</BrowserRouter>

// server (not the complete story)
<StaticRouter
  location={req.url}
  context={context}
>
  <App/>
</StaticRouter>

當你在客戶端渲染 < Redirect >, 瀏覽器地址欄會改變狀態(tài)使我們能看到新的頁面,然而在一個靜態(tài)的服務環(huán)境下, 我們不能夠改變app的狀態(tài)。代替的是, 我們將渲染的結(jié)果賦給context屬性. 如果我們找到了 context.url, 那么我們知道這個app重定向了. 這允許我們向服務端發(fā)送一個重定向請求

const context = {}
const markup = ReactDOMServer.renderToString(
  <StaticRouter
    location={req.url}
    context={context}
  >
    <App/>
  </StaticRouter>
)

if (context.url) {
  // Somewhere a `<Redirect>` was rendered
  redirect(301, context.url)
} else {
  // we're good, send the response
}

添加明確的應用內(nèi)容信息

路由只能添加 context.url. 但是你可能想要發(fā)送重定向的301或302的響應?;蛟S你在某些特殊的UI渲染后需要發(fā)送一個404響應, 又或者在客戶端沒有認證的情況下發(fā)送401。 context屬性是屬于你的, 所以你可以任意改變她. 下面是分辨301與302重定向的方法。

const RedirectWithStatus = ({ from, to, status }) => (
  <Route render={({ staticContext }) => {
    // there is no `staticContext` on the client, so
    // we need to guard against that here
    if (staticContext)
      staticContext.status = status
    return <Redirect from={from} to={to}/>
  }}/>
)

// somewhere in your app
const App = () => (
  <Switch>
    {/* some other routes */}
    <RedirectWithStatus
      status={301}
      from="/users"
      to="/profiles"
    />
    <RedirectWithStatus
      status={302}
      from="/courses"
      to="/dashboard"
    />
  </Switch>
)

// on the server
const context = {}

const markup = ReactDOMServer.renderToString(
  <StaticRouter context={context}>
    <App/>
  </StaticRouter>
)

if (context.url) {
  // can use the `context.status` that
  // we added in RedirectWithStatus
  redirect(context.status, context.url)
}

404, 401, 或其他狀態(tài)

我們現(xiàn)在可以做到和上面一樣的事,創(chuàng)建一個包含想要內(nèi)容的組件,當收到不同的的狀態(tài)碼時可以在應用的任何地方渲染該組件。

function Status({ code, children }) {
  return (
    <Route
      render={({ staticContext }) => {
        if (staticContext) staticContext.status = code;
        return children;
      }}
    />
  );
}

現(xiàn)在,當你想要給靜態(tài)內(nèi)容添加一個狀態(tài)碼時,你可以在應用的任何地方渲染一種狀態(tài)。

function NotFound() {
  return (
    <Status code={404}>
      <div>
        <h1>Sorry, can’t find that.</h1>
      </div>
    </Status>
  );
}

// somewhere else
<Switch>
  <Route path="/about" component={About} />
  <Route path="/dashboard" component={Dashboard} />
  <Route component={NotFound} />
</Switch>;

組合所有內(nèi)容

雖然這不是一個真正的應用,但是她展現(xiàn)了將所有內(nèi)容組合在一起所需的常規(guī)部分

import { createServer } from 'http'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import { StaticRouter } from 'react-router'
import App from './App'

createServer((req, res) => {
  const context = {}

  const html = ReactDOMServer.renderToString(
    <StaticRouter
      location={req.url}
      context={context}
    >
      <App/>
    </StaticRouter>
  )

  if (context.url) {
    res.writeHead(301, {
      Location: context.url
    })
    res.end()
  } else {
    res.write(`
      <!doctype html>
      <div id="app">${html}</div>
    `)
    res.end()
  }
}).listen(3000)

然后是客戶端

import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import App from './App'

ReactDOM.render((
  <BrowserRouter>
    <App/>
  </BrowserRouter>
), document.getElementById('app'))

數(shù)據(jù)加載

要做到這一點有很多不同的方法,對此并沒有最佳的實踐。所以我們尋求多種方法的不同組合方式,而不是規(guī)定或傾向某一種。我們相信React Router可以在你的應用的規(guī)則限制下找到一種合理的方式。

最主要的約束是你希望在頁面渲染前加載完數(shù)據(jù)。React Router暴露了一個matchPath靜態(tài)函數(shù),你可以用她來進行路由匹配。你可以在服務端用這個函數(shù)來確定哪些依賴的數(shù)據(jù)是要在渲染前完成的。

這種方法的特點是在進行實際跳轉(zhuǎn)前設定好靜態(tài)匹配規(guī)則,在實際跳轉(zhuǎn)前就已經(jīng)知道要使用哪些數(shù)據(jù)。

const routes = [
  { path: '/',
    component: Root,
    loadData: () => getSomeData(),
  },
  // etc.
]

然后使用這些規(guī)則在應用中渲染你的路由

import { routes } from './routes'

const App = () => (
  <Switch>
    {routes.map(route => (
      <Route {...route}/>
    ))}
  </Switch>
)

在服務端你可能會做這些;

import { matchPath } from 'react-router-dom'

// inside a request
const promises = []
// use `some` to imitate `<Switch>` behavior of selecting only
// the first to match
routes.some(route => {
  // use `matchPath` here
  const match = matchPath(req.url, route)
  if (match)
    promises.push(route.loadData(match))
  return match
})

Promise.all(promises).then(data => {
  // do something w/ the data so the client
  // can access it then render the app
})

最后,客戶端需要獲取數(shù)據(jù)。我們并不是給你的應用規(guī)定數(shù)據(jù)加載的模式,但這些是在開發(fā)中常用的形式。

你可能會對我們的進行數(shù)據(jù)加載和靜態(tài)路由配置的React Router Config包感興趣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容