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

新手上路,请多包涵

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

这是 React 16、React Router 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> 影响。 It redirects from /some/static/page to /#/ on initialization, while I expect it to be /some/static/page#/ or /some/static/page/#/ (works as intended only in IE 11 ).

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

It redirects to /foo/#/ in case of <base href="/foo"> , and it redirects to /some/static/page/#/ when <base> tag is removed.

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

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

这怎么能解决?

原文由 Estus Flask 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.3k
2 个回答

实际上这来自 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 ,让我们看看这个 线程。阅读该线程后,我们可以得出结论,这是来自 history特征

so, if you set <base href="/"> , because you are using hash (#), when browser loaded ( actually after componentDidMount ) it will append hash (#) in your case some/static/page => some/static/page + / => / + #/ = > /#/ 。您可以签入 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 组件(根包装组件)中调用一次。

2.当 componentDidMount 设置回来

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

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

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

原文由 hendrathings 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

原文由 Estus Flask 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题