前言
大家好,我是东东吖
,一名前端工程师。通过上篇文章【webpack快速入门】如何手动给项目配置一个webpack打包工具?我想你对webpack已经有了一个初步的认识,没有webapck相关经验的朋友,建议先看上篇文章哦。今天我们来学习webpack是如何加载资源文件的,打包时有哪些常用的loader加载器。
Webpack核心工作原理
在学习lodder加载器之前,我们先来康康Webpack核心工作原理,让你对webpack有一个全面的认识,对于Webpack核心工作原理,查看官网的这张首屏就知道了。
webpack是基于模块化的打包(构建)工具,它把一切视为模块。
它通过一个开发时态的入口模块为起点,分析出所有的依赖关系,然后经过一系列的过程(压缩、合并),最终生成运行时态的文件。
他解决了在浏览器端,开发时态(devtime)和运行时态(runtime)的侧重点不一样的问题,所以webpack主要做的就是让你开发的时候随便写,然后其他的交给webpack帮你处理好,能用在生产环境的代码。 首先我们需要知道一些关键词:
Entry
: 入口,webpack构建第一步从entry开始。
module
:模块,在webpack中一个模块对应一个文件。webpack会从entry开始,递归找出所有依赖的模块。
Chunk
:代码块,一个chunk由多个模块组合而成,用于代码合并与分割。
Loader
: 模块转换器,用于将模块的原内容按照需求转换成新内容。
Plugin
:拓展插件,在webpack构建流程中的特定时机会广播对应的事件,插件可以监听这些事件的发生,在特定的时机做对应的事情。
以上就是webpack的核心工作原理,今天我们将学习loader加载器。
Webpack常用加载器分类
-
编译转换类 css-loader
-
文件操作类 file-loader
-
代码检查类 eslint-loader
css-loader加载器
webpack不仅仅是javaScript的打包工具,更是整个前端工程化的打包工具。
我们在src下面创建index.css文件,编写一个简单的样式,我们来尝试打包css文件
再把我们的打包入口改成这个css文件,执行打包命令,就会报错。因为webpack默认只会对javaScrip的文件进行打包,也就是它会默认把所有文件当成js文件去解析,而我们是让它执行css文件,css文件里面是css样式,所有自然会报错。
我们来看错误信息的提示,翻译过来就是需要一个loader加载器
。
webpack内部的loader只能对js进行加载,我们需要加载其他资源,就需要添加其他相应的loader加载器。
下载安装 css loader
yarn add css-loader --dev
复制代码
安装完成之后,我们需要在配置文件中添加相应的配置。
//webpack.config.js
const path =require('path')
module.exports={
mode:'none', //使用开发模式打包
entry:'./src/index.css', //打包前入口
output:{ //打包后出口
filename:'bundle.js', //打包后文件名
path:path.join(__dirname,'dist') //打包后路径
},
module:{
// 匹配不同的资源loader加载器
rules:[
{
test: /.css$/,
use:'css-loader'
}
]
}
}
复制代码
再次执行打包命令,我们发现就没有报错了,我们启动http-server,发现样式并没有生效?
原因在于打包之后有了css样式,但是没有使用它,所以还需要下载一个style loader 加载器来使用它。
下载安装 style loader
yarn add style-loader --dev
复制代码
配置loader,使用多个loader,是从后往前执行,所有要先写样式,再把样式通过标签渲染到页面。
//webpack.config.js
const path =require('path')
module.exports={
mode:'none', //使用开发模式打包
entry:'./src/index.css', //打包前入口
output:{ //打包后出口
filename:'bundle.js', //打包后文件名
path:path.join(__dirname,'dist') //打包后路径
},
module:{
// 匹配不同的资源loader加载器
rules:[
{
test: /.css$/,
use:[
'style-loader',
'css-loader' //使用多个loader,是从后往前执行,所有要先写样式,再把样式通过标签渲染到页面
]
}
]
}
}
复制代码
再次打包,我们就会发现打包文件里面不仅有css相关的代码,还有style相关的代码。
启动http-server,我们就会发现样式生效了,我们的字体变成了红色。
通过以上做法,我们确实可以打包css,但是webpack的打包入口还是以js,所以我们需要改写一下导入css的方式。我们在webpack配置文件中,将打包入口重新还原为main.js,始终以mian.js作为打包的入口。
//webpack.config.js
const path =require('path')
module.exports={
mode:'none', //使用开发模式打包
entry:'./src/main.js', //打包前入口
output:{ //打包后出口
filename:'bundle.js', //打包后文件名
path:path.join(__dirname,'dist') //打包后路径
},
module:{
// 匹配不同的资源loader加载器
rules:[
{
test: /.css$/,
use:[
'style-loader',
'css-loader' //使用多个loader,是从后往前执行,所有要先写样式,再把样式通过标签渲染到页面
]
}
]
}
}
复制代码
然后我们在main.js中引入css:
// 有点类似Java导入,这里teacher随意命名
import teacher from './common.js'
import "./index.css"
// 一定要带上teacher
teacher.getList()
teacher.save()
复制代码
重新打包,启动应用,我们发现样式还是生效的,当然我们除了在main中引入css,还可以在common.js中引入css,我们把main.js中引入css的代码注释,在common.js中引入。
//common.is
import "./index.css"
// 有点像类定义
export default {
getList() {
// ajax调用
console.log('东东吖,获取讲师列表')
},
save() {
// ajax调用
console.log('东东吖,保存讲师')
}
}
复制代码
重新打包,启动应用,我们发现样式,同样是生效的,这样我们就始终了以main.js为入口文件,其他模块通过引入的方式导入。看到这儿,熟悉vue的朋友们,你会发现它和vue有类似之处,是不是对vue项目理解更加深刻了呢?
file-loader加载器
目前,webpack社区提供了非常多的loader加载器,大多数加载器都类似css-loader,将资源文件转化为js代码的方式去工作,但是还有一些我们用到的资源文件,例如图片,字体,这类资源文件是没有办法通过js的方式去表现的,那么这类资源文件,我们需要用到文件资源加载器file-loader
。
我们在项目中添加一张图片图片,大家可以找任意一张图片放进项目中。
//下载文件资源加载器file-loader
yarn add file-loader --dev
复制代码
配置文件资源加载器规则
//webpack.config.js
const path =require('path')
module.exports={
mode:'development', //使用开发模式打包
entry:'./src/main.js', //打包前入口
output:{ //打包后出口
filename:'bundle.js', //打包后文件名
path:path.join(__dirname,'dist') //打包后路径
},
module:{
// 匹配不同的资源loader加载器
rules:[
{
test: /.css$/,
use:[
'style-loader',
'css-loader' //使用多个loader,是从后往前执行,所有要先写样式,再把样式通过标签渲染到页面
]
},
{
test:/.jpg$/,
use:[
'file-loader'
]
}
]
}
}
复制代码
执行打包命令,我们会发现在dist文件下会多出一个图片文件,不过图片文件的名字变了。
启动应用,输入localhost,我们就会发现图片正常显示了,并在能看见图片引用的地址就是我们打包后dist下的图片文件。
如果你的图片没有正常显示,图片指向的地址不是dist,而是项目根目录下,而根目录下是没有这张图片的,所有如果有这种情况,那么请在配置文件中加入这行配置代码。
如果没有报错,如果图片正常加载,就不要配这个publicPath:'dist/' , 配这个打包反而会报错。
配置了会是无效属性,请注释掉。
这里大家思考一下,在日常开发中,我们的资源文件并不是资源图片,就算是图片也不止只有jpg,还有png、jpeg等等。那么我们该怎么配置呢? 是复制一个文件加载器吗? 其实不是的,只需要写个与,也就是'|',然后我们就可以在项目中使用其他资源文件了。
//webpack.config.js
const path =require('path')
module.exports={
mode:'development', //使用开发模式打包
entry:'./src/main.js', //打包前入口
// publicPath:'dist/' , //注意dist后面的/不能省略
output:{ //打包后出口
filename:'bundle.js', //打包后文件名
path:path.join(__dirname,'dist') //打包后路径
},
module:{
// 匹配不同的资源loader加载器
rules:[
{
test: /.css$/,
use:[
'style-loader',
'css-loader' //使用多个loader,是从后往前执行,所有要先写样式,再把样式通过标签渲染到页面
]
},
{
test:/.(png|jpg)$/,
use:[
'file-loader'
]
}
]
}
}
复制代码
大家可以在项目中自己再添加一张png图片进行尝试,我就不赘述了,直接看结果吧。
总结:文件加载器工作过程中,webpack遇到了我们的图片文件,根据我们在配置文件中的配置,匹配到对应的文件加载器,文件加载器开始工作,将导入的文件拷贝到输出目录,再将拷贝到输出目录的路径作为当前模块的返回值返回,对应应用,资源就被发布出来了,对于模块,也能拿到对应的路径。
url-loader加载器
data url给了我们一种很巧妙的将图片“嵌入”到HTML中的方法。跟传统的用img标记将服务器上的图片引用到页面中的方式不一样,在Data URL协议中,图片被转换成base64编码的字符串形式,并存储在URL中。
图片在网页中的使用方法通常是下面这种利用img标记的形式:
<img src="images/myimg.gif ">
这种方式中,img标记的src属性指定了一个远程服务器上的资源。当网页加载到浏览器中 时,浏览器会针对每个外部资源都向服务器发送一次拉取资源请求,占用网络资源。大多数的浏览器都有一个并发请求数不能超过4个的限制。这意味着,如果一个 网页里嵌入了过多的外部资源,这些请求会导致整个页面的加载延迟。而使用Data URL技术,图片数据以base64字符串格式嵌入到了页面中,与HTML成为一体,它的形式如下:
从上面的base64字符串中你看不出任何跟图片相关的东西,我们将传统的img写法和现在的Data URL用法左右对比显示,你就能看出它们是完全一样的效果。但实际上它们是不一样的,它们一个是引用了外部资源,一个是使用了Data URL。
安装url-loader
yarn add url-loader --dev
复制代码
更改file-loader为url-loader
//webpack.config.js
const path =require('path')
module.exports={
mode:'development', //使用开发模式打包
entry:'./src/main.js', //打包前入口
// publicPath:'dist/' , //注意dist后面的/不能省略
output:{ //打包后出口
filename:'bundle.js', //打包后文件名
path:path.join(__dirname,'dist') //打包后路径
},
module:{
// 匹配不同的资源loader加载器
rules:[
{
test: /.css$/,
use:[
'style-loader',
'css-loader' //使用多个loader,是从后往前执行,所有要先写样式,再把样式通过标签渲染到页面
]
},
{
test:/.(png|jpg)$/,
use:[
'url-loader'
]
}
]
}
}
复制代码
我们删除以前的dist文件,重新执行打包,就会发现dist下面没有再生成图片文件。
我们打开bundle.js,就能发现我们的data url
我们把完整的data url地址复制到浏览器,就可以访问我们这张图片。
我们在来启动应用,会发现图片还是成功显示了,并且是以date url 的形式。
项目最佳实践方式:
- 小文件使用Data URL,减少请求次数。
- 大文件单独提取存放,提高加载速度。
url-loader支持配合选项,来满足最佳实践方式。
//webpack.config.js
const path =require('path')
module.exports={
mode:'development', //使用开发模式打包
entry:'./src/main.js', //打包前入口
// publicPath:'dist/' , //注意dist后面的/不能省略
output:{ //打包后出口
filename:'bundle.js', //打包后文件名
path:path.join(__dirname,'dist') //打包后路径
},
module:{
// 匹配不同的资源loader加载器
rules:[
{
test: /.css$/,
use:[
'style-loader',
'css-loader' //使用多个loader,是从后往前执行,所有要先写样式,再把样式通过标签渲染到页面
]
},
{
test:/.(png|jpg)$/,
use:{
loader:'url-loader',
options:{
limit :30*1024, //30KB,因为这里的单位是字节,所有需要*1024
}
}
}
]
}
}
复制代码
我们再次执行打包命令,就会发现dist文件下只会出现一张图片文件。但是我们的项目其实是有两张图片的。
原因就在于我们项目中的两张图片一张其实是25KB
而另外一张是48KB
那么我们刚刚在配置文件中配置了30KB的文件,那么超过30KB的文件还是会以url-loader的形式加载,而没有超过30KB的文件,则以data url的形式加载。
值得注意的是,如果要按这种配置,就必须同时下载url-loader和file-loader,因为超过配置限制的文件还是会以url的形式加载。
babel-loader加载器
因为模块打包需要,所以需要处理import和export; webapck需要将es6的语法编译成es5,需要babel-loader加载器
//下载bale-loader加载器、核心模块、插件
yarn add babel-loader @babel/core @babel/preset-env --dev
复制代码
在webpack中配置 babel-loader
//webpack.config.js
const path =require('path')
module.exports={
mode:'development', //使用开发模式打包
entry:'./src/main.js', //打包前入口
// publicPath:'dist/' , //注意dist后面的/不能省略
output:{ //打包后出口
filename:'bundle.js', //打包后文件名
path:path.join(__dirname,'dist') //打包后路径
},
module:{
// 匹配不同的资源loader加载器
rules:[
{
test:/.js$/,
use:"babel-loader"
},
{
test: /.css$/,
use:[
'style-loader',
'css-loader' //使用多个loader,是从后往前执行,所有要先写样式,再把样式通过标签渲染到页面
]
},
{
test:/.(png|jpg)$/,
use:{
loader:'url-loader',
options:{
limit :30*1024, //30KB,因为这里的单位是字节,所有需要*1024
}
}
}
]
}
}
复制代码
在common.js中新增一个箭头函数的方法
//common.is
// import "./index.css"
// 有点像类定义
export default {
getList() {
// ajax调用
console.log('东东吖,获取讲师列表')
},
save() {
// ajax调用
console.log('东东吖,保存讲师')
},
}
// // 定义一个简单的箭头函数
export var sum = (num1, num2) => { num1 = num1+1;return num1 + num2; }
复制代码
在main.js中引入调用
//main.js
// 有点类似Java导入,这里teacher随意命名
import teacher ,{ sum } from './common.js'
import "./index.css"
import bg from "./bg.jpg" //引入图片路径
import icon from "./icon.png" //引入图片路径
const img = new Image() //创建img
img.src=bg //设置imgd的src路径
document.body.append(img) //追加到body中
const iconImg = new Image() //创建img
iconImg.src=icon //设置imgd的src路径
document.body.append(iconImg) //追加到body中
// 一定要带上teacher
console.log("teacher",teacher);
teacher.getList()
teacher.save()
console.log("sum",sum(1,1));
复制代码
执行打包命令,启动应用,查看结果。
在bundle.js中查看打包结果,我们发现还是es6得写法。
配置插件
//webpack.config.js
const path =require('path')
module.exports={
mode:'development', //使用开发模式打包
entry:'./src/main.js', //打包前入口
// publicPath:'dist/' , //注意dist后面的/不能省略
output:{ //打包后出口
filename:'bundle.js', //打包后文件名
path:path.join(__dirname,'dist') //打包后路径
},
module:{
// 匹配不同的资源loader加载器
rules:[
{
test:/.js$/,
use:{
loader:"babel-loader",
options:{
presets:['@babel/preset-env']
}
}
},
{
test: /.css$/,
use:[
'style-loader',
'css-loader' //使用多个loader,是从后往前执行,所有要先写样式,再把样式通过标签渲染到页面
]
},
{
test:/.(png|jpg)$/,
use:{
loader:'url-loader',
options:{
limit :30*1024, //30KB,因为这里的单位是字节,所有需要*1024
}
}
}
]
}
}
复制代码
执行打包命令查看打包结果,这个时候我们发现箭头函数已经变成了普通函数了。
webpack只是打包工具,想要编译和转换es6及更高版本的新特性,需要使用加载器。
html-loader加载器
- 遵循ES Modules 标准的 ipport声明
- 遵循CommonJS标准的require函数
- 遵循 AMD标准的difine函数和require函数
-
- 样式代码中的@import指令和url函数
-
- HTML代码中图片标签的src属性
建议使用一个标准,不要混用,提高代码的可维护性。
接下来,我们来尝试一下。
- 样式代码中的@import指令和url函数
在index.css中写入样式
/* index.css */
h1{
color: red;
}
body{
min-height: 100vh;
background-color: #fff;
background-image: url(tiger.gif);
background-size: cover;
}
复制代码
在main.js中引入,并注释掉其他无关的代码。
在打包过程中,会先执行我们的css-loader,当webpack中遇到url就会去执行url-loader,执行打包命令,启动应用。
我们再来看@import指令,新增一个样式文件reset.css
在index.css使用@import指令引入: @import url(reset.css);
执行打包命令,启动应用,查看效果。
- HTML代码中图片标签的src属性
新建html文件
下载html-loader
下载html-loader
yarn add html-loader --dev
复制代码
配置html-loader
//webpack.config.js
const path =require('path')
module.exports={
mode:'development', //使用开发模式打包
entry:'./src/main.js', //打包前入口
// publicPath:'dist/' , //注意dist后面的/不能省略
output:{ //打包后出口
filename:'bundle.js', //打包后文件名
path:path.join(__dirname,'dist') //打包后路径
},
module:{
// 匹配不同的资源loader加载器
rules:[
{
test:/.js$/,
use:{
loader:"babel-loader",
options:{
presets:['@babel/preset-env']
}
}
},
{
test: /.css$/,
use:[
'style-loader',
'css-loader' //使用多个loader,是从后往前执行,所有要先写样式,再把样式通过标签渲染到页面
]
},
{
test:/.(png|jpg)$/,
use:{
loader:'url-loader',
options:{
limit :30*1024, //30KB,因为这里的单位是字节,所有需要*1024
}
}
},{
test:/.html$/,
use:{
loader:'html-loader'
}
}
]
}
}
复制代码
在main中引入:
执行打包,启动应用,查看效果:
点击src属性可以正常访问
注意:html不止有src属性,如a标签的href属性,要自己设置配置项。
自己开发一个Loader
我们本次来自己开发一个名字为markdown-loader加载器
,加深大家对loader加载器的理解。
新建一个.md文件,在main.js中引入
创建一个loader,webpack配置文件配置loader。
执行打包命令,发现已经打印出来了soure,但是也同时报了一个错。
原因就在于返回的不是html代码,需要一个loader,那么有两种方式,①返回html代码,②添加一个loader。
一、我们把returen里面的东西写成html代码,再次执行打包,就发现没有报错了。
二、安装一个loader
//安装解析md的loader
yarn add marked --dev
复制代码
在loader里面编写代码,发现打包报错。
require(“marked”)获得,需要使用marked函数的marked属性才可以,或者使用解构赋值,再次打包。可发现打包成功了。
查看打包结果,md文件已经被转化成为html了。
结束
相信通过本文,你将对webpack的loader加载器有了一个深入的了解, 对于本文章,你有任何疑问,可在评论区留言。如果想进前端交流群(目前群里有40余人前端工程师,气氛活跃,欢迎大家加入),可以加我微信fangdongdong_25,请备注掘金哦。