一般在项目中除了js和css需要处理之外,图片也是使用比较多的,目前在项目中使用图片的方式基本有两种:
- 新建一个img标签,然后给标签的src属性进行赋值 注意:通过require()方法引入的资源在经过webpack处理的时候其返回值和file-loader的版本有关联
- 如果file-loader的版本是4.x,那么直接返回资源本身;
- 如果file-loader的版本是5.x+,那么返回的是一个module对象,资源路径是存放在该对象的default属性上的;
如果采用import语法来加载,就不会有这种版本考虑的问题,直接import xxx from '文件路径'即可。
function createImage(){
let img = new Image();
img.src= require('../img/test1.png').default;
document.body.appendChild(img);
}
createImage();
- 给一个元素如div设置背景图片,也就是css的background-image属性用url引入
function createImage(){
const ele = document.createElement('div');
ele.style.width = 200 + 'px';
ele.style.height = 200 + 'px';
ele.className = 'bg-image';
return ele;
}
createImage();
/* 在css文件中 */
.bg-image{
background-image:url('../img/woman.webp');
background-size;cover;
}
webpack自身是不识别这些图片资源的,要处理这些资源必须要借助于其对应的file-loader来进行处理,file-loader的作用就是帮助我们处理import和require()方式引入文件资源,然后将处理之后的资源输出到最终的打包之后的dist文件夹中,这样页面就建立起了和资源的引用关系。
- 安装
npm i file-loader -D
- 配置file-loader 配置file-loader的时候有几个注意点:
- 匹配资源的正则表达式中可以写成/.(png|jpg|jpeg|gif|svg)$/这种形式的,也可以写成 /.(png|jpe?g|gif|svg)$/,jpe?g中e后面的问好代表e可能会出现0次或者一次
- 匹配资源的后缀中不可以包含webp,这种格式的图片经过webpack处理重命名之后就会失效而无法加载
{
test: /\.(png|jpe?g|gif|svg)$/,
use: [
{
loader:"file-loader"
}
]
}
- 输出资源名称的重命名 webpack默认基于file-loader处理文件资源之后会对文件进行重命名,一般来说会基于MD4摘要算法生成一个128位的hash值,然后每4位用一个16进制数字表示,最后输出到dist文件夹中的文件名称都会是一个32位的16进制数字组成的名称。很显然这种默认的配置不利于我们查询资源的对应关系,所以我们可以通过配置来自定义输出资源的名称规则。
webpack中关于file-loader最终输出的文件名称是基于[Placeholder]来进行处理的,下面是常见的Placeholder:
- [name]:处理文件的名
- [path]:处理文件相对于webpack配置文件的路径
- 区分hash、chunkhash、contenthash文件指纹 共同点:
- hash、chunkhash、contenthash都是用来为打包后的文件进行命名的时候为了避免文件名称重复而使用的placeholder占位符
- hash、chunkhash、contenthash都是通过MD4的散列函数处理之后生成的一个128位的hash值,用32个16进制的数字表示
区别: 如果现在我们的项目有两个入口index和main,那么在进行打包的时候:
注意:file-loader和url-loader或者assets module中配置的hash其实和上述的contenthash是一个意思。
好处:
- 只修改一个文件,再次发布的时候hash值不必全部变化,浏览器可以很好的利用缓存机制减少http请求,节省带宽
在了解了以上占位符之后,就可以在webpack中进行配置了(下面这个配置也是Vue官方脚手架中对于file-laoder的配置),此配置输出后的文件名为:test1.6f97a9.png
{
test: /\.(png|jpe?g|gif|svg)$/,
use: [
{
loader:"file-loader",
options:{
name:[name].[hash:6].[ext] // 代表输出的文件保留文件名和拓展,并且取hash值的前6位
}
}
]
}
- 指定输出文件的存放路径 webpack在打包的时候会见文件资源都默认全部打包到build文件夹下,这样不利于资源的分类和统一管理,如果要指定最终输出资源到一个文件夹有两个办法:
- 通过outputPath属性声明
{
test: /\.(png|jpe?g|gif|svg)$/,
use: [
{
loader:"file-loader",
options:{
name:"[name].[hash:6].[ext]", // 代表输出的文件保留文件名和拓展,并且取hash值的前6位
outputPath:'image' // 代表输出的文件夹名称为build下的image文件夹
}
}
]
}
- 直接在name属性中和文件名称一起声明
{
test: /\.(png|jpe?g|gif|svg)$/,
use: [
{
loader:"file-loader",
options:{
name:"./image/[name].[hash:6].[ext]", // 代表输出的文件保留文件名和拓展,并且取hash值的前6位,然后统一输出到image文件夹下
}
}
]
}
在webpack中除了file-loader之外,还有一个专门处理图片文件资源的url-loader,url-loader的用法基本和file-loader一样,主要是原理不同:
- file-loader的原理是对所有需要打包的图片资源做一个拷贝,拷贝到最终的dist文件夹中,然后对文件名做一个重命名
- url-loader的原理是对所有需要打包的图片经过base64算法转化为base64data,直接嵌入到打包出来的bundle.js当中,然后在加载页面的时候会随着js一起被请求下来
但是在开发中我们希望文件大小比较大的图片直接使用图片,而对文件比较小的图片使用base64进行编码,这样做的好处在于:
- 大文件如果经过base64编码,会增大js文件的体积并最终影响页面的加载速度,所以大图片文件最好直接复制;
- 小文件如果经过base64编码,可以随着页面的js一起被请求下来,减少不必要的http请求;
上述的需求可以提供配置url-loader的limit字段来实现,limit字段的值的单位是byte。
{
test: /\.(png|jpg|jpeg|gif|svg)$/,
use: [
{
loader:"url-loader",
options:{
name:"img/[name].[hash:6].[ext]" ,// 代表输出的文件保留文件名和拓展,并且取hash值的前6位,然后打包后存在build目录下的img文件夹中
limit:10 * 1024,
esModule:false // 配置
}
}
],
type: 'javascript/auto' // V5官网文档说的防止重复打包图片
}
在webpack5之前打包的时候处理图片资源要借助于url-loader、file-loader等专门的loader去实现图片资源的打包,但是在webpack5中已经不推荐这种做法了,而是加载这些资源都由一个内置的Asset module type(资源模块类型)来代替上面的那些loader完成图片等资源的打包。
Asset module type(资源模块类型)有下面四种类型,来对不同的loader进行替换:
- asset/resource 代替之前的file-loader
- asset/inline 代替之前的url-loader
- asset/source 代替之前的raw-loader
- asset 可以基于配置打包资源体积的最大值来决定最终打包的资源是一个独立文件还是一个base64 Data直接嵌入到js中
相比于原来复杂的file-loader配置,这里只需要在type属性中声明当前处理资源模块的类型为asset/resource即可:
{
test: /\.(png|jpe?g|gif|svg)$/,
type:"asset/resource",
},
默认情况下webpack会将资源打包到build文件夹下的根目录中,如果要指定资源打包后的存放路径,有两种方法:
- 在output属性中配置assetModuleFilename,这里是全局配置代表不仅仅是asset/resource这一种类型,其他所有资源模块类型打包后的文件都会放在这个目录下。并且这里在配置最终生成的文件名的时候也可以和配置file-loader的时候一样,通过各种placeholder来实现资源的重命名,如下:
module.exports = {
output: {
filename: "bundle.js",
path: path.resolve(__dirname, '../build'),
assetModuleFilename:"img/[name]-[hash:6][ext]"
},
}
- 针对于asset/resource这一种资源模块类型单独配置,需要指定一个generator对象,generator是生成的意思代表生成资源的路径,推荐这种写法:
{
test: /\.(png|jpe?g|gif|svg)$/,
type:"asset/resource",
generator:{
filename:"img/[name]-[hash:6][ext]" // [ext]自身代表"."+后缀名 前面不再需要补一个点
}
},
注意点:在配置资源文件的打包之后的存放路径的时候,可以基于各种placeholder来实现资源的重命名,但是这里有一个和配置loader时不同的地方在于:ext在这里是代表了资源的文件后缀名加一个点的;而之前配置loader的时候ext只代表文件后缀名。比如: webpack5中的资源模块:filename:"img/[name]-hash:6",[hash:6]和ext中间不加点,最终打包出来的也是xxx.png这种资源名 webpack5之前的loader配置:filename:"img/[name]-[hash:6].ext",[hash:6]和ext中间有一个点
asset/inline是用来代替url-loader的,它的配置很简单,只需要注意在配置的时候不要加generator对象就好了,因为asset/inline模块类型会将资源转化为base64编码的dataUrl,不存在最终输出的资源路径:
{
test: /\.(png|jpe?g|gif|svg)$/,
type:"asset/inline",
}
一个完整的dataUrl组成:data: + MIME type; base64,base64编码值
data:image/png;base64,YABgAAD/2wBDAAkGBwgHBgkICAgKCgkLDhcPDg0ND...
data:image/jpeg;base64,6V4mvDoaiTgEnpROIyHog6nrXegxUV5Jc966TRFP...
asset主要是用来解决资源体积较大的文件打包成为独立的文件,而资源提交较小的文件被打包成为base64编码的dataUrl,具体是基于parser对象中dataUrl配置的maxSize属性来配置,具体配置如下:
{
test: /\.(png|jpe?g|gif|svg)$/,
type:"asset",
generator:{
filename:"img/[name][hash:6][ext]",
},
parser:{
dataUrlCondition:{
maxSize:200 *1024
}
}
}
在项目中有可能会用到特殊的字体文件,或者字体图标、音视频等资源,在webpack5之前这些资源都可以基于file-loader来进行加载,但是在webpack5的asset module type推出之后,就不用专门去安装loader,而是可以配置资源类型就可以处理这些资源了,比如:
{
test:/\.ttf|woff|woff2$/i,
type:"asset/resource", // 等于file-loader 因为字体文件这些资源一般不会转化为base64编码的
generator:{
filename:"font/[name][ext]"
}
},
{
test:/\.mp3|mp4|flv$/i,
type:"asset/resource",
generator:{
filename:"audio/[name][ext]"
}
},