简体   繁体   English

改善Webpack(4)CSS的构建性能

[英]Improve webpack (4) css build performance

I am migrating from gulp to webpack setup. 我正在从gulp迁移到webpack设置。 I want webpack process my js and css assets and pack them into bundles. 我希望webpack处理我的js和CSS资产并将它们打包成束。 I have set up 2 webpack config files: one for js and one for css. 我已经设置了2个webpack配置文件:一个用于js,一个用于css。

The total sizes of css and js assets in my project are similar: rougly 70 files (400kb minified) in each section. 我的项目中CSS和JS资产的总大小是相似的:每个部分中粗略地包含了70个文件(最小为400kb)。

My question is related to poor webpack performance when processing css assets compared to js. 我的问题与处理js资产相比js时webpack性能差有关。

To compare: 比较:

  • JS build (first run): 15-30 seconds JS构建(首次运行):15-30秒
  • JS build (with cache): 2 seconds JS构建(带缓存):2秒

  • CSS build (first run): 15 seconds CSS构建(首次运行):15秒

  • CSS build (with cache): 10 seconds CSS构建(带缓存):10秒

    Obviously the CSS builder doesn't use cache as efficiently as the CSS part. 显然,CSS构建器使用缓存的效率不如CSS部分。 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. 老实说,我认为它根本不使用缓存(node_modules / .cache没有任何关联),并且第二次运行更快的唯一原因是文件系统预热。

    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: 我进行了一个测试,其中我以监视模式运行webpack并修改了一个小文件(仅几行),该文件必须包含在更大的捆绑包中:

  • JS update: 150ms JS更新:150ms

  • CSS update: 1200-2000ms CSS更新:1200-2000ms

CSS builder doesn't perform very well here. CSS生成器在这里的效果不是很好。 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. 我尝试过使用cache-loader ,并将其放在提取插件之前和之后。 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). 当将缓存加载程序放置在提取插件的前面时,在监视模式下不会发射任何CSS(仅js文件)。 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. 我当前的猜测是sass loader不使用缓存,并且在每个模块更新时,整个捆绑包都必须从头开始编译,并再次遍历sass> postcss> css> extract pipe。 I wonder if delegating import management to postcss-import and running sass after postcss would do a better job. 我想知道将导入管理委托给postcss-import并在postcss之后运行sass是否会做得更好。 I have tried to write a sass-compatible import resolver for postcss-import . 我试图为postcss-import编写一个与sass兼容的导入解析器。 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. 在小型测试环境中,它的工作速度似乎要快一些,但是在我们的实际项目中使用它会导致webpack崩溃:(还没有足够的时间来找出原因。

How can I improve CSS build times of my current setup? 如何改善当前设置的CSS构建时间?

My JS and CSS configs are below. 我的JS和CSS配置如下。

JS: 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: 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;
  1. Add thread-loader to js handling loaders. 将线程加载器添加到处理加载器的js中。
  2. Replace css-loader with fast-css-loader and sass-loader with fast-sass-loader. 用fast-css-loader替换css-loader,用fast-sass-loader替换sass-loader。
  3. Place cache-loader as the first loader of js files and after extract plugin in the css loaders. 将cache-loader放置为js文件的第一个加载器,并将其放置在css加载器中的extract插件之后。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM