webpack3 css分割

64 阅读1分钟

背景

基于vue框架的单页面应用,仅有一个入口,在对js包进行一系列拆分后,css成为阻塞加载的瓶颈。因为各路由下的所有css均被提取到一个css文件中,导致产出包中css文件体积很大;且由于单页面应用,仅有一个模板文件,无法支持css按需加载,因此,本文只拆分css。

实现方案

经调研,在webpack5以上支持MiniCssExtract插件提取css,低版本webpack仅支持ExtractTextPlugin

  1. 使用ExtractTextPlugin插件,在加载模块时,定制css的处理方式,将特定路径下的css打包到一起
/**
 * @file css.conf.js
 */
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const cssFileName = '.[chunkhash:6].css';

// 存储公共样式
const baseCssBundles = {
    Common: [
        'src/assets/'
    ],
    Vendor: [
        'node_modules/'
    ]
};
// 存储业务组件样式
const cssBundles = {
    bundleA: [
       path1,
       path2,
       ...
    ],
    bundleB: [
        ...
    ],
    bundleC: [
        ...
    ],
    ...
};
// 
let extracters = {
    extractMainStyle: new ExtractTextPlugin({filename: `静态文件存储路径+文件名+ main.${cssFileName}`, allChunks: true}),
};
Object.keys({...baseCssBundles, ...cssBundles}).forEach(name => {
    extracters[`extract${name}Style`] = new ExtractTextPlugin({
        filename: `静态文件存储路径+文件名+${name.replace(name[0], name[0].toLowerCase())}${cssFileName}`, 
        allChunks: true
    });
});
module.exports = {
    baseCssBundles,
    cssBundles,
    extracters
}
  1. webpack.conf.js中引入css.conf.js配置的css处理规则
/**
 * @file webpack.conf.js
 */
const {baseCssBundles, cssBundles, extracters} = require('./css.conf'); // css.conf.js与webpack.conf.js同目录层级,可根据实际路径调整

const isProduction = process.env.NODE_ENV === 'production';
module.exports = {
    ...
    module: {
        rules: [
             // 使用baseCssBundles配置的特定提取器实例将基础css导出(公共css文件)
            ...Object.keys(baseCssBundles).reduce((res, name) => {
                return res.concat(utils.styleLoaders({
                    extract: isProduction,
                    extracter: extracters[`extract${name}Style`],
                    include: [
                        ...baseCssBundles[name].map(p => resolve(p))
                    ],
                    ...
                }));
            }, []),
            // 将其余不属于cssBundles、baseCssBundles导出到特定的产出包(剩余css文件)
            {
                test: /.vue$/,
                loader: 'vue-loader',
                options: {
                    ...vueLoaderConfig,
                    loaders: utils.cssLoaders({
                        extract: isProduction,
                        extracter: extracters['extractMainStyle'],
                        exclude: [
                            ...Object.values(cssBundles).reduce((res, curPaths) => {
                                return res.concat(curPaths.map(p => resolve(p)));
                            }, [])
                        ],
                        ...
                    })
                },
                exclude: [
                    ...Object.values(cssBundles).reduce((res, curPaths) => {
                        return res.concat(curPaths.map(p => resolve(p)));
                    }, [])
                ]
            },
            // 使用cssBundles配置的特定提取器实例将按照业务划分的css导出(特定分包的css文件)
            ...Object.keys(cssBundles).map(name => 
                ({
                    test: /.vue$/,
                    loader: 'vue-loader',
                    options: {
                        ...vueLoaderConfig,
                        loaders: utils.cssLoaders({
                            extract: isProduction,
                            extracter: extracters[`extract${name}Style`],
                            include: [
                                ...cssBundles[name].map(p => resolve(p))
                            ],
                            ...
                        })
                    },
                    include: [
                        ...cssBundles[name].map(p => resolve(p))
                    ]
                })
            ),
        ]
    }
}

至此,webpack打包产出中css文件会被拆分成多个子文件,减少css文件的下载时间