这是我参与8月更文挑战的第28天,活动详情查看:8月更文挑战
前面我们练习 webpack 中暴露全局变量和 webpack 内置插件的使用;今天我们练习多页面应用的配置和 sourceMap;
一、 sourceMap
为了提升代码的性能,我们开启了 webpack 压缩混淆代码的功能,但是如果代码运行时报错,压缩混淆后的代码十分不利于调试,为了解决这个问题,我们需要一个叫做 sourceMap 的机制。sourceMap 是浏览器的一种功能,可以根据 sourceMap 配置将压缩混淆后的代码虚拟还原成源码的样子。webpack 内置了相关配置,我们在打包时通过配置 devtool 选项来开启 sourceMap;sourceMap 有如下可选值:
source-map,生成 source-map 并且是全码映射,单独输出 .map 文件eval-source-map生成带有行和列策映射,但是不会产生单独的 source-map 文件cheap-module-source-map生成的 source-map 不带有行和列,产出单独 .map 文件cheap-module-eval-source-map生成 source-map,不带有行和列,不输出 .map 文件
值得一提的是,上面的顺序是按照详细程度降序排列的,越靠后表示详细程度越低。靠前的虽然详细程度很高,但是会拖慢打包的速度,一般用 eval-source-map;但是具体情况还要视情况而定,如果你的项目中需要 source-map 文件,如使用 sentry 等错误监控系统,那么就需要配置产出文件的方式;
1.1 修改配置文件开启 sourcemap
const path = require('path');
module.exports = {
entry: './src/index.js',
// mode: 'development',
mode: 'production',
+ devtool: "eval-source-map", // source-map
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
devServer: {
},
module: {
rules: [
]
},
plugins: [
],
// 配置优化配置项
optimization: {
minimizer: [
]
},
// 配置 externals
externals: {
vue: 'Vue' // import Vue from 'vue' 时,就会寻找 window.vue
}
}
1.2 效果如图
二、 webpack 配置多页面应用
之前我们的示例中都是以时下很流行的单页面应用配置的,但是有很多情况下我们会遇到多页面应用,webpack 同样支持多页面应用的处理;多页面涉及到几个问题:
- 多入口,js 文件的入口不再是一个 js 文件;
- 多出口,多个页面依赖不同的 js 文件,所以对应需要输出多个 js 文件
- 公用模块分离问题,项目中多个页面可能依赖相同的模块,这些模块并不需要打包多次
2.1. 多入口问题和多出口的问题
解决多入口,我们需要在 webpack 的 entry 配置一个对象;
解决多出口问题,需要修改 output 配置项,之前 output 的输出的 filename 是一个固定的 bundle.js ;但是多页面应用,的名字应该由的它的入口文件决定,所以不能写死成一个固定字符串,配合多页面应用 [name] 表示文件名, [hash:5] 生成文件名后跟随 5 位 hash 戳记;
最后,每个页面还需要引入特定的 js 文件,这个同样也不能混乱,例如打包区 index.html 引入的是 index.js,other.html 引入的是 other.js,那么打包后还要维持这种引入的关系,所以此时我们需要修改 HTMLWebpackPlugin 的配置了,此时 HTMLWebpackPlugin 的实例就不再是一个了,而是每个页面一个,并且在每个实例的配置中加入 chunks 配置字段,标识当前 HTML 文件需要的代码块是哪些。配置这些内容后,在 webpack 打包后,会自动将原来属于这个页面的 js 代码块插入到页面中,这样我们就初步实现了一个多页面应用程序的配置;
- 修改
webpack配置文件
module.exports = {
// entry: './src/index.js',
// mode: 'development',
mode: 'production',
devtool: 'eval-source-map',
entry: {
home: './src/index.js',
other: './src/other.js',
x: './src/x.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
+ filename: '[name].[hash].js'
},
devServer: {
// 开发服务器的配置
},
module: {
rules: [
]
},
plugins: [
+ // 为每个 HTML 文件设置一个 html-webpack-plugin 实例,chunks 属性是当前页面需要引入的 js 文件,如果有多个,就在数组中设置多项
+ new HTMLWebpackPlugin({
+ template: './src/index.html',
+ filename: "index.html",
+ chunks: ['home']
+ }),
+ new HTMLWebpackPlugin({
+ template: './src/other.html',
+ filename: "other.html",
+ chunks: ['other']
+ }),
// 抽离 css 文件
// 注入模块: key 是模块中可以访问的变量,value 是对应的模块
// 定义环境变量数据
// 清空打包的内容
new CleanWebpackPlugin(),
// 复制文件内容: 接收一个数组,数组项是对象,对象中的 from 表示被复制的目录或者文件,to 表示输出的目录
// 在打包后的代码中输出必要信息
new webpack.BannerPlugin('发上等愿,结中等缘,享下等福;择高处立,寻平处住,想宽处行')
],
// 配置优化配置项
optimization: {
minimizer: [
]
},
// 配置 externals
}
2.2 抽离公共模块
经过上面配置后,即可实现一个多页面应用的打包;此时,如果 index.js 和 other.js 同时都依赖于 x.js 这个模块,打包后我们会发现 x 这个模块既被 index.js 打包了进去,又被 other.js 打包了进去;如果这个模块被更多的模块依赖,那么将会被更多次打包,这样一来就会增大代码的体积;为了解决这个问题,我们需要配置提取公共模块;
提取公共模块需要在 webpack.config.js 的 optimization.splitChunks 选项,改项用于拆分代码, webpack4.x 以后用此功能代替之前的 CommonChunksPlugin;
-
配置如下:
-
cacheGroups: Object 缓存组,这个对象的每个 key 都对应一个 chunk 输出到文件输出目录,简言之一个 key 就是一个公共模块;- common: Object,定义一个叫做 common 的公共模块
- chunks: 'initial', 标识哪些模块会被优化
- minSize: 0, 打包后的代码超过该限制后的代码块会被提取成为公共模块,如果不超过该限制还是会打包到代码中,不会成为单独的模块
- minChunks: 2, 代码块被会被提取成公共模块需要被引用的最小次数
- vendors: Object,定义第二个 叫做 vendors 的模块,用于提取第三方代码
- priority: 1, 优先级,优先提取第三方代码
- test: /node_modules/,
- chunks: 'initial',
- minSize: 0,
- minChunks: 2
- common: Object,定义一个叫做 common 的公共模块
-
修改配置文件
module.exports = {
mode: 'development',
devtool: 'eval-source-map',
entry: {
home: './src/index.js',
other: './src/other.js',
x: './src/x.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[hash].js' // 配合多页面应用 [name] 表示文件名, [hash:5] 生成文件名后跟随 5 位 hash 戳记
},
devServer: {
// 开发服务器的配置
},
module: {
rules: [
]
},
plugins: [
],
// 配置优化配置项
optimization: {
minimizer: [
],
+ splitChunks: {
+ cacheGroups: {
+ common: {
+ chunks: 'initial',
+ minSize: 0,
+ minChunks: 2,
+ },
+ vendors: {
+ priority: 1,
+ test: /node_modules/,
+ chunks: 'initial',
+ minSize: 0,
+ minChunks: 2
+ }
+ }
+ }
},
// 配置 externals
externals: {
vue: 'Vue' // import Vue from 'vue' 时,就会寻找 window.vue
}
}
经过上面的配置后,我们再次执行打包的命令,再次检查打包输出的文件,我们发现会多出一个文件,文件名字中有 ~ 字符,这个文件就是提取出来的公共模块,包括这个 ~ 都是可以配置的。