3. 加载和处理其他资源
asset module type的介绍
- 我们当前使用的webpack版本是webpack5:
- 在webpack5之前,加载这些资源我们需要使用一些loader,比如raw-loader 、url-loader、file-loader;
- 在webpack5之后,我们可以直接使用资源模块类型(asset module type),来替代上面的这些loader;
- 资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
- asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现;
- asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现;
- asset/source 导出资源的源代码。之前通过使用 raw-loader 实现;
- asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源 体积限制实现;
- 比如加载图片,我们可以使用下面的方式:
output: {
filename: 'js/bundle.js',
path: path.resolve(__dirname, './dist'),
assetModuleFilename: 'img/[name].[hash:6][ext]' //方式一:自定义文件的输出路径和文件名
}
{
test: /\.(png|svg|jpe?g|gif)$/i,
type: 'asset/resource',
generator: {
filename: 'img/[name].[hash:6][ext]' // 方式二:自定义文件的输出路径和文件名
}
}
javascript
- url-loader的limit效果
rules: [
{
test: /\.(png|jpe?g|svg|gif)$/i,
type: 'asset',
generator: {
filename: 'img/[name].[hash:6][ext]'
},
parser: {
dataUrlCondition: {
maxSize: 100 * 1024
}
}
}
]
javascript
加载字体文件
- 如果我们需要使用某些特殊的字体或者字体图标,那么我们会引入很多字体相关的文件,这些文件的处理也是一样 的。
- 首先,我从阿里图标库中下载了几个字体图标:
- 在component中引入,并且添加一个i元素用于显示字体图标:
- 字体打包
- 这个时候打包会报错,因为无法正确的处理eot、ttf、woff等文件:
- 我们可以选择使用file-loader来处理,也可以选择直接使用webpack5的资源模块类型来处理;
// webpack.config.js
{
test: /\.(woff2?|eot|ttf)$/,
type: 'asset/resource',
generator: {
filename: 'font/[name].[hash:6][ext]'
}
}
javascript
认识plugin
- Webpack的另一个核心是Plugin,官方有这样一段对Plugin的描述:
- While loaders are used to transform certain types of modules, plugins can be leveraged to perform a wider range of tasks like bundle optimization, asset management and injection of environment variables.
- 上面表达的含义翻译过来就是:
- Loader是用于特定的模块类型进行转换;
- Plugin可以用于执行更加广泛的任务,比如打包优化、资源管理、环境变量注入等;
CleanWebpackPlugin
每次修改了一些配置,重新打包时,自动删除dist文件夹
npm i clean-webpack-plugin -D
shell
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.export = {
// 其他省略
plugins: [
new CleanWebpackPlugin()
]
}
javascript
HtmlWebpackPlugin
对HTML进行打包处理的插件
- 安装
npm i html-webpack-plugin -D
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.export = {
// 其他省略
plugins: [
new HtmlWebpackPlugin({
title: 'webpack案例', // 在进行htmlWebpackPlugin.options.title读取时,就会读到该信息
template: './public/index.html' // 指定我们要使用的模块所在的路径
})
]
}
javascript
- 根目录新建public目录
<!-- index.html 自定义模版 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 上面的代码中,会有一些类似这样的语法<% 变量 %>,这个是EJS模块填充数据的方式 -->
<link rel="shortcut icon" href="<%= BASE_URL %>favicon.ico" type="image/x-icon">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="app"></div>
</body>
</html>
html
DefinePlugin的介绍
- 在编译template模块时,有一个BASE_URL:
- ;
- 但是我们并没有设置过这个常量值,所以会出现没有定义的错误;
- 这个时候我们可以使用DefinePlugin插件;
- DefinePlugin允许在编译时创建配置的全局常量,是一个webpack内置的插件(不需要单独安装)
const { DefinePlugin } = require('webpack')
module.export = {
// 其他省略
plugins: [
new DefinePlugin({
BASE_URL: '"./"'
})
]
}
javascript
CopyWebpackPlugin
- 在vue的打包过程中,如果我们将一些文件放到public的目录下,那么这个目录会被复制到dist文件夹中。
- 这个复制的功能,我们可以使用CopyWebpackPlugin来完成;
- 安装
npm i copy-webpack-plugin -D
- 接下来配置CopyWebpackPlugin即可: p复制的规则在patterns中设置;
- from:设置从哪一个源中开始复制;
- to:复制到的位置,可以省略,会默认复制到打包的目录下;
- globOptions:设置一些额外的选项,其中可以编写需要忽略的文件:
- .DS_Store:mac目录下回自动生成的一个文件;
- index.html:也不需要复制,因为我们已经通过HtmlWebpackPlugin完成了index.html的生成;
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.export = {
// 其他省略
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
globOptions: {
ignore: [
'**/index.html',
'**/.DS_Store',
]
}
}
]
})
]
}
javascript
4.模块化原理和source-map
Mode配置
- Mode配置选项,可以告知webpack使用响应模式的内置优化:
- 默认值是production(什么都不设置的情况下);
- 可选值有:‘none’ | ‘development’ | ‘production’;
- 选项区别
Mode配置代表
认识source-map
- 我们的代码通常运行在浏览器上时,是通过打包压缩的:
- 也就是真实跑在浏览器上的代码,和我们编写的代码其实是有差异的;
- 比如ES6的代码可能被转换成ES5;
- 比如对应的代码行号、列号在经过编译后肯定会不一致;
- 比如代码进行丑化压缩时,会将编码名称等修改; p比如我们使用了TypeScript等方式编写的代码,最终转换成JavaScript;
- 但是,当代码报错需要调试时(debug),调试转换后的代码是很困难的
- 但是我们能保证代码不出错吗?不可能。
- 那么如何可以调试这种转换后不一致的代码呢?答案就是source-map
- source-map是从已转换的代码,映射到原始的源文件;
- 使浏览器可以重构原始源并在调试器中显示重建的原始源;
如何使用source-map
- 如何可以使用source-map呢?两个步骤:
- 第一步:根据源文件,生成source-map文件,webpack在打包时,可以通过配置生成source-map;
- 第二步:在转换后的代码,最后添加一个注释,它指向sourcemap;
// # sourceMappingURL=common.bundle.js.map
- 浏览器会根据我们的注释,查找响应的source-map,并且根据source-map还原我们的代码,方便进行调试。
- 在Chrome中,我们可以按照如下的方式打开source-map:
分析source-map
- 最初source-map生成的文件带下是原始文件的10倍,第二版减少了约50%,第三版又减少了50%,所以目前一个 133kb的文件,最终的source-map的大小大概在300kb。
- 目前的source-map长什么样子呢?
- version:当前使用的版本,也就是最新的第三版;
- sources:从哪些文件转换过来的source-map和打包的代码(最初始的文件);
- names:转换前的变量和属性名称(因为我目前使用的是development模式,所以不需要保留转换前的名 称);
- mappings:source-map用来和源文件映射的信息(比如位置信息等),一串base64 VLQ(veriable- length quantity可变长度值)编码;
- file:打包后的文件(浏览器加载的文件); psourceContent:转换前的具体代码信息(和sources是对应的关系);
- sourceRoot:所有的sources相对的根目录;
- [参考文档(MDN):](https://developer.mozilla.org/en- US/docs/Mozilla/JavaScript_code_modules/SourceMap.jsm)
生成source-map
- 如何在使用webpack打包的时候,生成对应的source-map呢?
- webpack为我们提供了非常多的选项(目前是26个),来处理source-map;
- https://webpack.docschina.org/configuration/devtool/
- 选择不同的值,生成的source-map会稍微有差异,打包的过程也会有性能的差异,可以根据不同的情况进行 选择;
- 下面几个值不会生成source-map
- false:不使用source-map,也就是没有任何和source-map相关的内容。
- none:production模式下的默认值,不生成source-map。
- eval:development模式下的默认值,不生成source-map
- 但是它会在eval执行的代码中,添加 //# sourceURL=;
- 它会被浏览器在执行时解析,并且在调试面板中生成对应的一些文件目录,方便我们调试代码;
source-map值
// webpack.config.js
module.exports = {
mode: 'development',
devtool: 'source-map'
}
javascript
- source-map:
- 生成一个独立的source-map文件,并且在bundle文件中有一个注释,指向source-map文件;
- bundle文件中有如下的注释:
- 开发工具会根据这个注释找到source-map文件,并且解析;
//# sourceMappingURL=bundle.js.map
eval-source-map值
// webpack.config.js
module.exports = {
mode: 'development',
devtool: 'eval-source-map'
}
javascript
- eval-source-map:会生成sourcemap,但是source-map是以DataUrl添加到eval函数的后面
inline-source-map值
- inline-source-map:会生成sourcemap,但是source-map是以DataUrl添加到bundle文件的后面
cheap-source-map
- cheap-source-map:
- 会生成sourcemap,但是会更加高效一些(cheap低开销),因为它没有生成列映射(Column Mapping)
- 因为在开发中,我们只需要行信息通常就可以定位到错误了
cheap-module-source-map值
- cheap-module-source-map:
- 会生成sourcemap,类似于cheap-source-map,但是对源自loader的sourcemap处理会更好。
- 这里有一个很模糊的概念:对源自loader的sourcemap处理会更好,官方也没有给出很好的解释
- 其实是如果loader对我们的源码进行了特殊的处理,比如babel;
cheap-source-map和cheap-module-source-map
- cheap-source-map和cheap-module-source-map的区别:
hidden-source-map值
- hidden-source-map:
- 会生成sourcemap,但是不会对source-map文件进行引用;
- 相当于删除了打包文件中对sourcemap的引用注释;
// 被删除掉的
//# sourceMappingURL=bundle.js.map
- 如果我们手动添加进来,那么sourcemap就会生效了
nosources-source-map值
- nosources-source-map:
- 会生成sourcemap,但是生成的sourcemap只有错误信息的提示,不会生成源代码文件;
- 正确的错误提示:
- 点击错误提示,无法查看源码:
多个值的组合
- 事实上,webpack提供给我们的26个值,是可以进行多组合的。
- 组合的规则如下:
- inline-|hidden-|eval:三个值时三选一;
- nosources:可选值;
- cheap可选值,并且可以跟随module的值;
[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
- 那么在开发中,最佳的实践是什么呢?
- 开发阶段:推荐使用 source-map或者cheap-module-source-map
- 这分别是vue和react使用的值,可以获取调试信息,方便快速开发;
- 测试阶段:推荐使用 source-map或者cheap-module-source-map
- 测试阶段我们也希望在浏览器下看到正确的错误提示;
- 发布阶段:false、缺省值(不写)
- 开发阶段:推荐使用 source-map或者cheap-module-source-map