项目中有多个html文件,互相功能独立,但是有些会共同引用同一个module,比如多个页面都会使用lodash。
webpack中通过遍历项目中的pages目录中的html文件,将html文件同级的js/index.js作为入口,动态配置HTMLWebpackPlugin。
现在想使用webpack的optimization.splitChunks将共同引用的模块单独打包,
比如a.html和b.html中的index.js都会引入lodash,正常打包后,a和b都会生成a.js和b.js,a.js和b.js中都会包含lodash(假如lodash是100kb),那么a.js和b.js都会是101kb,我想让a和b自身的业务逻辑单独打包,lodash单独打包,然后a.html和b.html共同引入lodash.js
那么应该怎么配置HTMLWebpackPlugin的chunk呢?
目录结构
:
.
├── assets
│ └── img
│ ├── loading-icon.gif
│ ├── logo.png
├── pages
│ ├── page-a
│ │ ├── index.html
│ │ ├── js
│ │ │ └── index.js
│ │ └── styles
│ │ └── index.scss
│ ├── page-b
│ │ ├── index.html
│ │ └── js
│ │ └── index.js
├── static
└── styles
├── common.scss
├── reset.css
└── variables.scss
下面是我的webpack配置
webpack.base.config.js
:
// 基础配置文件,包含了不同环境通用配置
const path = require('path')
const fs = require('fs')
const config = require('./config.js')
const { getHTMLFileList } = require('./utils')
const HTMLWebpackPlugin = require('html-webpack-plugin') // html-webpack-plugin 用于生成html
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const devMode = process.env.NODE_ENV !== 'production'
const htmlPath = path.join(__dirname, '..', '/src/pages/')
const htmlFIleList = getHTMLFileList(htmlPath)
// 根据每个html文件来生成HTMLWebpackPlugin实例 和 入口列表
const HTMLPlugins = [] // 保存HTMLWebpackPlugin实例
const Entries = {} // 保存入口列表
htmlFIleList.forEach(page => {
// 生成HTMLWebpackPlugin实例和入口列表
const htmlConfig = {
filename: `${page.name}.html`, // 生成的html文件名
template: page.path, // 原文件位置
}
const found = config.ignoreJs.findIndex(val => {
return val === page.name
}) // 筛选没有入口js的名
const jsFilePath = path.join(page.dirPath, 'js/index.js')
if (found === -1 && fs.existsSync(jsFilePath)) {
// 有入口js文件的html,添加本页的入口js和公用js,并将入口js写入Entries中
htmlConfig.chunks = [page.name] // html文件绑定入口JS和公用JS
Entries[page.name] = jsFilePath // 每个HTML文件添加一个入口,除非设置不用
} else {
// 没有入口js文件,chunk为空
htmlConfig.chunks = []
}
// console.log('====htmlConfig====', htmlConfig)
HTMLPlugins.push(new HTMLWebpackPlugin(htmlConfig))
})
const ESLintRule = () => ({
test: /\.js$/,
loader: 'eslint-loader',
enforce: 'pre', // loader种类,pre / post
include: [config.srcPath], // 检测的目录
options: {
formatter: require('eslint-friendly-formatter'), // 错误信息显示在终端上
// 如果option设置为true,Loader将始终返回警告。如果您正在使用热模块更换,您可能希望在开发中启用此功能,否则在出现夹板错误时将跳过更新。
emitWarning: !config.dev.showEslintErrorsInOverlay
}
})
module.exports = {
context: config.projectPath, // 入口、插件路径会基于context查找
entry: Entries,
resolve: {
extensions: ['.js', '.scss', '.css', '.json'], // 自动补全的扩展名
alias: {
// 省略路径
'@': path.join(__dirname, '..', 'src')
}
},
module: {
// 用了noParse的模块将不会被loaders解析,所以当我们使用的库如果太大,并且其中不包含import require、define的调用,我们就可以使用这项配置来提升性能, 让 Webpack 忽略对部分没采用模块化的文件的递归解析处理。
noParse: function (content) {
return /jquery|lodash/.test(content)
},
// 处理字体,生成图片,JS babel
rules: [
...(config.dev.useEslint ? [ESLintRule()] : []),
{
// html loader,主要用于html内图片等src路径解析
test: /\.html$/,
use: {
loader: 'html-loader',
options: {
minimize: false,
removeComments: false,
collapseWhitespace: false
}
}
},
{
test: /\.js$/,
include: [config.srcPath], // 在源文件目录查询
exclude: [config.assetsSubDirectory, /node_modules/],
use: [{ loader: 'babel-loader' }]
},
{
test: /\.(sa|sc|c)ss$/,
use: [
devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader'
]
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
include: [config.srcPath], // 在源文件目录查询
exclude: [config.assetsSubDirectory], // 忽略第三方的任何代码
use: [
{
// 导入字体文件,并最打包到output.path+ options.name对应的路径中
loader: 'url-loader',
options: {
limit: 8192,
name: 'fonts/[name].[hash:7].[ext]',
fallback: 'file-loader'
}
}
]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
include: [config.srcPath], // 在源文件目录查询
// exclude: [config.assetsSubDirectory], // 忽略第三方的任何代码
use: [
{
// 图片文件小于8k时编译成dataUrl直接嵌入页面,超过8k回退使用file-loader
loader: 'url-loader',
options: {
limit: 8192, // 8k
name: 'img/[name].[hash:7].[ext]', // 回退使用file-loader时的名称
fallback: 'file-loader' // 当超过8192byte时,会回退使用file-loader
}
}
]
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 8192,
name: 'media/[name].[hash:7].[ext]',
fallback: 'file-loader'
}
}
]
},
plugins: [
// 生成HTML文件
...HTMLPlugins // 扩展运算符生成所有HTMLPlugins
]
}
webpack.prod.config.js
// 生产环境配置文件
const webpackBase = require('./webpack.config.base.js') // 引入基础配置
const config = require('./config.js') // 引入配置
const webpack = require('webpack') // 用于引用官方插件
const webpackMerge = require('webpack-merge') // 用于合并配置文件
const CleanWebpackPlugin = require('clean-webpack-plugin') // 用于清除文件夹
const UglifyJSPlugin = require('uglifyjs-webpack-plugin') // 用于压缩js文件
const CopyWebpackPlugin = require('copy-webpack-plugin') // 用于拷贝文件
const TerserJSPlugin = require('terser-webpack-plugin') // This plugin uses terser to minify your JavaScript.
const MiniCssExtractPlugin = require('mini-css-extract-plugin') // webpack v4开始,处理css
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin') // 压缩提取出的css,并解决ExtractTextPlugin分离出的js重复问题(多个文件引入同一css文件)
const devMode = process.env.NODE_ENV !== 'production'
console.log('====process.env====', process.env.NODE_ENV)
console.log('====devMode====', devMode)
const webpackProd = {
// 生产配置文件
output: {
publicPath: config.build.assetsPublicPath, // 相对于服务器根目录的路径,用于加载资源。
filename: 'js/[name].[contenthash].bundle.js', // 构建文件名
chunkFilename: 'js/[id].[contenthash].js' // 按需加载的文件名
},
// 是否开启 sourceMap
devtool: config.build.prodSourceMap ? config.build.devtool : false,
mode: 'production',
optimization: {
splitChunks: {
chunks: 'all', // 哪些块进行优化,"initial"|"all"|"async"(默认) (string function)
minSize: 30000, // 要生成的块的最小大小,默认30000(30k)
minChunks: 1, // 分割前必须共享模块的最小块数,默认1
maxAsyncRequests: 5, // 最大异步并行请求数,默认5
maxInitialRequests: 3, // 最大初始化并行请求书,默认3
automaticNameDelimiter: '~', // 生成的名称分隔符,默认~ (string)
name: true, // 拆分快的名称,默认true(function true string)
cacheGroups: {
// 缓存组,可以继承和/或覆盖任何选项
// priority: 0, // 缓存组的优先级,默认0
// test: null, // 控制此缓存组选择的模块,默认空(function RegExp string)
// reuseExistingChunk: true, // 如果当前块包含已从主束拆分的模块,是否重用它。
vendors: {
name: 'vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
},
minimizer: [
new TerserJSPlugin({}),
// 压缩提取出的css,并解决ExtractTextPlugin分离出的js重复问题(多个文件引入同一css文件)
new OptimizeCSSAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano'),
canPrint: true
})
]
},
plugins: [
// 提取CSS
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: devMode ? '[name].css' : 'css/[name].[contenthash].css',
chunkFilename: devMode ? '[id].css' : 'css/[id].[contenthash].css'
}),
// 根据模块的相对路径生成一个四位数的hash作为模块id, 建议用于生产环境
// 当供应商模块不变时,保持module.id稳定
new webpack.HashedModuleIdsPlugin(),
// 删除dist文件夹
new CleanWebpackPlugin(),
// 复制静态资源
new CopyWebpackPlugin([
{
from: config.assetsSubDirectory,
to: '../dist/static',
ignore: ['.*']
}
])
]
}
module.exports = webpackMerge(webpackBase, webpackProd)
还少一个 HtmlWebpackPlugin 的配置