1、Loader的作用
webpack可以自动解析js和json格式的文件,这个是webpack开箱即用的功能,那如果要解析其他类型的文件呢,比如一张图片?
前面我们讲过 Asset Module功能可以,而作为最常用的解析工具,loader也可以将这些非js的文件转化为有效的模块。
webpack允许我们使用loader来处理文件,loader是一个导出为function的node模块,说白了,loader就是一个函数function。可以将匹配到的文件进行一次转换,同时loader可以链式传递。
2、Loader和Plugin的区别
在之前的文章,我也简单的聊过Plugin的作用,而两者经常被用来作比较,我也容易搞混。
其实,但是他们是完全两个不同的东西:
Loaders(加载器):是在打包构建过程中用来处理源文件的(JSX,Scss,Less…),一次处理一个Plugins(插件):并不直接操作单个文件,它直接对整个构建过程其作用

这里,我在网上找到了一张还不错的图示,从上面也能看出,Plugins是直接对整个构建过程生效,而Loaders是作用于某个具体的源文件类型。
所以,在后续使用的过程中,大家就不要在搞混了。
3、Loader的使用方式
一般loader的使用方式分为三种:
3.1、webpack.config.js配置文件
一般情况下,我们通过使用webbpack.config.js配置文件的方式来加载引入各种loaders
module.exports = {
module: {
rules: [
{
test: /\.txt$/,
use: 'raw-loader'
}
]
}
}
其中test这个属性用来识别哪些文件被转换,use这个属性用来在转换的时候,定义用哪个loader来进行转换
3.2、命令行参数
第二种方式,就是通过命令行参数方式
webpack --module-bind 'txt=raw-loader'
3.3、内联
第三种方式,就是通过内联使用
import txt from 'raw-loader!./file.txt'
4、常用的Loader
处理样式的Loader:style-loader、css-loader、less-loader、sass-loader等
处理文件的Loader:raw-loader、file-loader、url-loader等
用于编译的Loader:babel-loader、coffee-loader 、ts-loader等
用于校验测试的Loader:mocha-loader、jshint-loader、eslint-loader等
比如下面的配置文件,可以匹配.scss的文件,分别经过sass-loader、css-loader、style-loader的处理。
module.exports = {
module: {
rules: [
{
test: /\.scss$/,
use:[
{loader:'style-loader'},
{loader:'css-loader',options:{sourceMap:true,modules:true}},
{loader:'sass-loader',options:{sourceMap:true}}
],
或
use:['style-loader','css-loader','sass-loader']
exclude:/node_modules/
}
]
}
}
sass-loader转化sass为css文件,并且包一层module.exports成为一个js modulecss-loader则处理其中的@import和url()style-loader将创建一个style标签将css文件嵌入到html中,且该标签在head头里面
vue-loader、coffee-loader、babel-loader等可以将特定文件格式转成js模块,将其他语言转化为js语言和编译下一代js语言。
file-loader、url-loader等可以处理资源。file-loader可以复制和放置资源位置,并可以指定文件名模板,用hash命名更好利用缓存;url-loader可以将小于配置limit大小的文件转换成内敛Data Url的方式,减少请求。
raw-loader可以将文件已字符串的形式返回。
imports-loader、exports-loader等可以向模块注入变量或者提供导出模块功能,
比如 imports-loader(导入加载器)允许您使用依赖于特定全局变量的模块,这对于依赖全局变量$或this作为window对象的第三方模块非常有用。常见场景是:
- jQuery插件注入$,imports-loader?$=jquery
- 禁用AMD,imports-loader?define=false,等同于:var $ = require(“jquery”) 和 var define = false;
5、Loader的执行顺序
正常情况下,loader执行顺序遵循从右向左,从下到上的原则
1、数组方式使用(执行的顺序为倒序即 loader1 > loader2 > loader3)
module: {
rules: [
{
test: /\.js$/,
use: ["loader3", "loader2","loader1"],
},
],
},
2、多个rules方式(执行的顺序为倒序即 loader1 > loader2 > loader3)
module: {
rules: [
{
test: /\.js$/,
use: "loader3",
},
{
test: /\.js$/,
use: "loader2",
},
{
test: /\.js$/,
use: "loader1",
},
],
},
6、Loader的分类
一般情况下,loader分为 pre(前置)、normal、post(后置)、inline(行内,嵌在代码中的loader),这四大类(默认是normal),loader的执行顺序为:
pre(前置) > normal > inline(行内)> post(后置)
上面说loader的执行顺序是倒序,那如果我真的是希望强制更改loader3在 loader1之前执行就可以这么做(此时的执行顺序为 loader3 > loader1 > loader2)
module: {
rules: [
{
test: /\.js$/,
use: "loader3",
enforce:"pre" //增加了此行
},
{
test: /\.js$/,
use: "loader2",
},
{
test: /\.js$/,
use: "loader1",
},
],
},
inline loader的使用方式
console.log("hello")
let str = require("inline-loader!./a.js");
//这句话的意思就是把a.js的内容导入,并传递到inline-loader,然后require的是inline-loader处理后的结果
loader中可以使用的webpack提供的常用api
7、Loader加载CSS
首先我们先新建一个文件夹来测试,文件夹目录如下,这里我沿用了上一部分:Webpack5学习笔记(基础篇六)—— Assets资源模块的加载
的文件内容目录,除此之外,我们在src下面创建一个基础的.css文件。

