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

Posted

技术标签:

【中文标题】React Router 的 HashRouter 重定向到 <base> 标签 url【英文标题】:React Router's HashRouter redirects to <base> tag url 【发布时间】:2018-09-03 04:07:43 【问题描述】:

我有一个带有 React 应用程序的非 SPA 服务器端应用程序,该应用程序仅限于当前页面 /some/static/page应用程序在所有页面上都有&lt;head&gt; 中的&lt;base href="/"&gt; 并依赖它,这是无法更改的。

这是 React 16、React Router 4 和 &lt;HashRouter&gt; 的基本示例:

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 明显受到&lt;base&gt; 的影响。它在初始化时从 /some/static/page 重定向到 /#/,而我希望它是 /some/static/page#//some/static/page/#/(仅在 IE 11 中按预期工作)。

Root 组件在重定向到 /#/ 之前快速飞溅。

如果是&lt;base href="/foo"&gt;,它会重定向到/foo/#/,当&lt;base&gt; 标签被移除时,它会重定向到/some/static/page/#/

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

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

如何解决这个问题?

【问题讨论】:

仅在 IE11 上按预期工作......好吧,这是第一个...... 请说明您想要的预期行为是什么。 它在初始化时从 /some/static/page 重定向到 /#/,而我希望它是 /some/static/page#/ 或 /some/static/page/# / 。 IE。它不应该用基本网址替换location.pathname 【参考方案1】:

其实这个来自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功能

所以,如果您设置&lt;base href="/"&gt;,因为您使用的是hash (#),当浏览器加载时(实际上是在componentDidMount 之后),它将在您的情况下附加hash (#) some/static/page => some/static/page + / => / + #/ => /#/。您可以检查componentDidMount 设置debugger 在追加路线之前捕获。


解决方案

简单地说,只需删除元素 &lt;base href&gt; 或不要使用 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 的链接将是安全的(不会被react 覆盖,而是由base 标签保留)。它也适用于 css 链接&lt;link rel="stylesheet" href="styles.css"&gt;

【讨论】:

感谢历史问题的链接。我不确定当前问题的 this means 是什么。是否应该将 解析添加到 react-router-dom,因为历史不支持它? 简单地说,只需删除元素 或不使用 HashRouter - 这是不可能的。这是非 SPA 应用程序,其中某些部分依赖于当前的 。我不得不暂时切换到另一个没有这个问题的路由器。 如果只是避免做出反应,我认为最后的解决方案是合适的。 我记住了,谢谢。但我想这会破坏可以在 React 应用程序初始化后加载的资产。没有太多,但他们可以在那里。由于 的工作原理,修改它对于依赖它的网站来说将是非常具有破坏性的举动。 @estus,我现在明白了,查看我的更新了解更多信息 作为我的更新,timeout 将为您提供帮助。此外,我认为 s-s-r 将有助于解决问题,因为来自 NextJS 等服务器的 s-s-r(我的观点)。不确定【参考方案2】:

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

【讨论】:

【参考方案3】:

您对HashRouter&lt;base&gt; 标签的观察是正确的。我在此处提交了有关浏览器差异的问题:https://github.com/ReactTraining/history/issues/574 和相应的 PR 并在此处进行了修复:https://github.com/ReactTraining/history/pull/577

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

&lt;BrowserRouter basename="/some/static/page"&gt;.

【讨论】:

感谢您为解决问题所做的努力。考虑在您的history PR 的答案中提供应该解决此问题的链接。当 PR 被合并时,我可能会接受它的答案。 谢谢!当前接受的答案是您问题的即时解决方案,因此无需担心更改它。【参考方案4】:

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

在https://reacttraining.com/react-router/web/api/HashRouter 上,它在basename 中说:字符串部分:格式正确的basename 应该有一个前导斜杠,但没有尾随斜杠

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

【讨论】:

您是否建议将&lt;base href="/"&gt; 替换为&lt;base href=""&gt;?它会违背目的,不是吗? HashRouter 文档还指出 basename 是哈希的基本 url,而不是整个位置路径,请参阅带有 &lt;HashRouter basename="/calendar"/&gt; 的示例。如果您有不同的信息,请提供。 是的&lt;base href=""&gt; 违背了目的,但就目前的行为而言,它似乎是一个解决方案。但是,&lt;base&gt;basename 是不同的,因此更好的解决方案可能是同时保留 basename = ""&lt;base href = "/"&gt; @estus。是的,basename 是哈希的基本 url,而不是整个位置路径,但问题不在于使用它。它只使用&lt;base&gt; 不能成为解决方案,因为这会破坏依赖它的网站资产(我更新了问题以明确说明这一点)。 basename 对我不起作用,它只影响哈希部分,正如文档所暗示的那样。你试过了吗?【参考方案5】:

这是history 包的问题。偶已经解决了,请看this pr

作为临时修复,我建议您在 package.json 中指定此分支

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

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


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

【讨论】:

谢谢。这很有效,尽管我更喜欢避免使用来自未合并 PR 的修补程序,因为如果它们被丢弃或放弃,我最终会得到无人维护的分叉。 @estus 好的...个人意见 - 保持主代码干净并打开相应的 PR,对社区有用。而且通常“修复主要代码以使用有缺陷的库”就像一个雪球,日复一日地增长,在某些时候,保持旧版本比更新库更容易,因为没有人知道修复在哪里:-)

以上是关于React Router 的 HashRouter 重定向到 <base> 标签 url的主要内容,如果未能解决你的问题,请参考以下文章

react-router-dom中的BrowserRouter和HashRouter

react-router-dom中的BrowserRouter和HashRouter

react-router 4 的使用

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

React 项目使用 React-router-dom 4.0 以上版本时使用 HashRouter 怎么控制 history

react-router