web-dev-qa-db-ja.com

ReactルーターのHashRouterは<base>タグurlにリダイレクトします

React現在のページに制限されているアプリケーション、/some/static/pageのアプリケーションは<base href="/"> in <head>はすべてのページにあり、それに依存しています。これは変更できません。

React 16、Reactルーター4および<HashRouter>

export class App extends React.Component {
    render() {
        return (
            <HashRouter>
                <div>
                    <Route exact path="/" component={Root} />
                </div>
            </HashRouter>
        );
    }
}

すべてのルートはテスト目的で無効にすることができますが、これにより動作が変わることはありません。

これはcreate-react-appプロジェクトです 問題を示しています。複製する手順は次のとおりです。

  • npm i
  • npm start
  • http://localhost:3000/some/static/pageに移動します

HashRouterは、明らかに<base>の影響を受けます。初期化時に/some/static/pageから/#/にリダイレクトしますが、/some/static/page#/または/some/static/page/#/(IE 11)。

/#/にリダイレクトされる前に、Rootコンポーネントがすぐにスプラッシュします。

/foo/#/の場合は<base href="/foo">にリダイレクトし、/some/static/page/#/タグが削除されると<base>にリダイレクトします。

この問題は、ChromeおよびFirefox(最新バージョン)に影響しますが、Internet Explorer(IE 11)には影響しません。)

<HashRouter><base>の影響を受けるのはなぜですか?ここで使用されているのは、ロケーションパスに影響を与えるものではなく、ハッシュのみであるためです。

これはどのように修正できますか?

10
Estus Flask

実際これはhistoryから。 コード が表示される場合、createHashHistoryのみを使用し、childrenを設定します。したがって、これと同等です:

import React from 'react';
import { Route, Router } from 'react-router-dom';
import { createHashHistory } from 'history';

const Root = () => <div>Root route</div>;
export default class App extends React.Component {

  history = createHashHistory({
    basename: "", // The base URL of the app (see below)
    hashType: "slash", // The hash type to use (see below)
    // A function to use to confirm navigation with the user (see below)
    getUserConfirmation: (message, callback) => callback(window.confirm(message)),
  });


  render() {
    return (

      <Router history={this.history}>
      <div>Router
        <Route exact path="/" component={Root} />
      </div>
      </Router>
      );
    }
}

同じ問題が表示されます。次に、historyコードを次のように変更した場合:

import {createBrowserHistory } from 'history';

...

history = createBrowserHistory({
    basename: "", // The base URL of the app (see below)
    forceRefresh: false, // Set true to force full page refreshes
    keyLength: 6, // The length of location.key
    // A function to use to confirm navigation with the user (see below)
    getUserConfirmation: (message, callback) => callback(window.confirm(message))
});

問題はなくなりますが、hashは絶対に使用しないでください。したがって、問題はHashRouterからではなく、historyからです。

これはhistoryから来るので、これを見てみましょう thread 。そのスレッドを読んだ後、これはhistoryからfeatureであると結論付けることができます。

したがって、<base href="/">を設定すると、hash(#)を使用しているため、ブラウザがロードされたとき(実際はcomponentDidMountの後に)hash(#)が追加されますあなたの場合some/static/page => some/static/page + / => / + #/ => /#/componentDidMount set debuggerをチェックインして、ルートを追加する前にキャッチできます。


解決

単に、要素<base href>を削除するか、HashRouterを使用しないでください。

特定のcomponentから回避する必要があるが、避けたい場合は、これをclassの前に置いてください

const base = document.querySelector("base");
base.setAttribute('href', '');

更新

リンクを保持し、baseルーターを使用するためにhashタグを保持したいので、ここで私が思うに近い解決策です。

1。タグbaseを空に設定します。

const base = document.querySelector('base');
base.setAttribute('href', '');

そのコードをAppコンポーネント(ルートラップコンポーネント)に入れて1回呼び出します。

2。 componentDidMountが設定し直した場合

componentDidMount() {
  setTimeout(() => {
    base.setAttribute('href', '/');
  }, 1000);
}

タイムアウトを使用して、反応したレンダリング仮想domを待ちます.

これは非常に近いと思います(テストしてみてください)。 hashルーターを使用しているため、インデックスhtmlからのリンクは安全です(reactでオーバーライドするのではなく、baseタグで保持します)。また、cssリンク<link rel="stylesheet" href="styles.css">でも機能します。

2
hendrathings

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base#Hint が表示される場合、#target URLであっても<base>を使用している予想される動作です。

そして https://reacttraining.com/react-router/web/api/HashRouter basename:string section:適切にフォーマットされたbasenameにはスラッシュが必要ですが、スラッシュなし

したがって、HashRouter要素に別のベース名を定義するか、<base>から末尾のスラッシュを削除する必要があります。

1
Dhruv Murarka

history パッケージの問題です。解決済みです。 this pr をご覧ください

一時的な修正として、package.jsonでこのブランチを指定することをお勧めします

"dependencies": {
  ...
  "history": "git://github.com/amuzalevskiy/history.git",
  ...
}

そして、修正が元のブランチにマージされると、これを修正されたメインのnpmモジュールに戻します


リポジトリに関して:publishスクリプトを実行せずに元のリポジトリを使用することは不可能なので、 microbouji solutionnpm run buildを実行し、結果をコミットしました

1

this answer で説明されている修正を適用するHOCで終了しました:

function withBaseFix(HashRouter) {
    return class extends React.Component {
        constructor() {
            super();
            this.baseElement = document.querySelector('base');
            if (this.baseElement) {
                this.baseHref = this.baseElement.getAttribute('href');
                this.baseElement.setAttribute('href', '');
            }
        }

        render() {
            return <HashRouter {...this.props}>{this.props.children}</HashRouter>;
        }

        componentDidMount() {
            if (this.baseElement)
                this.baseElement.setAttribute('href', this.baseHref);
        }
    }
};

const FixedHashRouter = withBaseFix(HashRouter);

...
<FixedHashRouter>
    <div>
        <Route exact path="/" component={Root} />
    </div>
</FixedHashRouter>
...
1
Estus Flask

HashRouterおよび<base>タグに関する観察は正しいです。ブラウザーの違いに関する問題をここに提出しました: https://github.com/ReactTraining/history/issues/574 および対応するPRをここに修正します: https:// github。 com/ReactTraining/history/pull/577

当面、必要なすべてのルーティングについてはわかりませんが、reactアプリが/some/static/page/の下に完全に存在する場合、おそらく次のように動作させることができます。

<BrowserRouter basename="/some/static/page">

1
Elian Ibaj