背景
基于vue框架的单页面应用,仅有一个入口,在对js包进行一系列拆分后,css成为阻塞加载的瓶颈。因为各路由下的所有css均被提取到一个css文件中,导致产出包中css文件体积很大;且由于单页面应用,仅有一个模板文件,无法支持css按需加载,因此,本文只拆分css。
实现方案
经调研,在webpack5以上支持MiniCssExtract插件提取css,低版本webpack仅支持ExtractTextPlugin。
- 使用
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
}
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文件的下载时间