Reputation: 2618
I am migrating from gulp to webpack setup. I want webpack process my js and css assets and pack them into bundles. I have set up 2 webpack config files: one for js and one for css.
The total sizes of css and js assets in my project are similar: rougly 70 files (400kb minified) in each section.
My question is related to poor webpack performance when processing css assets compared to js.
To compare:
JS build (with cache): 2 seconds
CSS build (first run): 15 seconds
CSS build (with cache): 10 seconds
Obviously the CSS builder doesn't use cache as efficiently as the CSS part. To be honest I don't think it uses caching at all (node_modules/.cache doesn't have anything related) and the only reason for the 2nd run to be faster is filesystem warmup.
The problem in watch mode is even bigger. I did a test where I run webpack in watch mode and modify one small file (just a few lines), which has to be included in a bigger bundle:
JS update: 150ms
CSS builder doesn't perform very well here. Also, the larger the bundles, the longer it takes to update them, even though a change is done in a small file which should be compiled instantly.
Furthermore, it seems that increasing the number of entry points also negatively affects update times. More entries = slower updates, even though the update only affects one tiny file.
I've tried playing with cache-loader
and placing it before and after extract plugin. Honestly it doesn't help much. When cache loader is placed in front of extract plugin, no css is being emitted in watch mode (only the js files). Placing cache loader after the extractor improves production build a bit, but slows down watch mode updates.
My current guess is that sass loader doesn't use caching and that on every module update, the entire bundle has to be compiled from scratch and go through sass > postcss > css > extract pipe all over again. I wonder if delegating import management to postcss-import
and running sass after postcss would do a better job. I have tried to write a sass-compatible import resolver for postcss-import
. It seems to work a bit faster in a small test environment, but using it in our real project causes webpack to crash :( Didn't have enough time to find out why that happens yet.
How can I improve CSS build times of my current setup?
My JS and CSS configs are below.
JS:
const path = require('path');
const merge = require('webpack-merge');
const EntryPlugin = require('webpack-watched-glob-entries-plugin');
const dir = require('./node/dirconfig');
// use NODE_ENV var to switch between production and development
const devel = (process.env.NODE_ENV == 'development');
// base config for both prod and devel modes
let config =
{
name: 'scripts',
// webpack preset
// this should take care of production optimizations automatically
mode: devel ? 'development' : 'production',
// use all js files inside bundle dir as entries
entry: EntryPlugin.getEntries(
dir.assets + '/js/bundle/*.js'
),
output: {
path: dir.static + '/js',
filename: "[name].js"
},
externals: {
jquery: 'jQuery'
},
resolve: {
modules: [dir.assets + '/js', 'node_modules']
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
},
]
},
plugins: [
new EntryPlugin(),
],
};
// additional config for development mode
const development =
{
// add eslint loader
module: {
rules: [
{
enforce: "pre", // make sure this rule gets executed first
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'eslint-loader',
options: {
cache: true,
},
},
},
]
}
};
module.exports = merge(config, devel ? development : {});
CSS:
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const EntryPlugin = require('webpack-watched-glob-entries-plugin');
const dir = require('./node/dirconfig');
// use NODE_ENV var to switch between production and development
const devel = (process.env.NODE_ENV == 'development');
// base config for both prod and devel modes
let config =
{
name: 'styles',
// webpack preset
mode: devel ? 'development' : 'production',
// use all .scss files which don't start with _ as entries
entry: EntryPlugin.getEntries(
dir.assets + '/sass/**/!(_*).scss'
),
output: {
path: dir.static + '/css',
filename: "[name].js"
},
// enable sourcemaps in devel mode
devtool: devel ? 'inline-source-map' : false,
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
// 'cache-loader',
{
loader: 'css-loader',
options: {
sourceMap: devel,
}
},
{
loader: 'postcss-loader',
options: {
sourceMap: devel,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: devel,
}
},
]
},
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css", // relative to path setting in the output section
}),
new EntryPlugin()
],
};
module.exports = config;
Upvotes: 0
Views: 951
Reputation: 163
Upvotes: 1