代码之家  ›  专栏  ›  技术社区  ›  Estus Flask

React路由器的HashRouter重定向到标签url

  •  13
  • Estus Flask  · 技术社区  · 7 年前

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

    下面是React 16、React路由器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/#/ (仅在IE 11中按预期工作)。

    有一个快速飞溅的 Root 组件重定向到之前 /#/

    它重定向到 /foo/#/ 如果 <base href="/foo"> ,并重定向到 /部分/静态/页面/#/ 什么时候 <基本(>); 标签已删除。

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

    为什么是 <HashRouter> 受影响者 <基本(>); ?这里使用它正是因为它不应该影响位置路径,只影响哈希。

    如何修复此问题?

    5 回复  |  直到 7 年前
        1
  •  11
  •   Community CDub    5 年前

    实际上这是 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>
          );
        }
    }
    

    它将显示与您相同的问题。如果你改变了 历史 代码如下:

    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 但是来自 历史

    因为这个来自 历史 ,让我们看看这个 thread 。读了这篇文章后,我们可以得出结论 特色 从…起 历史

    所以,如果你 <base href="/"> ,因为您正在使用 搞砸 (#),加载浏览器时(实际上在 componentDidMount )它将附加 搞砸 (#)在你的情况下 some/static/page => 部分/静态/页面 + / => / + #/ => /#/ 。您可以办理登机手续 组件安装 设置 debugger 在附加路由之前捕获。


    解决方案

    简单地说,只需删除元素 <base href> 或不使用 哈希路由器

    如果仍然需要但想要避免特定 component ,把这个放在前面 class :

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

    更新

    既然你想 base 标记以保持持久链接并使用 搞砸 路由器,这是我认为最接近的解决方案。

    1、设置标签 基础 以清空。

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

    把代码放进去 App 组件(根包装组件)调用一次。

    2、何时 组件安装 把它放回去

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

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

    我想这很接近(让我们测试一下)。因为您正在使用 搞砸 路由器,来自索引html的链接将是安全的(不是通过react覆盖,而是通过keep 基础 标签)。它还可以与css链接一起使用 <link rel="stylesheet" href="styles.css">

        2
  •  2
  •   Estus Flask    7 年前

    我以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
  •  2
  •   Elian Ibaj    7 年前

    您对 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">

        4
  •  1
  •   Dhruv Murarka    7 年前

    如果你看到 https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base#Hint ,它说使用 <base> 即使使用#目标URL也是预期行为。

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

    因此,也许您应该在 HashRouter 元素或从中删除尾部斜杠 <基本(>);

        5
  •  1
  •   Andrii Muzalevskyi    7 年前

    这是一个 history 包裹甚至已经解决了,请看 this pr

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

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

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


    关于回购: 我刚刚做到了 npm run build microbouji solution 和提交的结果,因为不运行 publish 剧本

    推荐文章