本文介绍了DOMException:无法在“History"上执行“replaceState":带有 URL 的历史状态对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
在 Google 上打开缓存版本的网页时,React 应用程序出现以下错误.
DOMException:无法在历史"上执行replaceState":历史无法在 URL 中创建带有 URL 'https://projecturl' 的状态对象具有来源https://webcache.googleusercontent.com"和 URL 的文档'https://webcache.googleusercontent.com/search?q=cache:X4dz2ukZZAYJ:https://projecturl/+&cd=1&hl=en&ct=clnk&gl=in'
在我们的应用中,我们使用 react-router-dom 并实现了服务器端渲染.在谷歌搜索中打开带有缓存选项的页面时,它首先加载页面几秒钟,然后在控制台中显示带有上述错误的空白页面.
在寻找解决方案时,我发现了
在 bing 和 yahoo 搜索引擎上,404 页面都出现在缓存版本中.
更新 4:
这是路由文件的样子:
从'react'导入React;从'react-router-dom'导入{开关,路由};从'react-loadable'导入可加载;进口 {OTP新,登录新脸书,入职包装,找不到网页,注册专长,注册详情,喂养,来自'./懒惰';const RootComponent = 可加载({加载器:() =>import(/* webpackChunkName: "rootcomp" */'../components/RootComponent'),加载:() =>空值,模块:['../components/RootComponent'],webpack: () =>[require.resolveWeak('../components/RootComponent')],});const 注册路由 = [{路径:'/登录/otp',组件:OTPNew,},{路径:'/登录',组件:LoginNewFb,},{路径:'/注册/详细信息',组件:注册详细信息,},{路径:'/注册',组件:SignUpSpecialty,},];const 路由 = () =>{返回 (<开关>{signupRoutes.map(sRoute => (<路线键={sRoute.path}路径={sRoute.path}渲染={routeProps =>(<入职包装器><sRoute.component {...routeProps}/></OnboardingWrapper>)}/>))}<路由路径=/feed";组件={Feed}/><路由路径="/";组件={RootComponent}/><路由路径=*"组件={PageNotFound}/></开关>);};导出默认路由;
RootComponent.js
import React from 'react';从'react-router-dom'导入{开关,路由};从'react-redux'导入{useSelector};从 './MainComponent' 导入 MainComponent;从 '../routes/lazy' 导入 { DiscussionComponent, HomePage, PageNotFound };从 '../actions/VerifyMobileAction' 导入 { useIsMobile };从 '../routes/rootRoutes' 导入 rootRoutes;从 '../routes/quizRoutes' 导入 quizRoutes;import { parseQueryParameter, getAPIHost } from '../helpers/helperFunctions';函数 cacheQueryParser(查询,projectCanonicalAddr){返回查询.split(projectCanonicalAddr).流行音乐().split('+')[0];}函数 getPageOrNotFound(location) {const queryObject = parseQueryParameter(location.search);const projectCanonicalAddr = getAPIHost();如果 (location.pathname === '/search' &&queryObject && 中的q"queryObject.q.indexOf('cache') === 0 &&queryObject.q.indexOf(projectCanonicalAddr) >-1){const replacer = cacheQueryParser(queryObject.q, projectCanonicalAddr);返回 {组件渲染:空,替代品,};}返回 {组件渲染:PageNotFound,替换器:空,};}const RootComponent = () =>{const { OtpVerified } = useSelector(store => store.authenticationReducer);const isMobileViewport = useIsMobile();函数 logicForHomeRoute() {如果(OtpVerified){返回 {组件:讨论组件,};}返回 {renderHeaderDesktop:假,renderHeaderMobile: 假,渲染页脚:假,renderSideProfile: 假,组件:主页,};}const typeOfAppClassName = `${是移动视口?移动":桌面"}-viewport-app`;返回 (<div className={typeOfAppClassName}><开关><路线精确的路径=/"渲染={() =><MainComponent {...logicForHomeRoute()}/>}/>{[...quizRoutes, ...rootRoutes].map(sRoute => (<路线键={sRoute.path}路径={sRoute.path}渲染={道具=>{const { location, history } = props;if (sRoute.path === '/:alternate_username') {if (location.pathname.startsWith('/dr') === false) {const { 替换器,ComponentRender } = getPageOrNotFound(地点);如果(组件渲染){返回 <ComponentRender/>;}历史.replace(替换者);返回空;}}return <MainComponent {...props} {...sRoute}/>;}}/>))}</开关>
);};导出默认的 RootComponent;
更新 5
控制台显示另一个错误:
获取脚本时收到错误的 HTTP 响应代码 (404).
解决方案
既然你在空白页面"之前看到的是你的正常页面,我的猜测是首先你看到的是网站的 SSR 版本,然后在由于 CORS 限制,加载脚本后您会看到空白页面".
作为一种解决方案,您可以只为您的域加载脚本,而为 google/bing/yahoo 保留 SSR 版本,而没有机会加载这些脚本并破坏网站.
答案基于 react-loadable-ssr-addon的示例,这里的想法是检查 window.location.origin
与您的域的来源.否则,只需完全跳过这些文件.
原始示例:
res.send(`<!doctype html><html lang="en"><头>...</头>${styles.map(style => {return `<link href="/dist/${style.file}";rel=样式表"/>`;}).join('\n')}<身体><div id="app">${html}</div>${scripts.map(script => {返回`<script src="/dist/${script.file}></script>`}).join('\n')}</html>');
这是我的动态加载示例:
res.send(`<!doctype html><html lang="en"><头>...</头>${styles.map(style => {return `<link href="/dist/${style.file}";rel=样式表"/>`;}).join('\n')}<身体><div id="app">${html}</div><脚本>函数加载脚本(网址){var s = document.createElement(脚本");s.src = url;document.body.appendChild(s);}如果 (window.location.origin === "https://example.com";||window.location.origin === "http://localhost";//用于开发目的){${scripts.map(script => {return `loadScript("/dist/${script.file}");`}).join('\n')}}</html>');
当然,你的应用程序中有你自己的代码,这就是为什么我不能给你一个完整的解决方案.您应该更改代码以遵循此想法.
至于其他错误.部分原因是,它们也基于 CORS 限制,其中之一来自 https://connect.facebook.net/en_US/fbevents.js
.与您的 Service Worker 文件相关的 404 错误,由 google 的内部webcache"引起.算法,我不确定您是否可以对此做点什么.
无论如何,这些错误不应干扰缓存站点的正确显示,因为它们不是您看到空白页面的原因.在我看来,当然,这是基于屏幕截图.
In react app getting below error when opening cached version of webpage on google.
In our app, we are using react-router-dom and implemented server side rendering. When opening a page with Cached option in google search it first loads the page for fraction of seconds then shows blank page with the above error in console.
While searching for a solution I found https://github.com/ReactTraining/react-router/issues/5801related to my problem, but no solution.
UPDATE 1:
The same question is asked here, but for Angular.Though I could not understand what is explained in the answer and how it could be related to my problem.
We are using React Loadable SSR Add-on for Server-side rendering of our react app.
UPDATE 2:
Opened the same issue on Git repo of npm package used for Server side rendering.Issue opened
UPDATE 3:
The page works fine when I open it in google chrome by disabling security. Therefore it should not be something related to my code.
Also, it gives different error when opened in bing search engine cached version:
On both bing and yahoo search engine, 404 page appears in cached version.
UPDATE 4:
This is how routing file looks:
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;
RootComponent.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;
UPDATE 5
There is another error displayed in console:
解决方案
Since you see your normal page before "blank page", my guess is that at first, you see the SSR version of the site, and then after the scripts were loaded you see a "blank page", because of CORS restrictions.
As a solution, you could load scripts only for your domain and leave the SSR version for google/bing/yahoo without a chance to load those scripts and to break a site.
The answer is based on the react-loadable-ssr-addon's examples, the idea here is to check window.location.origin
with your domain's origin. Otherwise, just skip those files altogether.
The original example:
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>
`);
And this is my example with dynamic loading:
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>
`);
Of course, you have your own code in your application, that's why I can't give you a complete solution. You should change your code to follow this idea.
As for other errors. Partly, they also based on CORS restrictions, one of them comes from https://connect.facebook.net/en_US/fbevents.js
. The 404 error, related to your service worker file, caused by google's internal "webcache" algorithm, I'm not sure if you could do something about this.
Anyway, those errors shouldn't interfere with the correct display of the cached site, because they are not the reason why you see a blank page. In my opinion, of course, which is based on screenshots.
这篇关于DOMException:无法在“History"上执行“replaceState":带有 URL 的历史状态对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!