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 说明了问题。复制它的步骤是:
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>
的影响?它在这里使用正是因为它不应该影响位置路径,只会影响哈希。
如何解决这个问题?
如果您看到 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
与此同时,我不确定您需要的所有路由,但如果 React 应用程序完全位于 /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
脚本
就不可能使用原始存储库
我有非 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 说明了问题。复制它的步骤是:
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>
的影响?它在这里使用正是因为它不应该影响位置路径,只会影响哈希。
如何解决这个问题?
如果您看到 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
.
所以,如果你设置 <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
与此同时,我不确定您需要的所有路由,但如果 React 应用程序完全位于 /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
脚本