style.css
.hello{
color:red
}
index.js
import hello from './hello'
import img1 from './assets/man.jpeg'
import img2 from './assets/store.svg'
import img3 from './assets/women.jpg'
import Txt from './assets/wenzi.txt'
import dynamic from './assets/dongtu.gif'
import './style.css'
hello()
const IMG1 = document.createElement('img')
IMG1.src = img1
document.body.appendChild(IMG1)
const IMG2 = document.createElement('img')
IMG2.src = img2
IMG2.style.cssText = 'width:200px;height:200px'
document.body.appendChild(IMG2)
const IMG3 = document.createElement('img')
IMG3.src = img3
document.body.appendChild(IMG3)
const TXT = document.createElement('div')
TXT.textContent = Txt
TXT.style.cssText = 'width:200px;height:200px;backGround:aliceblue'
document.body.appendChild(TXT)
const DYNAMIC = document.createElement('img')
DYNAMIC.src = dynamic
document.body.appendChild(DYNAMIC)
document.body.classList.add('hello')
hello.js
function hello(){
console.log("hello-world!!!")
}
export default hello
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>kobe,永远的神!!</title>
</head>
<body>
</body>
</html>
webpack.config.js(本文之后的webpack.config.js均是在此基础上增加)
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry : './src/index.js',
output : {
filename:'bundle.js',
path:path.resolve(__dirname,'./dist'),
clean:true,
//如果不设置,打包完之后资源会直接打包在dist目录下
assetModuleFilename:'images/[contenthash][ext][query]'
},
mode : 'development',
devtool:'inline-source-map',
plugins:[
new HtmlWebpackPlugin({
template:'./index.html',
filename:'app.html',
inject:"body"
})
],
devServer:{
static:'./dist'
},
module:{
rules:[{
test:/\.jpeg$/,
type:"asset/resource",
generator:{
filename:'images/[contenthash][ext][query]'
}
},{
test:/\.svg$/,
type:'asset/inline'
},{
test:/\.txt$/,
type:'asset/source'
},{
test:/\.(gif|jpg)$/,
type:'asset',
parser:{
dataUrlCondition:{
maxSize : 10 * 1024 * 1024
}
}
}
]
}
}
可以看到,我们通过style.css给hello设置了字体红色,并且在index.js中引入改css,同时给body加了一个class名hello,假如我们不安装任何css相关Loader,直接执行npx webpack打包,我们发现控制台会提示报错,提示我们应该使用一个合适的loader来帮助我们加载css

7.1、css-loader
所以我们需要安装css-loader,执行:
npm install css-loader -D
安装完成后,我们需要在webpack.config.js中的module的rules里面配置一下
module:{
rules:[
···
{
test: /\.css$/,
use: 'css-loader'
}
···
]
}
配置好后,再次执行npx webpack,这时候就可以顺利打包了。
然后我们可以执行npx webpack-dev-server --open,打开浏览器,观察这个样式是否生效。
可以看出,body被加上了一个hello的class名,但是head里面并没有引入外部css的样式或者有style这个属性

body中的文本字体颜色并没有变成红色

