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
打开浏览器,发现可以正常打印
本博客参考: