React Router的HashRouter重定向到<base />标记网址

时间:2018-03-24 16:47:34

标签: javascript reactjs react-router base-url base-tag

我的非SPA服务器端应用程序的React应用程序仅限于当前页面/some/static/page该应用在所有网页上的<base href="/">中都有<head>并且依赖于它,无法更改。

以下是React 16,React Router 4和<HashRouter>的基本示例:

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

出于测试目的,可以禁用所有路线,但这不会改变行为。

显示问题的

Here is create-react-app project。复制它的步骤是:

  • 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>影响?它在这里使用的确是因为它不应该影响位置路径,只有哈希。

如何解决这个问题?

5 个答案:

答案 0 :(得分:2)

实际上来自history。如果您看到their code,则他们只使用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 =&gt; some/static/page + / =&gt; / + #/ =&gt; /#/。您可以在追加路线之前签入componentDidMount设置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组件(root wrap component)中以调用一次。

<强> 2。 componentDidMount将其设置回来

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

使用超时等待反应完成渲染虚拟dom。

这是非常接近的,我认为(测试过)。因为您正在使用hash路由器,所以来自索引html的链接将是安全的(不会被反应覆盖,而是通过base标记保留)。它也适用于css链接<link rel="stylesheet" href="styles.css">

答案 1 :(得分:1)

如果您看到https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base#Hint,则表示即使使用#target网址<base>也是预期的行为。

https://reacttraining.com/react-router/web/api/HashRouter上,它在basename:string部分中说明:格式正确的基本名称应该有一个前导斜杠,但没有尾部斜杠

所以也许你应该在HashRouter元素上定义一个不同的基本名称,或者从<base>中删除尾部斜杠

答案 2 :(得分:1)

您对HashRouter<base>标记的观察是正确的。我在此处提交了一个有关浏览器差异的问题:https://github.com/ReactTraining/history/issues/574以及相应的PR修复此处:https://github.com/ReactTraining/history/pull/577

与此同时,我不确定您需要的所有路由,但如果反应应用程序完全位于/some/static/page/下,您可以将其用于:

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

答案 3 :(得分:1)

我在HOC结束时只是应用this answer中描述的修复:

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>
...

答案 4 :(得分:1)

这是history包的问题。它甚至已经解决,请查看this pr

作为临时修复,我建议您在package.json

中指定此分支
"dependencies": {
  ...
  "history": "git://github.com/amuzalevskiy/history.git",
  ...
}

一旦修复将合并到原始分支 - 将其恢复为固定的主要npm模块

关于回购: 我刚刚对microbouji solution做了npm run build并提交了结果,因为如果不运行publish脚本就无法使用原始存储库