7.2、style-loader
所以表明,只有这一个css-loader是不能正常将css样式加载到页面,我们需要再安装另一个style-loader
npm install style-loader -D
这个style-loader会在当前的页面引入我们的css样式 ,安装完成之后,我们在webpack.config.js配置文件中引入
module:{
rules:[
···
{
test: /\.css$/,
use: ['style-loader','css-loader']
}
···
]
}
注意:
- style-loader和css-loader的位置不能颠倒,我们需要先用css-loader处理css文件,再用style-loader引入css文件
我们再次执行npx webpack打包,然后npx webpack-dev-server --open打开浏览器,这次我们发现head里面多了一个<style>标签,里面使我们写的css样式

页面上的字也变成了红色

7.3、less-loader
通常在平时工作中,我们常使用less或者sass等于处理工具,来帮助我们加载样式,所以我们也可以在webpack中配置它们,这里我们以less-loader为例。
首先,安装less-loader
npm install less-loader -D
然后我们在webpack.config,js配置文件里面配置一下
module:{
rules:[
···
{
test: /\.(css|less)$/,
use: ['style-loader','css-loader','less-loader']
}
···
]
}
然后我们在src目录下面,跟刚才style.css同级的位置创建一个style-less文件
style.less

@color:#f9efd4;
body{
background-color:@color
}
并在index.js里面引入,完成后执行npx webpack打包,并执行npx webpack-dev-server --open打开浏览器,我们发现head头里面多了一个style的标签,表明我们的less样式被添加成功

body的背景也变成了浅色

7.4、mini-css-extract-plugin(Webpack5新增)
通过上面的三种loader,我们成功的在页面上加载了css样式,但是我们发现都是通过在head头中的<style>来加载的,我们能不能将css样式单独抽离出来,然后在html中统一引入,这就要用到mini-css-extract-plugin这个插件了。
注意:
mini-css-extract-plugin这个插件是Webpack5新增加的,所以在使用的时候,我们Webpack的版本必须在5以上
安装mini-css-extract-plugin
npm install mini-css-extract-plugin -D
安装完成后,在webpack.config.js中配置,引入这个插件,并实例化
webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
entry : './src/index.js',
output : {
filename:'bundle.js',
path:path.resolve(__dirname,'./dist'),
clean:true,
//如果不设置,打包完之后资源会直接打包在dist目录下
assetModuleFilename:'images/[contenthash][ext][query]'
},
mode : 'development',
devtool:'inline-source-map',
plugins:[
new HtmlWebpackPlugin({
template:'./index.html',
filename:'app.html',
inject:"body"
}),
new MiniCssExtractPlugin()
],
devServer:{
static:'./dist'
},
module:{
rules:[
···
{
test:/\.(css|less)$/,
//MiniCssExtractPlugin将css样式抽离
use:[MiniCssExtractPlugin.loader,'css-loader','less-loader']
}
···
]
}
}
值得注意的是,之前我们是使用style-loader将css样式通过<style>放在head头里,现在我们要换一种引用方式,用实例化的MiniCssExtractPlugin.loader代替style-loader。
配置完后,执行npx webpack打包,我们发现style.css和style.less都被打包在了main.css中

同时打包后的html文件是通过<link>标签来引入

我们可以执行npx webpack-dev-server --open打开浏览器,发现浏览器上也是通过<link>标签来引入外部main.css

正如前面图片的引入路径可以自定义设置,css样式也可以,我们只需要在webpack.config.js实例化这个插件的时候,加上filename这个属性,这样我们就可以自己定义css的打包输出的文件夹名字和文件名字
webpack.config.js
···
plugins:[
new HtmlWebpackPlugin({
template:'./index.html',
filename:'app.html',
inject:"body"
}),
new MiniCssExtractPlugin({
filename:"styles/[contenthash].css"
})
],
···
配置完成后,再次执行npx webpack打包,发现生成了styles文件夹

执行npx webpack-dev-server --open打开浏览器,这次<link>标签引入的css就是一个新的路径了

