我有反应服务器端渲染的FOUC问题。我使用了React + ReactRouter + Redux 以下是我认为有必要列出的主要技术堆栈:
CSS Module
处理组件样式extract-text-webpack-plugin
用于生成单独的css文件css-modules-require-hook
& asset-require-hook
用于处理服务器端的资产和我的webpack配置:
module.exports = {
entry: [
APP_PATH,
],
output: {
path: BUILD_PATH,
filename: 'client.bundle.js',
publicPath: '/JdBus/',
},
resolve: {
extensions: ['.js', '.jsx'],
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
'babel-loader',
],
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
modules: true, // 开启CSS Module
localIdentName: '[name]__[local]-[hash:base64:5]',
},
},
{
loader: 'postcss-loader',
options: {
plugins() { // 这里配置postcss的插件
return [autoprefixer];
},
},
},
],
}),
},
{ // 处理图片
test: /\.(png|jpg|gif|webp')$/,
// include:path.resolve(__dirname,'/client/assets'),
use: [{
loader: 'url-loader',
options: {
limit: 10000,
// 这个是输出的图片文件,跟output一致,生成的是bundleImg目录下的图片文件
name: 'bundleImg/[hash:8].[name].[ext]',
},
}],
},
{ // 处理文字
test: /\.(woff|ttf|svg|eot|woff2)$/,
// include:path.resolve(__dirname,'/client/assets'),
use: [{
loader: 'url-loader',
options: {
limit: 10000,
name: 'bundleFonts/[hash:8]-[name].[ext]',
},
}],
},
],
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production'),
WEBPACK: true,
},
}),
new webpack.optimize.UglifyJsPlugin({
compressor: {
warnings: false,
},
}),
new ExtractTextPlugin('bundle.css'),
new webpack.optimize.CommonsChunkPlugin({
name: 'commons',
filename: 'commons.js',
minChunks: 2,
}),
};
ExtractTextPlugin
将在bundle.css中生成所有CSS样式。
我的服务器端处理快递:
match({ routes: AppRoutes, location: `/jdbus${req.url}` }, (err, redirectLocation, renderProps) => {
if (err) {
res.status(500).send(err.message);
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else if (renderProps) {
const store = configureStore({ user: {
name: 'ddd',
avatar: 'ddd',
} });
// console.log(renderProps);
const marked = renderToStaticMarkup(
<Provider store={store}>
<RouterContext {...renderProps} />
</Provider>
);
const initHtml = renderFullPage(marked, store.getState(), process.env.NODE_ENV);
res.status(200).end(initHtml);
} else {
res.status(404).end('404 not');
}
});
renderFullPage()
是一个从 veiw.js 生成html的函数:
function renderFullPage (html, initiaState, env) {
// 根据生产和开发环境配置不同的页面
if (env === 'development') {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<title>开发测试页面</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-
scale=1.0, user-scalable=no"/>
<style>
body{
margin:0px;
}
</style>
</head>
<body>
<div id="root"></div>
<script>
window.__INITIAL_STATE__ = ${JSON.stringify(initiaState)};
</script>
<script src='/devClient.bundle.js'></script>
</body>
</html>
`;
} else if (env === 'production') {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>十二棵橡树</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0,
user-scalable=no"/>
<link rel="stylesheet" type="text/css" href="/JdBus/bundle.css">
<style>
body{
margin:0px;
scroll:no
}
</style>
</head>
<body>
<div id="root">
${html}
</div>
<script>
window.__INITIAL_STATE__ = ${JSON.stringify(initiaState)};
</script>
</body>
</html>
`;
}
}
module.exports = renderFullPage;
我的 server.js 是程序开始的地方:
// 各种钩子
require('babel-polyfill');
require('css-modules-require-hook')({
extensions: ['.css'],
generateScopedName: '[name]__[local]-[hash:base64:8]',
});
require('asset-require-hook')({
extensions: ['jpg', 'png', 'gif', 'webp'],
limit: 10000,
});
// 处理字体
require('asset-require-hook')({
extensions: ['ttf', 'woff', 'svg', 'eot', 'woff2'],
limit: 10000,
});
const app = require('./app');
app.listen(PORT, () => {
console.log('Node app is running on port:', PORT);
// 注册全局未捕获异常处理器
process.on('uncaughtException', (e) => {
console.error(`Caught exception:, ${e.stack}`);
});
process.on('unhandledRejection', (reason, p) => {
console.error(`Unhandled Rejection at: Promise , ${p}, reason: , ${reason.stack}`);
});
});
asset-require-hook
和css-modules-require-hook
处理服务器端无法处理的资产。
除了...... FOUC,一切都还可以!我的服务器端会正常生成html,而每次尝试时都没有css。
这是我的截图: enter image description here
答案 0 :(得分:0)
以下是我的webpack.config文件中的一个片段。我在React Server-side Rendering Example修正了FOUC问题。我认为它是在HtmlWebpackPlugin中使用HtmlWebpackHarddiskPlugin将alwaysWriteToDisk设置为true。
new ExtractTextPlugin({
filename: isDevelopment
? 'assets/styles/main.css'
: 'assets/styles/[name].[chunkhash].css',
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'src/index.html'),
minify: isProduction ? {collapseWhitespace: true, collapseInlineTagWhitespace: true} : false,
alwaysWriteToDisk: true,
}),
new HtmlWebpackHarddiskPlugin(),