你好,
我们注意到一个问题,如果您登录到 Google 的搜索控制台,并以 Google 的身份提取,则该网站会呈现为一个空白页面。
我做了一些研究,其中一些使我想到了这篇文章:
https://medium.com/@gajus/react -application-seen-as-a-blank-page-via-fetch-as-google-afb11dff8562
很可能是搜索引擎和 fetch as google 本身的行为方式不同,否则我们现在已经失去了排名,不过我想解决这个问题,因为它会让任何网站所有者保持清醒。
我已经下载了 phantomjs,假设它与 Google 的 fetch 相同,并尝试呈现它会返回许多错误:
这个问题可能就像更改正在编译的目标浏览器一样容易解决,但是简单地安装 babel/polyfil 并导入是行不通的。
有人能在这里指出我正确的方向吗?
如果您的页面显示为空白,那么您已经破坏了 SSR 或补水方案。 Razzle 不会受到您所描述的开箱即用的影响,事实上,它用于以 SEO 为重点的网站,例如 BBC.com。 你能粘贴你的 server.js 文件吗? 您是否在服务器上获取数据?
嗨,贾里德,
感谢您的及时回复,是的,我们确实从服务器获取数据。
这是我的 server.js
import App from './App';
import React from 'react';
import Helmet from 'react-helmet';
import { StaticRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import express from 'express';
import compression from 'compression';
import { renderToString } from 'react-dom/server';
import configureStore from './store/configureStore';
import { remoteLoader } from './api/remoteLoader';
import serialize from 'serialize-javascript';
import { Capture } from 'react-loadable';
import { getBundles } from 'react-loadable/webpack';
import stats from '../build/react-loadable.json';
import { IS_PRODUCTION } from './components/shared/constants';
const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);
const server = express();
server.use(compression());
if (!IS_PRODUCTION) {
server.set('cache', false);
}
server
.disable('x-powered-by')
.use(express.static(process.env.RAZZLE_PUBLIC_DIR, { Expires: '30d' }))
.get('/*', (req, res) => {
//Ensures clients with old css paths are served the current file
if (req.path.indexOf('/static/css') > -1 && assets.client.css) {
const currentCssFile = `${process.env.RAZZLE_PUBLIC_DIR}${assets.client.css}`;
return res.sendFile(currentCssFile);
}
remoteLoader(apiResult => {
const responseCode = typeof apiResult.status === 'undefined' ? 404 : apiResult.status;
if (responseCode === 301) {
return res.redirect(responseCode, apiResult.headers.location);
}
// Compile an initial state
const initialState = {
remote: {
cms: {
result: apiResult ? apiResult.data : false,
loading: false
},
myDrewberry: {
searchResults: null,
loading: false,
failed: false
}
}
};
// Create a new Redux store instance
const store = configureStore(initialState);
const context = {};
const modules = [];
const markup = renderToString(
<Capture report={moduleName => modules.push(moduleName)}>
<StaticRouter context={context} location={req.url}>
<Provider store={store}>
<App />
</Provider>
</StaticRouter>
</Capture>
);
const helmet = Helmet.renderStatic();
if (context.url) {
res.redirect(context.url);
} else {
const bundles = getBundles(stats, modules);
const chunks = bundles.filter(bundle => bundle.file.endsWith('.js'));
res.status(responseCode).send(
`<!doctype html>
<html ${helmet.htmlAttributes.toString()}>
<head>
${assets.client.css ? `<link rel="stylesheet" href="${assets.client.css}">` : ''}
${helmet.title.toString()}
${helmet.meta.toString()}
${helmet.link.toString()}
<link rel="icon" type="image/png" href="/favicon16.png" sizes="16x16"/>
<link rel="icon" type="image/png" href="/favicon32.png" sizes="32x32"/>
<link rel="icon" type="image/png" href="/favicon96.png" sizes="96x96"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"/> <!--320-->
<meta http-equiv="expires" content="0">
</head>
<body class="drewberry-preload">
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-TKPXWB');</script>
<!-- End Google Tag Manager -->
<div id="root">${markup}</div>
<script>
window.__PRELOADED_STATE__ = ${serialize(initialState)}
</script>
${
IS_PRODUCTION
? `<script src="${assets.client.js}"></script>`
: `<script src="${assets.client.js}" crossorigin></script>`
}
${chunks
.map(chunk =>
IS_PRODUCTION
? `<script src="/${chunk.file}"></script>`
: `<script src="http://${process.env.HOST}:${parseInt(process.env.PORT, 10) + 1}/${
chunk.file
}"></script>`
)
.join('\n')}
<script>window.main();</script>
</body>
</html>`
);
}
}, req.path);
});
export default server;
我可以确认开箱即用的 Razzle 没有受到影响,只是加载了一个干净的安装并使用 phantomjs 没有出现任何警告,这意味着它可能是我们安装的某个库的东西。
你在上面看到了什么明显的东西吗?
此刻的感觉就像是在追赶大雁! :-)
所以我的建议(也许我们需要文档中的故障排除指南)是尽可能多地从server.js
、 client.js
、 App.js
和潜在的您的 webpack 覆盖(如果有的话)的各个方面,以使您的 Razzle 应用程序回到“hello world”之类的状态。 我还建议将 Razzle 的 HTML 模板复制回您的server.js
文件,以确保所有脚本都正确加载开箱即用。 运行后,打开 Chrome 开发工具并转到“性能”选项卡,选中“屏幕截图”复选框,然后按刷新图标。 请参阅下面的 GIF。
目标是确保时间轴区域中没有空白的白框。 在您获得没有闪光的正确渲染后,您应该添加越来越多的旧代码。 采取婴儿的步骤。 如果你不小心,这很容易搞砸。
至于你的具体情况,我会首先删除所有谷歌标签管理器的东西,你所有的 CSS,以及你正在做的任何代码拆分。 只需让它渲染并向后工作即可。 接下来,让 Redux 工作。 然后重新添加 react-helmet,然后,一旦您认为自己做对了,还可以通过进行 Lighthouse 测试(审核选项卡)来仔细检查。 将此状态提交到新分支,以便您将来可以返回到它。 接下来,重新添加您的分析脚本并再次检查渲染。 最后,尝试添加回代码拆分。
注意:如果你保留你的 css 并在开发模式下用 Razzle 做上面的分析,你可能会得到一个 FOUC,因为 Razzle 在开发过程中不会在服务器上处理
.css
样式(只是在客户端)。 因此,如果您正在使用.css
文件,要全面测试您是否正在使用样式水化进行适当的 SSR,您应该为生产构建 Razzle 并在本地运行生产服务器。 但是,如果您只是从服务器检查 HTML 是否存在,您可以在开发模式下运行它。
好的,感谢 Razzle 所做的出色工作,现在为此。 我会很好地审计它并报告回来。
当然,检查任何“空”(不是空白,这是一个错误)seo 页面的第一件事是:您的页面是否依赖于componentDidMount
来加载要显示的数据。 您可以使用curl
进行快速检查。 如果是,你需要像 After.js 这样的东西来预加载。
如果在 1000 毫秒标记附近有空白页,您能否建议如何使用“性能”选项卡进行调试? 我知道我的问题是图标字体库没有加载到服务器上,因此它在初始加载时显示这些空白的空方块......
啊,我所有的自定义字体直到很久以后才被加载。 我正在通过 CSS 的 @font-face {} 加载我的字体。
@font-face {
font-family: 'SFProText';
font-display: auto;
src: url('fonts/SFPro/SF-Pro-Display-Regular.otf') format('truetype');
font-weight: normal;
font-style: normal;
}
@jaredpalmer我也有同样的问题。 我的客户端页面中未加载字体和资产。
我没有在我的应用程序设置中添加任何 webpack.config 文件,这是我从你的 repo 克隆的。 我刚刚从你的 repo 中获取了副本并安装了 npm modules ,然后我通过运行 npm start 命令开始使用该应用程序。
我错过了什么配置或者必须使用我的新 webpack 文件来解决这个问题?
请帮我解决这个问题。
我的 App.css 文件看起来像这样..
@字体脸{
font-family: 'Roboto-medium';
src: url("./static/fonts/Roboto-Medium-webfont.eot");
src: url("./static/fonts/Roboto-Medium-webfont.eot?#iefix") 格式("embedded-opentype"), url("./static/fonts/Roboto-Medium-webfont.woff")格式("woff"), url("./static/fonts/Roboto-Medium-webfont.ttf") 格式("truetype"); }
@字体脸{
font-family: 'Roboto-regular';
src: url("./static/fonts/roboto-regular-webfont.eot");
src: url("./static/fonts/roboto-regular-webfont.eot?#iefix") 格式("embedded-opentype"), url("./static/fonts/roboto-regular-webfont.woff")格式("woff"), url("./static/fonts/roboto-regular-webfont.ttf") 格式("truetype"); }
谢谢 inadvane :)
最有用的评论
所以我的建议(也许我们需要文档中的故障排除指南)是尽可能多地从
server.js
、client.js
、App.js
和潜在的您的 webpack 覆盖(如果有的话)的各个方面,以使您的 Razzle 应用程序回到“hello world”之类的状态。 我还建议将 Razzle 的 HTML 模板复制回您的server.js
文件,以确保所有脚本都正确加载开箱即用。 运行后,打开 Chrome 开发工具并转到“性能”选项卡,选中“屏幕截图”复选框,然后按刷新图标。 请参阅下面的 GIF。目标是确保时间轴区域中没有空白的白框。 在您获得没有闪光的正确渲染后,您应该添加越来越多的旧代码。 采取婴儿的步骤。 如果你不小心,这很容易搞砸。
至于你的具体情况,我会首先删除所有谷歌标签管理器的东西,你所有的 CSS,以及你正在做的任何代码拆分。 只需让它渲染并向后工作即可。 接下来,让 Redux 工作。 然后重新添加 react-helmet,然后,一旦您认为自己做对了,还可以通过进行 Lighthouse 测试(审核选项卡)来仔细检查。 将此状态提交到新分支,以便您将来可以返回到它。 接下来,重新添加您的分析脚本并再次检查渲染。 最后,尝试添加回代码拆分。