7.5、css-minimizer-webpack-plugin(生产环境压缩CSS)
在上面,我们已经成功将css抽离提取出来了,但是发现打包后的css代码还是很多,并没有进行压缩,而在生产环境中,我们往往都是将代码进行压缩,所以我们需要借助一个新的插件css-minimizer-webpack-plugin
安装css-minimizer-webpack-plugin
npm install css-minimizer-webpack-plugin -D
安装完成后,我们在webpack.config.js中进行配置,我们引入css-minimizer-webpack-plugin后,不是在plugins里面实例化,而是在optimization的minimizer中进行实例化,同时我们将mode改为production生产环境模式
webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
entry : './src/index.js',
output : {
filename:'bundle.js',
path:path.resolve(__dirname,'./dist'),
clean:true,
//如果不设置,打包完之后资源会直接打包在dist目录下
assetModuleFilename:'images/[contenthash][ext][query]'
},
mode : 'production',
devtool:'inline-source-map',
plugins:[
new HtmlWebpackPlugin({
template:'./index.html',
filename:'app.html',
inject:"body"
}),
new MiniCssExtractPlugin({
filename:"styles/[contenthash].css"
})
],
devServer:{
static:'./dist'
},
module:{
rules:[
···
{
test:/\.(css|less)$/,
//MiniCssExtractPlugin将css样式抽离
use:[MiniCssExtractPlugin.loader,'css-loader','less-loader']
}
···
]
},
//production环境压缩css代码
optimization:{
minimizer:[
new CssMinimizerPlugin()
]
}
}
配置完后,执行npx webpack打包,我们发现这时打包后被抽离的css代码已经被压缩了

7.6、利用css插入images图像
在上一篇博客中,我们通过在webpack.config.js中配置4种Asset Modules Type来加载图片,而平常我们可能在css中引入background-image背景图之类,现在我们来尝试一下webpack是否可以正常编译
我们在刚才index.js的文本处加一个class名

然后在style.css给这个类名加一个background背景图

执行webpack.config.js打包,我们看到打包后的css确实有这张背景图
(这里及下文中webpack.config.js中的mode已恢复development)

执行npx webpack-dev-server --open,打开浏览器,发现确实背景图已添加成功

7.7、利用css加载fonts字体
在CSS3中增加了一个webfont, 我们可以在css中加载一个font字库,这样就可以在代码中定义icon图标了,这里我们需要用到前面的Asset Modules 资源模块,资源模块可以帮助我们加载任何的资源类型
我们在assets中放入事先准备好的iconfont.ttf文件

我们在style.css中定义用font-face定义这个字体,format设置成truetype

然后在index.js中引入当前style.css,并创建一个DOM元素,添加个类名,注意这里的innerHTML要设置成下载这个字体时的文字(这里是)

在webbpack.config.js中进行配置
module:{
rules:[
···
{
test:/\.(css|less)$/,
//MiniCssExtractPlugin将css样式抽离
use:[MiniCssExtractPlugin.loader,'css-loader','less-loader']
},
{
test:/\.(woff|woff2|eot|ttf|otf)$/i,
type:"asset/resource"
}
···
]
},
执行npx webpack打包,
npx webpack-dev-server --open打开浏览器,发现页面上成功加载了iconfont图标

8、Loader加载csv、xml等数据
其实前面提到的 图片,iconfont图标,文本文件都属于数据,这里我们聊得数据是像csv、tsv、xml、json等,而json在webpack天然支持,其他几个我们需要借助于loader
安装csv-loader、xml-loader
npm install csv-loader、xml-loader -D
我们在assets中创建一个csv和xml文件
(csv使用逗号来分割,tsv使用type来分割)


然后在index.js中引入这两个文件,并打印一下

在webpack.config.js中通过csv-loader、xml-loader配置一下
module:{
rules:[
···
{
test: /\.(csv|tsv)$/,
use:'csv-loader'
},{
test:/\.xml$/,
use:'xml-loader'
},
···
]
},
执行npx webpack打包,npx webpack-dev-server --open打开浏览器,查看控制台,可以看出,csv被打包成一个数组,cml被打包成一个对象

9、Loader加载自定义JSON模块parser
这里我们使用yaml、toml、json5来测试,其中json5相比于json文件,他可以添加注释,同时key也不用单引号来包裹起来,同时可以使用\n\r这种转义字符
安装yaml、toml、json5
npm install yaml toml json5 -D
我们在assets中创建一个yaml、toml和json5文件


然后在index.js中引入这三个文件,并打印一下

