问题
In react app getting below error when opening cached version of webpage on google.
DOMException: Failed to execute 'replaceState' on 'History': A history state object with URL 'https://projecturl' cannot be created in a document with origin 'https://webcache.googleusercontent.com' and URL 'https://webcache.googleusercontent.com/search?q=cache:X4dz2ukZZAYJ:https://projecturl/+&cd=1&hl=en&ct=clnk&gl=in'
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/5801 related 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:
The script resource is behind a redirect, which is disallowed.
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:
A bad HTTP response code (404) was received when fetching the script.
回答1:
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.
来源:https://stackoverflow.com/questions/63115145/domexception-failed-to-execute-replacestate-on-history-a-history-state-obj