web-dev-qa-db-ja.com

クライアントルーティング(react-routerを使用)およびサーバー側ルーティング

私は考えていましたが、クライアントとサーバー間のルーティングと混同しています。 Webブラウザにリクエストを送信する前にサーバー側のレンダリングにReactJSを使用し、クライアント側のルーティングとして反応ルーターを使用して、SPAとして更新せずにページを切り替えると仮定します。

頭に浮かぶのは:

  • ルートはどのように解釈されますか?たとえば、ホームページ(/home)から投稿ページ(/posts)へのリクエスト
  • サーバー側またはクライアントで、ルーティングはどこに行きますか?
  • それがどのように処理されるかをどのように知るのですか
106
heartmon

、この回答はReactルーターバージョン0.13.xを対象としています- 次のバージョン1. は実装の詳細が大幅に異なるようです

サーバ

これは、react-routerを備えた最小限のserver.jsです:

var express = require('express')
var React = require('react')
var Router = require('react-router')

var routes = require('./routes')

var app = express()

// ...express config...

app.use(function(req, res, next) {
  var router = Router.create({location: req.url, routes: routes})
  router.run(function(Handler, state) {
    var html = React.renderToString(<Handler/>)
    return res.render('react_page', {html: html})
  })
})

routesモジュールがルートのリストをエクスポートする場所:

var React = require('react')
var {DefaultRoute, NotFoundRoute, Route} = require('react-router')

module.exports = [
  <Route path="/" handler={require('./components/App')}>
    {/* ... */}
  </Route>
]

サーバーにリクエストが行われるたびに、着信URLを静的な場所として構成されたシングルユースRouterインスタンスを作成します。これは、ルートツリーに対して解決され、適切な一致ルートをセットアップして、コールバックしますレンダリングされる最上位のルートハンドラと、各レベルで一致した子ルートのレコードを使用します。これは、ルート処理コンポーネント内で<RouteHandler>コンポーネントを使用して、一致した子ルートをレンダリングするときに参照されるものです。

ユーザーがJavaScriptをオフにしている場合、またはロードが遅い場合、クリックしたリンクは再びサーバーにヒットします。これは上記のように再び解決されます。

クライアント

これは、react-routerを使用した最小のclient.jsです(同じルートモジュールを再利用):

var React = require('react')
var Router = require('react-router')

var routes = require('./routes')

Router.run(routes, Router.HistoryLocation, function(Handler, state) {
  React.render(<Handler/>, document.body)
})

Router.run()を呼び出すと、舞台裏でルーターインスタンスが作成されます。これは、アプリ内を移動するたびに再利用されます。URLは、単一のリクエストが存在するサーバーではなく、クライアントで動的にできるため固定URL。

この場合、HistoryLocationを使用します。これは History AP​​I を使用して、戻る/進むボタンを押したときに正しいことを確認します。 URLを変更するHashLocationもありますhash履歴エントリを作成し、ナビゲーションをトリガーするために window.onhashchange イベントをリッスンします。

React-routerの<Link>コンポーネントを使用する場合、ルートの名前であるto propに加えて、ルートに必要なparamsおよびqueryデータを指定します。このコンポーネントによってレンダリングされる<a>には、最終的にルーターインスタンスでrouter.transitionTo()を呼び出すonClickハンドラーがあります。

  /**
   * Transitions to the URL specified in the arguments by pushing
   * a new URL onto the history stack.
   */
  transitionTo: function (to, params, query) {
    var path = this.makePath(to, params, query);

    if (pendingTransition) {
      // Replace so pending location does not stay in history.
      location.replace(path);
    } else {
      location.Push(path);
    }
  },

通常のリンクの場合、これは最終的にlocation.Push()を呼び出します。これは履歴の設定の詳細を処理し、戻るボタンと進むボタンで操作できるようにし、router.handleLocationChange()にコールバックしてルーターに処理を続行できることを通知します新しいURLパスへの移行。

次に、ルーターは新しいURLで独自のrouter.dispatch()メソッドを呼び出します。このメソッドは、構成されたルートのいずれがURLと一致するかを決定する詳細を処理し、一致したルートに存在する transition hooks を呼び出します。これらのトランジションフックをルートハンドラーに実装して、ルートが近づいたり近づいたりするときにアクションを実行し、好みに合わない場合にトランジションを中止することができます。

遷移が中断されなかった場合、最後のステップは、Router.run()に指定したコールバックを、トップレベルハンドラーコンポーネントと、URLと一致したルートのすべての詳細を含む状態オブジェクトで呼び出すことです。最上位のハンドラーコンポーネントは、実際にはRouterインスタンスそのものであり、一致した最上位のルートハンドラーのレンダリングを処理します。

上記のプロセスは、クライアントで新しいURLに移動するたびに再実行されます。

サンプルプロジェクト

135
Jonny Buchanan

1.0では、React-Routerは、peerDependencyとして history モジュールに依存します。このモジュールは、ブラウザでのルーティングを扱います。デフォルトでは、React-RouterはHTML5 History API(pushStatereplaceState)を使用しますが、ハッシュベースのルーティングを使用するように設定できます(以下を参照)

これで、ルートの処理はバックグラウンドで行われ、ReactRouterはルートが変更されると新しい小道具をルートハンドラーに送信します。ルーターには、ルートが変更されるたびに新しいonUpdate propコールバックがあり、ページビューの追跡、または<title>の更新などに役立ちます。

クライアント(HTML5ルーティング)

import {Router} from 'react-router'
import routes from './routes'

var el = document.getElementById('root')

function track(){
  // ...
}

// routes can be children
render(<Router onUpdate={track}>{routes}</Router>, el)

クライアント(ハッシュベースのルーティング)

import {Router} from 'react-router'
import {createHashHistory} from 'history'
import routes from './routes'

var el = document.getElementById('root')

var history = createHashHistory()

// or routes can be a prop
render(<Router routes={routes} history={history}></Router>, el)

サーバ

サーバーでは、ReactRouter.matchを使用できます。これは サーバーレンダリングガイド

import { renderToString } from 'react-dom/server'
import { match, RoutingContext } from 'react-router'
import routes from './routes'

app.get('*', function(req, res) {
  // Note that req.url here should be the full URL path from
  // the original request, including the query string.
  match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
    if (error) {
      res.status(500).send(error.message)
    } else if (redirectLocation) {
      res.redirect(302, redirectLocation.pathname + redirectLocation.search)
    } else if (renderProps) {
      res.status(200).send(renderToString(<RoutingContext {...renderProps} />))
    } else {
      res.status(404).send('Not found')
    }
  })
})
26
tom