React Router 的 HashRouter 重定向到 <base> 标签 url

React Router's HashRouter redirects to <base> tag url

我有非 SPA 服务器端应用程序和仅限于当前页面的 React 应用程序,/some/static/page应用在所有页面<head>中有<base href="/">并依赖它,这个不能改。

这是 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 说明了问题。复制它的步骤是:

HashRouter 明显受到 <base> 的影响。它在初始化时从 /some/static/page 重定向到 /#/,而我希望它是 /some/static/page#//some/static/page/#/(仅在 IE 11 中按预期工作)。

Root 组件在重定向到 /#/ 之前快速启动。

<base href="/foo"> 的情况下重定向到 /foo/#/,在删除 <base> 标签时重定向到 /some/static/page/#/

该问题影响 Chrome 和 Firefox(最新版本),但不影响 Internet Explorer (IE 11)。

为什么 <HashRouter> 会受到 <base> 的影响?它在这里使用正是因为它不应该影响位置路径,只会影响哈希。

如何解决这个问题?

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

并且在 https://reacttraining.com/react-router/web/api/HashRouter 上它在 basename: string section 中说:格式正确的 basename 应该有一个前导斜杠,但是 没有尾随斜杠

所以也许您应该在 HashRouter 元素上定义一个不同的基本名称,或者从 <base>

中删除结尾的斜线

实际上这个来自 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 => some/static/page + / => / + #/ => /#/。您可以签入 componentDidMount 设置 debugger 以在追加路由之前捕获。


解决方案

简单地说,删除元素 <base href> 或不使用 HashRouter

如果仍然需要但想避免特定的 component,只需将其放在 class:

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

更新

因为你想保留 base 标签以保持 link 并使用 hash 路由器,这里是我认为的关闭解决方案。

1.将标记 base 设置为空。

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

将该代码放在App组件(root wrap组件)中调用一次。

2。当componentDidMount设置回来

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

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

我认为这很接近(已测试)。因为您使用的是 hash 路由器,索引 html 中的 link 将是安全的(不会被 React 覆盖,但会保留 base 标签)。它也适用于 css link <link rel="stylesheet" href="styles.css">

您对 HashRouter<base> 标签的观察是正确的。我在这里提出了一个关于浏览器差异的问题:https://github.com/ReactTraining/history/issues/574 and corresponding PR with fix here: https://github.com/ReactTraining/history/pull/577

与此同时,我不确定您需要的所有路由,但如果 R​​eact 应用程序完全位于 /some/static/page/ 之下,您可能可以使其与以下产品一起使用:

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

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

这是 history package. It is even resolved, please look at this pr

的问题

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

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

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


关于回购: 我只是在 microbouji solution 上做了 npm run build 并提交了结果,因为如果没有 运行 publish 脚本

就不可能使用原始存储库