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
。 应用程序在所有页面上都有<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 显示问题。复制它的步骤是:
npm i
npm start
导航到http://localhost:3000/some/static/page
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>
的影响?在这里使用它正是因为它不应该影响位置路径,只会影响哈希。
如何解决这个问题?
【问题讨论】:
仅在 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
的功能。
所以,如果您设置<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
标记以保持持久链接并使用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 链接<link rel="stylesheet" href="styles.css">
。
【讨论】:
感谢历史问题的链接。我不确定当前问题的 this means 是什么。是否应该将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
和<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">
.
【讨论】:
感谢您为解决问题所做的努力。考虑在您的history
PR 的答案中提供应该解决此问题的链接。当 PR 被合并时,我可能会接受它的答案。
谢谢!当前接受的答案是您问题的即时解决方案,因此无需担心更改它。【参考方案4】:
如果您看到https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base#Hint,它表示即使使用#target URL 也使用<base>
是预期行为。
在https://reacttraining.com/react-router/web/api/HashRouter 上,它在basename 中说:字符串部分:格式正确的basename 应该有一个前导斜杠,但没有尾随斜杠。
所以也许您应该在 HashRouter
元素上定义一个不同的基本名称,或者从 <base>
中删除尾部斜杠
【讨论】:
您是否建议将<base href="/">
替换为<base href="">
?它会违背目的,不是吗? HashRouter 文档还指出 basename
是哈希的基本 url,而不是整个位置路径,请参阅带有 <HashRouter basename="/calendar"/>
的示例。如果您有不同的信息,请提供。
是的<base href="">
违背了目的,但就目前的行为而言,它似乎是一个解决方案。但是,<base>
和 basename
是不同的,因此更好的解决方案可能是同时保留 basename = ""
和 <base href = "/">
。
@estus。是的,basename
是哈希的基本 url,而不是整个位置路径,但问题不在于使用它。它只使用<base>
这是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 的 HashRouter 重定向到 <base> 标签 url
React 项目使用 React-router-dom 4.0 以上版本时使用 HashRouter 怎么控制 history