在webpack.config.js中引入这三个插件,并使用parser解析器进行配置
···
const toml = require('toml')
const yaml = require('yaml')
const json5 = require('json5')
···
module:{
rules:[
···
{
test:/\.toml$/,
type:'json',
parser:{
parse:toml.parse
}
},{
test:/\.yaml$/,
type:'json',
parser:{
parse:yaml.parse
}
},{
test:/\.json5$/,
type:'json',
parser:{
parse:json5.parse
}
}
···
]
},
执行npx webpack打包,npx webpack-dev-server --open打开浏览器,查看控制台

10、Babel-Loader
babel-loader使得 webpack 可以通过 babel 转译 JavaScript 代码,用来处理ES5以上的语法,将其编译为浏览器可以执行的js语法
10.1、未使用babel-loader
我们创建一个hello.js,在里面写一个ES6的Promise函数,函数内通过setTimeout,2秒以后打印”中国男足,永远的神“,然后通过async await来调用

在index.js里面执行hello(),执行npx webpack打包,在bundle.js中查看打包后的代码,发现这段ES6的代码并没有做转化,原封不动的被打包了

执行npx webpack-dev-server --open打开浏览器,2秒后打印字符串 “中国男足,永远的神”

现在是浏览器支持ES6的代码,那万一浏览器不支持,我们就不能正常打印
10.2、使用babel-loader
前面我们提到过,babel-loader是一个 npm 包,它使得 webpack 可以通过 babel 转译 JavaScript 代码。
babel 7 中 babel-core 和 babel-preset 被建议使用 @babel 开头声明作用域,因此应该分别下载 @babel/core 和 @babel/presets
babel的功能在于「代码转译」,具体一点,即将目标代码转译为能够符合期望语法规范的代码。在转译的过程中,babel 内部经历了「解析 - 转换 - 生成」三个步骤@babel/core这个库则负责「解析」,具体的「转换」和「生成」步骤则交给各种插件(plugin)和预设(preset)来完成。注意:babel-loader必须和babel-core结合使用,babel-core封装了babel-loader需要用到的api@babel/preset-*实际上就是各种插件的打包组合,也就是说各种转译规则的统一设定,目的是告诉loader要以什么规则来转化成对应的js版本
首先,安装babel-loader @babel/core @babel/preset-env
npm install babel-loader @babel/core @babel/preset-env -D
还是上面的hello.js

我们在webpack.config.js中配置一下
module:{
rules:[
···
{
test:/\.js$/,
//排除node_modules文件夹
exclude:/node_modules/,
use:{
//Es6转为Es5的写法
loader:'babel-loader',
options:{
presets:['@babel/preset-env'],
}
}
}
···
]
},
执行npx webpack打包,执行npx webpack-dev-server --open打开浏览器,发现浏览器上面没有内容,控制台报错,提示regeneratorRuntime is not defined

我们打开bundle.js发现这里面hello这个地方确实貌似用到regeneratorRuntime,所以我们需要继续安装

注意:
regeneratorRuntime 是webpack打包生成的全局辅助函数,由babel生成,用于兼 容async/await的语法
regeneratorRuntime is not defined这个错误显然是未能正确配置babel,我们需要继续安装以下插件:
npm install @babel/runtime -D
这个包中包含了regeneratorRuntime
npm install @babel/plugin-transform-runtime -D
这个插件会在需要regeneratorRuntime的地方自动require导包
在webpack.config.js中配置一下这两个插件
module:{
rules:[
···
{
test:/\.js$/,
exclude:/node_modules/,
use:{
//Es6转为Es5的写法
loader:'babel-loader',
options:{
presets:['@babel/preset-env'],
//兼容async/await写法
plugins:[
[
'@babel/plugin-transform-runtime'
]
]
}
}
}
···
]
},
执行npx webpack打包,查看bundle.js,webpack帮助我们进行处理,将ES6的代码转化了

然后npx webpack-dev-server --open打开浏览器,发现可以正常打印

本博客参考:
本文详细介绍了webpack中Loader的作用,如将非js文件转化为有效模块,以及Loader与Plugin的区别。Loader按从右到左的顺序执行,处理源文件,而Plugin直接作用于整个构建过程。文章列举了常见的Loader,如style-loader、css-loader、babel-loader等,并通过示例说明了Loader的配置和使用方法,包括处理CSS、图片、CSV和XML等数据。此外,还探讨了Babel-Loader如何将ES6代码转译为浏览器可执行的ES5代码。
659





