const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const webpack = require('webpack')
// 更多webpack配置,访问 https://cloud.tencent.com/developer/section/1477556 , https://www.cnblogs.com/tugenhua0707/p/9384953.html
module.exports = {
devServer: {
port: 3000,
progress: true
},
mode: "development",
entry: {
index: path.resolve(__dirname, 'src/index.js'),
index1: path.resolve(__dirname, 'src/index1.js')
},
output: {
filename: '[name].[hash:8].js',
path: path.resolve(__dirname, 'dist'),
},
resolve: { // 解析第三方包
// 解析第三方库时查找的位置
modules: [
path.resolve(__dirname, './node_modules')
],
// 按照以下顺序解析文件后缀名
extensions: ['.js', '.css', '.json'],
// 引入第三方库时按照以下顺序查找第三方库里的package.json入口
mainFields: ['style', 'main'],
// 别名,第三方库配置别名后,引入第三方库时只需要引入别名即可
alias: {
bootstrap: 'bootstrap/dist/css/bootstrap.css'
}
},
devtool: 'inline-source-map',
// devtool: 'eval',
// 不会产生单独的文件的情况,会集成到打包的js里
// 1.会产生单独的文件,会显示报错的行和列
// devtool: 'source-map',
// 2.不会产生单独的文件,会显示报错的行和列
// devtool: 'eval-source-map',
// 3.会产生单独的文件,不会显示报错的列
// devtool: 'cheap-module-source-map',
// 4.不会产生单独的文件, 不会显示报错的列
// devtool: 'cheap-module-eval-source-map',
// watch: true,
// watchOptions: { // 监控的选项
// poll: 1000, // 多少秒监控一次
// aggregateTimeout: 500, // 防抖,防止一直输入代码一直打包,单位毫秒
// ignored: /node_modules/ // 不需要监控哪个文件
// },
/**
*
* 配置项
* 类型 含义
* source-map 原始代码 最好的sourcemap质量有完整的结果,但是会很慢
* eval-source-map 原始代码 同样道理,但是最高的质量和最低的性能
* cheap-module-eval-source-map 原始代码(只有行内) 同样道理,但是更高的质量和更低的性能
* cheap-eval-source-map 转换代码(行内) 每个模块被eval执行,并且sourcemap作为eval的一个dataurl
* eval 生成代码 每个模块都被eval执行,并且存在@sourceURL,带eval的构建模式能cache SourceMap
* cheap-source-map 转换代码(行内) 生成的sourcemap没有列映射,从loaders生成的sourcemap没有被使用
* cheap-module-source-map 原始代码(只有行内) 与上面一样除了每行特点的从loader中进行映射
*
* 看似配置项很多, 其实只是五个关键字eval、source-map、cheap、module和inline的任意组合
* 关键字可以任意组合,但是有顺序要求
* 关键字 含义
* eval 使用eval包裹模块代码
* source-map 产生.map文件
* cheap 不包含列信息(关于列信息的解释下面会有详细介绍)也不包含loader的sourcemap
* module 包含loader的sourcemap(比如jsx to js ,babel的sourcemap),否则无法定义源文件
* inline 将.map作为DataURI嵌入,不单独生成.map文件
*
* 组合规则
* [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
* source-map 单独在外部生成完整的sourcemap文件,并且在目标文件里建立关联,能提示错误代码的准确原始位置
* inline-source-map 以base64格式内联在打包后的文件中,内联构建速度更快,也能提示错误代码的准确原始位置
* hidden-source-map 会在外部生成sourcemap文件,但是在目标文件里没有建立关联,不能提示错误代码的准确原始位置
* eval-source-map 会为每一个模块生成一个单独的sourcemap文件进行内联,并使用eval执行
* nosources-source-map 也会在外部生成sourcemap文件,能找到源始代码位置,但源代码内容为空
* cheap-source-map 外部生成sourcemap文件,不包含列和loader的map
* cheap-module-source-map 外部生成sourcemap文件,不包含列的信息但包含loader的map
*/
optimization: {
/**
chunks选项,决定要提取那些模块,详细 https://zhuanlan.zhihu.com/p/136443552
async:默认值,只提取异步加载的模块出来打包到一个文件中,异步加载的模块:通过import('xxx')或require(['xxx'],() =>{})加载的模块。
1.只要是被异步加载的模块,单独放入新的chunks中
2.只要是被同步加载的模块,不管复用没复用,保留一份在之前的chunk中
initial:提取同步加载和异步加载模块,如果xxx在项目中异步加载了,也同步加载了,那么xxx这个模块会被提取两次,分别打包到不同的文件中。
1.只要是被异步加载的模块,单独放入新的chunks中
2.对于复用的,每次都是被同步加载的模块,单独放入新的chunks中
3.对于复用的,一次被同步加载,这次保留一份在原来的chunks中,一次被异步加载,这次单独放入一份在新的chunks中
同步加载的模块:通过 import xxx或require('xxx')加载的模块。
all:不管异步加载还是同步加载的模块都提取出来,打包到一个文件中。
1.只要是被复用的,不管是异步加载还是同步加载的模块,单独放入新的chunks中
2.对于不复用的被同步加载的模块,保留在原来的chunks中
minSize选项:规定被提取的模块在压缩前的大小最小值,单位为字节,默认为30000,只有超过了30000字节才会被提取。
maxSize选项:把提取出来的模块打包生成的文件大小不能超过maxSize值,如果超过了,要对其进行分割并打包生成新的文件。单位为字节,默认为0,表示不限制大小。
minChunks选项:表示要被提取的模块最小被引用次数,引用次数超过或等于minChunks值,才能被提取。
maxAsyncRequests选项:最大的按需(异步)加载次数,默认为 6。
maxInitialRequests选项:打包后的入口文件加载时,还能同时加载js文件的数量(包括入口文件),默认为4。
优先级 maxInitialRequests / maxAsyncRequests < maxSize < minSize。
automaticNameDelimiter选项:打包生成的js文件名的分割符,默认为~。
name选项:打包生成js文件的名称。
cacheGroups选项,核心重点,配置提取模块的方案。里面每一项代表一个提取模块的方案。下面是cacheGroups每项中特有的选项,其余选项和外面一致,若cacheGroups每项中有,就按配置的,没有就使用外面配置的。
test选项:用来匹配要提取的模块的资源路径或名称。值是正则或函数。
priority选项:方案的优先级,值越大表示提取模块时优先采用此方案。默认值为0。
reuseExistingChunk选项:true/false。为true时,如果当前要提取的模块,在已经在打包生成的js文件中存在,则将重用该模块,而不是把当前要提取的模块打包生成新的js文件。
enforce选项:true/false。为true时,忽略minSize,minChunks,maxAsyncRequests和maxInitialRequests外面选项
*/
splitChunks: { // 代码分割
cacheGroups: { // 缓存组
common: { // 公共模块
chunks: "all",
minSize: 0, // 文件大小大于多少进行抽离,单位为B
minChunks: 2, // 文件被引用多少次进行抽离
},
vendor: {
priority: 1,
test: /node_modules/,
chunks: 'initial',
minSize: 0,
minChunks: 2
}
}
},
minimizer: [
new UglifyjsWebpackPlugin({
include: /.js$/, // 要包含的文件
exclude: /.min.js$/, // 过滤掉.min.js结尾的文件,这个后缀本身就是已经压缩好的代码,没必要进行二次压缩
cache: true, // 启用文件缓存,文件内容没变化不会重新压缩
parallel: true, // 使用多进程并行运行来提高构建速度,并行压缩
sourceMap: true, // 使用sourceMap将错误消息位置映射到模块,Type: Boolean Default: false
extractComments: false, // 将注释提取到单独的文件中
chunkFilter: (chunk) => { // 按照代码块名排除哪些需要被压缩,哪些不需要被压缩
// `vendor` 模块不压缩
if (chunk.name === 'vendor') {
return false;
}
return true;
},
uglifyOptions: {
output: {
beautify: false, // 是否输出可读性较强的代码,即会保留空格和制表符,默认为输出,为了达到更好的压缩效果,可以设置为false
comments: false // 是否保留代码中的注释,默认为保留,为了达到更好的压缩效果,可以设置为false
},
compress: {
drop_console: true, // 是否删除代码中所有的console语句,默认为不删除,开启后,会删除所有的console语句
collapse_vars: true, // 是否内嵌虽然已经定义了,但是只用到一次的变量,比如将 var x = 1; y = x, 转换成 y = 5, 默认为不转换,为了达到更好的压缩效果,可以设置为false
// 是否提取出现了多次但是没有定义成变量去引用的静态值,比如将 x = 'xxx'; y = 'xxx' 转换成
// var a = 'xxxx'; x = a; y = a; 默认为不转换,为了达到更好的压缩效果,可以设置为false
reduce_vars: true
},
warnings: false, // 是否在UglifyJS删除没有用到的代码时输出警告信息,默认为输出,可以设置为false关闭这些作用不大的警告
}
}),
new OptimizeCssAssetsWebpackPlugin()
]
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html',
filename: 'index.html',
minify: {
removeComments: true, // 清理html中的注释
removeEmptyElements: true, // 清理内容为空的元素
caseSensitive: true, // 以区分大小写的方式处理自定义标签内的属性
removeScriptTypeAttributes: true, // 去掉script标签的type属性,type="text/javascript"
removeStyleLinkTypeAttributes: true, // 去掉style和link标签的type属性,type="text/css"
minifyCSS: true, // 是否压缩html内的样式,构建后html内的样式,无论是写在style标签内还是直接写在元素上的样式,包含的空格、换行和最后一个分号都被清理
minifyJS: true, // 是否压缩html内的js
collapseWhitespace: true // 清除html中的空格、换行符
},
hash: true // 是否给生成的js添加hash值
}),
// 向打包文件里注入版本号注释
new webpack.BannerPlugin('make 2021 by pa'),
new webpack.DefinePlugin({
'process.env': "'production'"
}),
// 复制插件,打包时将某个目录下的文件拷贝到打包目录里
// new CopyWebpackPlugin([
// { from: './doc', to: './' }
// ]),
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, 'public'),
to: path.resolve(__dirname, 'dist/public')
}
]),
new MiniCssExtractPlugin({
filename: 'css/main.css'
}),
new webpack.ProvidePlugin({
$: 'jquery',
'_b': 'bootstrap' // 此处的bootstrap,就是上面配置的别名
// '_b': 'bootstrap/dist/css/bootstrap.css' // 如果上面没有配置别名,则这样处理
}),
new CleanWebpackPlugin()
],
module: {
rules: [
{
test: /.css$/,
use: [
// 'style-loader',
{ // 代替style-loader
loader: MiniCssExtractPlugin.loader
},
'css-loader',
'postcss-loader',
]
},
{
test: /.scss$/,
use: [
// 'style-loader',
{
loader: MiniCssExtractPlugin.loader
},
'css-loader',
'postcss-loader',
'sass-loader'
]
},
{
test: /.js$/,
use: {
loader: 'eslint-loader',
options: {
fix: true, // 如果发现不合法,自动修复
enforce: 'pre' // 设置该loader为前置loader,优先于babel-loader执行,意思就是在代码编译之前先进行格式校验
}
}
},
/**
* babel-loader调用babel核心@babel/core
* 但是,@babel/core虽然能识别js代码,但不知道如何转换代码,插件知道
* es6语法很多,转换es6代码的插件也就很多,把这些插件打成一个包,包被称为preset-env预设
*/
{
test: /.js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
],
plugins: [
// 当打包构建项目时,JavaScript 包会变得非常大,影响页面加载。
// 如果我们能把不同路由对应的组件分割成 不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
'@babel/plugin-syntax-dynamic-import' // 路由懒加载插件
]
}
},
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
}
]
}
}