Reputation: 5155
I'm working on a large single-page application, with very large CSS & JS files that are generated by Webpack, which causes a flash of unstyled content when the app is initially loaded.
To avoid that, I would like to print the critical part of the CSS into the head of the document, so that the document will not appear unstyled while the browser is processing the rest of the JS & CSS.
This CSS is written in SASS, and is dependant on global app variables, so webpack must process the files to compile them from SASS to CSS before printing it to the document head.
How can this be done with Webpack?
Upvotes: 1
Views: 2247
Reputation: 1850
The way I did it is
html-webpack-plugin
to inject assets manually in the template;compilation
variable (example).Note. You can put the logic in a function in HtmlWebpackPlugin
options. Example is at the end of the answer.
This way, one has pretty much full control of how the assets are included, as in tags, attributes, positions.
There are several plugins that might also help with inlining CSS and JS, such as
but they take away the flexibility of having full control over format, logic, and output, and substitute it with limited config options.
Example config
plugins: [
//...
new HtmlWebpackPlugin({
template: 'path-to-index-template',
inject: false,
injectCriticalCss(htmlWebpackPluginStats, compilation) {
return lodash(htmlWebpackPluginStats.files.chunks)
.map(chunk => chunk.css)
.flatten()
.filter(cssFilename => /^critical\b/.test(cssFilename))
.map(cssFilename => `<style>${
compilation.assets[cssFilename].source()
}</style>`)
.join('\n');
},
injectNonCriticalCss(htmlWebpackPluginStats) {
return lodash(htmlWebpackPluginStats.files.chunks)
.map(chunk => chunk.css)
.flatten()
.filter(cssFilename => !/^critical\b/.test(cssFilename))
.map(cssFilename => `<link rel="preload" as="style" href="${
cssFilename
}" onload="this.rel='stylesheet'"/>`)
.join('\n');
},
//...
}),
//...
]
and then in the template
<html>
<head>
<!-- ... -->
<%= htmlWebpackPlugin.options.injectCriticalCss(htmlWebpackPlugin, compilation) %>
</head>
<body>
<!-- ... -->
<%= htmlWebpackPlugin.options.injectNonCriticalCss(htmlWebpackPlugin) %>
</body>
</html>
Upvotes: 2
Reputation: 1406
You need use ExtractTextPlugin in order to extract it from js file and write to a single file so that you can link it to head of html.
See how it is used with sass-loader.
const extractSass = new ExtractTextPlugin({
filename: '[name].css',
})
module: {
rules: [
{
test: /\.(scss)$/,
use: extractSass.extract({
use: [{
loader: 'css-loader', options: {
sourceMap: true,
minimize: true
}
}, {
loader: 'sass-loader', options: {
sourceMap: true,
minimize: true
}
}],
// use style-loader in development
fallback: 'style-loader'
})
}
]
},
plugins: [
extractSass,
]
Upvotes: 1