1. ホーム
  2. javascript

[解決済み] クライアントルーティング(react-router使用)およびサーバーサイドルーティング

2022-10-15 02:08:34

質問

クライアントとサーバーの間のルーティングについて考えているのですが、混乱しています。サーバーサイドのレンダリングにReactJSを使用してからWebブラウザにリクエストを返し、クライアントサイドのルーティングとしてreact-routerを使用してSPAとして更新せずにページを切り替えたとします。

思い浮かぶのは

  • ルートはどのように解釈されるのでしょうか?例えば、トップページからのリクエスト ( /home ) から Posts ページ ( /posts )
  • ルーティングはサーバーサイドかクライアントか、どちらで行うのでしょうか?
  • どのように処理されるかを知るには?

どのように解決するのですか?

この回答はReact Routerバージョン0.13.xを対象としていることに注意してください。 次期バージョン1.0 は大幅に異なる実装を持つように見えます。

サーバー

これは、最小限の server.js をreact-routerで表示しています。

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 モジュールはRoutesのリストをエクスポートします。

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

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

サーバへのリクエストが行われるたびに、一回だけ使用する Router インスタンスを作成します。これは適切なマッチしたルートをセットアップするためにルートツリーに対して解決され、レンダリングされるトップレベルのルートハンドラと各レベルでマッチした子ルートの記録とともにコールバックされます。これは <RouteHandler> コンポーネントを使用して、マッチした子ルートをレンダリングするときに参照されます。

ユーザーが JavaScript をオフにしているか、読み込みに時間がかかっている場合、ユーザーがクリックしたリンクが再びサーバーにヒットし、上記のように再び解決されます。

クライアント

これは最小限の client.js をreact-router(同じroutesモジュールを再利用)で実装しています。

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() を呼び出すと、舞台裏であなたのために Router インスタンスが作成されます。これは、1 つのリクエストが固定 URL であるサーバー上とは対照的に、クライアント上では URL が動的であり得るため、あなたがアプリ内を移動するたびに再利用されます。

この例では HistoryLocation を使用していますが、これは History API を使用して、戻る/進むボタンを押したときに正しい処理が行われるようにします。また HashLocation があり、これは URL を変更する hash を変更して履歴のエントリを作成し window.onhashchange イベントをリスンしてナビゲーションをトリガーします。

react-routerの <Link> コンポーネントを使用する場合、このコンポーネントには to プロップを与えます。これはルートの名前であり、さらに任意の paramsquery のデータが必要です。その <a> このコンポーネントによってレンダリングされた onClick ハンドラを持ち、最終的に router.transitionTo() を呼び出すことになります。

  /**
   * 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 パスへの移行を進めることができることを知らせます。

次にルーターは自身の router.dispatch() メソッドを呼び出します。このメソッドは、設定されたルートのどれが URL にマッチするかを決定する詳細を処理し、次にすべての 遷移フック を呼び出します。これらの遷移フックを任意のルート ハンドラに実装して、ルートがナビゲートされなくなるか、またはナビゲートされるときに何らかのアクションを実行し、好みに合わない場合は遷移を中断することができます。

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

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

プロジェクト例