reactjs - DOMException:无法在“历史”上执行“replaceState”:具有 URL 的历史状态对象
问题描述
在谷歌上打开网页的缓存版本时,反应应用程序出现以下错误。
DOMException:无法在“历史”上执行“replaceState”:无法在源为“https://webcache.googleusercontent.com”和 URL 为“https”的文档中创建 URL 为“https://projecturl”的历史状态对象: //webcache.googleusercontent.com/search?q=cache:X4dz2ukZZAYJ:https://projecturl/+&cd=1&hl=en&ct=clnk&gl=in'
在我们的应用程序中,我们使用 react-router-dom 并实现了服务器端渲染。在谷歌搜索中打开带有缓存选项的页面时,它首先加载页面几秒钟,然后在控制台中显示带有上述错误的空白页面。
在寻找解决方案时,我发现https://github.com/ReactTraining/react-router/issues/5801 与我的问题相关,但没有解决方案。
更新 1:
这里问了同样的问题,但对于 Angular。虽然我无法理解答案中解释的内容以及它与我的问题的关系。
我们正在使用React Loadable SSR Add-on来对我们的 react 应用程序进行服务器端渲染。
更新 2:
在用于服务器端渲染的 npm 包的 Git repo 上打开了相同的问题。 问题已打开
更新 3:
当我通过禁用安全性在谷歌浏览器中打开它时,该页面工作正常。因此,它不应该与我的代码有关。
此外,在 bing 搜索引擎缓存版本中打开时会出现不同的错误:
脚本资源位于重定向后面,这是不允许的。
在 bing 和 yahoo 搜索引擎上,404 页面出现在缓存版本中。
更新 4:
这是路由文件的外观:
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import Loadable from 'react-loadable';
import {
OTPNew,
LoginNewFb,
OnboardingWrapper,
PageNotFound,
SignUpSpecialty,
SignUpDetails,
Feed,
} from './lazy';
const RootComponent = Loadable({
loader: () =>
import(/* webpackChunkName: "rootcomp" */ '../components/RootComponent'),
loading: () => null,
modules: ['../components/RootComponent'],
webpack: () => [require.resolveWeak('../components/RootComponent')],
});
const signupRoutes = [
{
path: '/login/otp',
component: OTPNew,
},
{
path: '/login',
component: LoginNewFb,
},
{
path: '/signup/details',
component: SignUpDetails,
},
{
path: '/signup',
component: SignUpSpecialty,
},
];
const Routes = () => {
return (
<Switch>
{signupRoutes.map(sRoute => (
<Route
key={sRoute.path}
path={sRoute.path}
render={routeProps => (
<OnboardingWrapper>
<sRoute.component {...routeProps} />
</OnboardingWrapper>
)}
/>
))}
<Route path="/feed" component={Feed} />
<Route path="/" component={RootComponent} />
<Route path="*" component={PageNotFound} />
</Switch>
);
};
export default Routes;
根组件.js
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import { useSelector } from 'react-redux';
import MainComponent from './MainComponent';
import { DiscussionComponent, HomePage, PageNotFound } from '../routes/lazy';
import { useIsMobile } from '../actions/VerifyMobileAction';
import rootRoutes from '../routes/rootRoutes';
import quizRoutes from '../routes/quizRoutes';
import { parseQueryParameter, getAPIHost } from '../helpers/helperFunctions';
function cacheQueryParser(query, projectCanonnicalAddr) {
return query
.split(projectCanonnicalAddr)
.pop()
.split('+')[0];
}
function getPageOrNotFound(location) {
const queryObject = parseQueryParameter(location.search);
const projectCanonnicalAddr = getAPIHost();
if (
location.pathname === '/search' &&
'q' in queryObject &&
queryObject.q.indexOf('cache') === 0 &&
queryObject.q.indexOf(projectCanonnicalAddr) > -1
) {
const replacer = cacheQueryParser(queryObject.q, projectCanonnicalAddr);
return {
ComponentRender: null,
replacer,
};
}
return {
ComponentRender: PageNotFound,
replacer: null,
};
}
const RootComponent = () => {
const { OtpVerified } = useSelector(store => store.authenticationReducer);
const isMobileViewport = useIsMobile();
function logicForHomeRoute() {
if (OtpVerified) {
return {
component: DiscussionComponent,
};
}
return {
renderHeaderDesktop: false,
renderHeaderMobile: false,
renderFooter: false,
renderSideProfile: false,
component: HomePage,
};
}
const typeOfAppClassName = `${
isMobileViewport ? 'mobile' : 'desktop'
}-viewport-app`;
return (
<div className={typeOfAppClassName}>
<Switch>
<Route
exact
path="/"
render={() => <MainComponent {...logicForHomeRoute()} />}
/>
{[...quizRoutes, ...rootRoutes].map(sRoute => (
<Route
key={sRoute.path}
path={sRoute.path}
render={props => {
const { location, history } = props;
if (sRoute.path === '/:alternate_username') {
if (location.pathname.startsWith('/dr') === false) {
const { replacer, ComponentRender } = getPageOrNotFound(
location
);
if (ComponentRender) {
return <ComponentRender />;
}
history.replace(replacer);
return null;
}
}
return <MainComponent {...props} {...sRoute} />;
}}
/>
))}
</Switch>
</div>
);
};
export default RootComponent;
更新 5
控制台中显示另一个错误:
获取脚本时收到错误的 HTTP 响应代码 (404)。
解决方案
由于您在“空白页面”之前看到您的正常页面,我的猜测是,首先,您会看到网站的 SSR 版本,然后在加载脚本后,您会看到“空白页面”,因为 CORS 限制。
作为一种解决方案,您可以仅为您的域加载脚本,并将 SSR 版本保留给google/bing/yahoo,而没有机会加载这些脚本并破坏站点。
答案基于react-loadable-ssr-addon的示例,这里的想法是检查window.location.origin
您的域的来源。否则,只需完全跳过这些文件。
原始示例:
res.send(`
<!doctype html>
<html lang="en">
<head>...</head>
${styles.map(style => {
return `<link href="/dist/${style.file}" rel="stylesheet" />`;
}).join('\n')}
<body>
<div id="app">${html}</div>
${scripts.map(script => {
return `<script src="/dist/${script.file}"></script>`
}).join('\n')}
</html>
`);
这是我的动态加载示例:
res.send(`
<!doctype html>
<html lang="en">
<head>...</head>
${styles.map(style => {
return `<link href="/dist/${style.file}" rel="stylesheet" />`;
}).join('\n')}
<body>
<div id="app">${html}</div>
<script>
function loadScript(url) {
var s = document.createElement("script");
s.src = url; document.body.appendChild(s);
}
if (
window.location.origin === "https://example.com" ||
window.location.origin === "http://localhost" // for development purpose
) {
${scripts.map(script => {
return `loadScript("/dist/${script.file}");`
}).join('\n')}
}
</script>
</html>
`);
当然,你的应用程序中有你自己的代码,这就是为什么我不能给你一个完整的解决方案。您应该更改代码以遵循此想法。
至于其他错误。部分地,它们也基于 CORS 限制,其中之一来自https://connect.facebook.net/en_US/fbevents.js
. 与您的服务工作人员文件相关的 404 错误是由 google 的内部“webcache”算法引起的,我不确定您是否可以对此做些什么。
无论如何,这些错误不应干扰缓存站点的正确显示,因为它们不是您看到空白页面的原因。在我看来,当然,这是基于截图。
推荐阅读
- pandas - 时间序列在切片和相乘时返回 NaN
- php - 如何获取和设置高度和宽度phpspreadsheet
- html - 来自 HTML OnClick 的 gtag 调用未到达 GA
- php - TYPO3 内容元素:PHP 警告:strlen() 期望参数 1 为字符串,给定数组
- python - 在 Django 应用程序中一直在后台运行的函数(以及启动本身)
- matlab - 如何从时频分析中找到谱熵?
- sql-server - 如果启用了端口 1434 UDP,这是否意味着什么?
- apache-spark - Spark:平衡的任务但不平衡的执行者
- vim - 如何在vim中找到函数的名称?
- javascript - 尝试与正则表达式匹配时有一个 in if 语句