11.importLoaders 属性
问题:
test.css的内容如下:
.title {
transition: all .5s;
user-select: none;
}
login.css的内容如下:
/* 导入test.css */
@import './test.css';
.title {
color: #12345678;
}
再次npm run build发现运行之后的test.css里面的代码并没有做兼容性处理。
问题分析:
- login.css @import 语句导入了test.css
- login.css可以被匹配,当它被匹配到之后就是postcss-loader进行工作
- 基于当前的代码,postcss-loader拿到了login.css当中的代码之后分析基于我们的筛选条件并不需要做额外的处理
- 最终就将代码交给了css-loader
- 此时css-loader是可以处理@import media、 url … ,这个时候它又拿到了test.css文件,但是loader不会回头找
- 最终将处理好的css代码交给style-loader进行展示
解决问题:修改Webpack.config.js给css-loader设置一些属性。
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'build.js',
//output必须设置绝对路径,所以这里导入path模块
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
//简写方式
test: /\.css$/,
//先执行style-loader再执行css-loader
//顺序规则,从右往左,从下往上,因为兼容性处理要在css调用之前,所以需要将postcss-loader的配置放在css-loader右边
use: ['style-loader', {
loader: 'css-loader',
options: {
// css-loader工作时,遇到css文件时,再往前找一个loader,即追回到postcss-loader
importLoaders: 1
}
}, 'postcss-loader']
},
{
//简写方式
test: /\.less$/,
//先执行style-loader再执行css-loader
//顺序规则,从右往左,从下往上
use: ['style-loader', 'css-loader', 'postcss-loader', 'less-loader']
}
]
}
}
再次运行成功。运行结果如下,test.css的内容也被修改成功。
12.file-loader 处理图片
12.1 JS导入图片并写入HTML
在js文件中引入img图片并输出到页面上
要处理jpg、png等格式的图片,我们也需要有对应的loader: file-loader。file-loader的作用就是帮助我们处理import/require()等方式引入的一个文件资源,并且会将它放到我们输出的文件夹中;当然也可以修改它的名字和所在文件夹
安装file-loader
npm install file-loader -D
目录结构:
Image.js中导入图片并显示在页面上:
import oImgSrc from '../img/01.wb.png'
function packImg() {
// 01 创建一个容器元素
const oEle = document.createElement('div')
// 02 创建 img 标签,设置 src 属性
const oImg = document.createElement('img')
oImg.width = 600
// 写法1:使用require...default取值
// require导入默认一个对象,有一个default的键,代表的导入的内容
// oImg.src = require('../img/01.wb.png').default
// 写法2:lg.Webpack.js配置文件搭配使用,不需要写default取值
// esModule: false // 不转为 esModule
// oImg.src = require('../img/01.wb.png')
// 写法3:使用import导入,不需要写default或者config配置esModule
oImg.src = oImgSrc
oEle.appendChild(oImg)
return oEle
}
document.body.appendChild(packImg())
lg.Webpack.js
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.(png|svg|gif|jpe?g)$/,
// use: [
// {
// loader: 'file-loader',
// options: {
// esModule: false // 不转为 esModule,在js导入时无需写default取值
// }
// }
// ]
use: ['file-loader']
}
]
}
}
最终效果:
12.2 JS导入图片并设置到css样式
css-loader处理时,会默认将background-image: url('../img/02.react.png')
处理为require的形式,而require会返回一个ESModule,所以需要在Webpack配置中添加css-loader的属性值->esModule: false
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
img.css
.bgBox {
width: 240px;
height: 310px;
border: 1px solid #000;
background-image: url('../img/02.react.png');
}
Image.js
import oImgSrc from '../img/01.wb.png'
import '../css/img.css'
function packImg() {
// 01 创建一个容器元素
const oEle = document.createElement('div')
// 02 创建 img 标签,设置 src 属性
const oImg = document.createElement('img')
oImg.width = 600
// 写法1:使用require...default取值
// require导入默认一个对象,有一个default的键,代表的导入的内容
// oImg.src = require('../img/01.wb.png').default
// 写法2:lg.Webpack.js配置文件搭配使用,不需要写default取值
// esModule: false // 不转为 esModule
// oImg.src = require('../img/01.wb.png')
// 写法3:使用import导入,不需要写default或者config配置esModule
oImg.src = oImgSrc
oEle.appendChild(oImg)
// 03 设置背景图片
const oBgImg = document.createElement('div')
oBgImg.className = 'bgBox'
oEle.appendChild(oBgImg)
return oEle
}
document.body.appendChild(packImg())
lg.Webpack.js
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.(png|svg|gif|jpe?g)$/,
// use: [
// {
// loader: 'file-loader',
// options: {
// esModule: false // 不转为 esModule
// }
// }
// ]
use: ['file-loader']
}
]
}
}
13.设置图片名称与输出
修改file-loader的options用于设置图片名称和输出。
常见占位符:
[ext]: 扩展名
[name]: 文件名称
[hash]: 文件内容+MD4生成128为占位置,作为文件名
[contentHash]: 文件内容+MD4生成128为占位置,作为文件名
[hash:<length>]: hash截取,作为文件名
[path]: 文件路径
lg.Webpack.js
{
test: /\.(png|svg|gif|jpe?g)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'img/[name].[hash:6].[ext]',
// outputPath: 'img'
}
}
]
}
其中,目录有两种写法,一种为添加outputPath: 'img'
,另一种为直接在name处写入img/
重新打包后,目录如下:
14.url-loader 处理图片
14.1 什么是 url-loader
url-loader
会将引入的文件进行编码,生成 DataURL
,相当于把文件翻译成了一串字符串,再把这个字符串打包到 JavaScript
。
14.2 什么时候使用
一般来说,我们会发请求来获取图片或者字体文件。如果图片文件较多时(比如一些 icon
),会频繁发送请求来回请求多次,这是没有必要的。此时,我们可以考虑将这些较小的图片放在本地,然后使用 url-loader
将这些图片通过 base64
的方式引入代码中。这样就节省了请求次数,从而提高页面性能。
14.3 什么时候使用
一般来说,我们会发请求来获取图片或者字体文件。如果图片文件较多时(比如一些 icon
),会频繁发送请求来回请求多次,这是没有必要的。此时,我们可以考虑将这些较小的图片放在本地,然后使用 url-loader
将这些图片通过 base64
的方式引入代码中。这样就节省了请求次数,从而提高页面性能。
14.4 如何使用
- 安装
url-loader
npm install url-loader --save-dev
- 配置
webapck
module.exports = {
module: {
rules: [
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {},
},
],
},
],
},
};
- 引入一个文件,可以是
import
(或require
)
import logo from '../assets/image/logo.png';
console.log('logo的值: ', logo); // 打印一下看看 logo 是什么
简单三步就搞定了。
- 见证奇迹的时刻
Webpack
执行 Webpack
之后,dist
目录只生成了一个 bundle.js
。和 file-loader
不同的是,没有生成我们引入的那个图片。上文说过,url-loader
是将图片转换成一个 DataURL
,然后打包到 JavaScript
代码中。
那我们就看看 bundle.js
是否有我们需要的 DataURL
:
// bundle.js
(function(module, exports) {
module.exports = "data:image/jpeg;base64.........."; // 省略无数行
})
我们可以看到这个模块导出的是一个标准的 DataURL
。
一个标准的DataURL:
data:[<mediatype>][;base64],<data>
通过这个 DataURL
,我们就可以从本地加载这张图片了,也就不用将图片文件打包到 dist
目录下。
使用 base64
来加载图片也是有两面性的:
- 优点:节省请求,提高页面性能
- 缺点:增大本地文件大小,降低加载性能
所以我们得有取舍,只对部分小 size
的图片进行 base64
编码,其它的大图片还是发请求吧。
url-loader
自然是已经做了这个事情,我们只要通过简单配置即可实现上述需求。
14.5 options
- limit: 文件阈值,当文件大小大于
limit
的时候使用fallback
的loader
来处理文件 - fallback: 指定一个
loader
来处理大于limit
的文件,默认值是file-loader
我们来试试设一个 limit
:
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
name: 'img/[name].[hash:6].[ext]',
limit: 25 * 1024 // 25kb
}
}
]
},
/**
* 01 url-loader base64 uri 文件当中,减少请求次数
* 02 file-loader 将资源拷贝至指定的目录,分开请求
* 03 url-loader 内部其实也可以调用 file-loader
* 04 limit
*/
重新执行 Webpack
,由于我们引入的 logo.png
大于 1000
,所以使用的是 file-loader
来处理这个文件。图片被打包到 dist
目录下,并且返回的值是它的地址:
(function(module, exports, __Webpack_require__) {
module.exports = __Webpack_require__.p + "dab1fd6b179f2dd87254d6e0f9f8efab.png";
}),
14.6 源码解析
file-loader
的代码也不多,就直接复制过来通过注释讲解了:
import { getOptions } from 'loader-utils'; // loader 工具包
import validateOptions from 'schema-utils'; // schema 工具包
import mime from 'mime';
import normalizeFallback from './utils/normalizeFallback'; // fallback loader
import schema from './options.json'; // options schema
// 定义一个是否转换的函数
/*
*@method shouldTransform
*@param {Number|Boolean|String} limit 文件大小阈值
*@param {Number} size 文件实际大小
*@return {Boolean} 是否需要转换
*/
function shouldTransform(limit, size) {
if (typeof limit === 'boolean') {
return limit;
}
if (typeof limit === 'number' || typeof limit === 'string') {
return size <= parseInt(limit, 10);
}
return true;
}
export default function loader(src) {
// 获取 Webpack 配置里的 options
const options = getOptions(this) || {};
// 校验 options
validateOptions(schema, options, {
name: 'URL Loader',
baseDataPath: 'options',
});
// 判断是否要转换,如果要就进入,不要就往下走
// src 是一个 Buffer,所以可以通过 src.length 获取大小
if (shouldTransform(options.limit, src.length)) {
const file = this.resourcePath;
// 获取文件MIME类型,默认值是从文件取,比如 "image/jpeg"
const mimetype = options.mimetype || mime.getType(file);
// 如果 src 不是 Buffer,就变成 Buffer
if (typeof src === 'string') {
src = Buffer.from(src);
}
// 构造 DataURL 并导出
return `module.exports = ${JSON.stringify(
`data:${mimetype || ''};base64,${src.toString('base64')}`
)}`;
}
// 判断结果是不需要通过 url-loader 转换成 DataURL,则使用 fallback 的 loader
const {
loader: fallbackLoader,
options: fallbackOptions,
} = normalizeFallback(options.fallback, options);
// 引入 fallback loader
const fallback = require(fallbackLoader);
// fallback loader 执行环境
const fallbackLoaderContext = Object.assign({}, this, {
query: fallbackOptions,
});
// 执行 fallback loader 来处理 src
return fallback.call(fallbackLoaderContext, src);
}
// 默认情况下 Webpack 对文件进行 UTF8 编码,当 loader 需要处理二进制数据的时候,需要设置 raw 为 true
export const raw = true;
15.asset 处理图片
在 Webpack
出现之前,前端开发人员会使用 grunt
和 gulp
等工具来处理资源,并
将它们从 /src
文件夹移动到 /dist
或 /build
目录中。Webpack
最出色的功能之一就是,除了引入 JavaScript
,还可以内置的资源模块 Asset Modules
引入任何其他类型的文件。
在Webpack4
的时候以及之前,我们通常是使用file-loader
与url-loader
来帮助我们加载其他资源类型。
15.1 Asset Modules Type的四种类型
而Webpack5可以使用资源模块来帮助我们,称之为Asset Modules
,它允许我们打包其他资源类型,比如字体文件、图表文件、图片文件
等。
其中,资源模块类型我们称之为Asset Modules Type
,总共有四种,来代替loader
,分别是:
asset/resource
:发送一个单独的文件并导出URL,替代file-loader
asset/inline
:导出一个资源的data URI
,替代url-loader
asset/source
:导出资源的源代码,之前通过使用raw-loader
实现asset
:介于asset/resource
和asset/inline
之间,在导出一个资源data URI
和发送一个单独的文件并导出URL
之间做选择,之前通过url-loader+limit
属性实现。
不过在介绍这四种资源模块类型之前,我们先说一下怎么自定义这些输出的资源模块的文件名
15.2 自定义资源模块名称
15.2.1 assetModuleFilename
第一种方式,就是在 Webpack
配置中设置 output.assetModuleFilename
来修改此模板字符串,其中assetModuleFilename默认会处理文件名后缀的点,所以无需手动添加点。此方式为公共的处理方法,当需要同时处理图片资源和字体资源时,通用方法会导致两种资源类型放在同一个目录下,此处不建议使用assetModuleFilename。
比如关于图片的输出文件名,我们可以让其都输出在images
文件夹下面,[contenthash]
表示文件名称,[ext]
表示图片文件的后缀,比如.png、.jpg、.gif、jpeg等,[query]
表可能存在的参数
output: {
···
assetModuleFilename: 'images/[contenthash][ext][query]'
···
},
15.2.2 geneator属性
第二种方式,就是在module.rules里面某种资源文件配置的时候,加上geneator
属性,例如
rules: [
{
test: /\.png/,
type: 'asset/resource',
generator: {
filename: 'images/[contenthash][ext][query]'
}
}
]
【注意】
generator 的优先级高于 assetModuleFilename
15.3 四种类型的导入
首先我们先新建一个文件夹来测试,文件夹目录如下,我们在src下面新建一个assets文件夹,里面放上事先准备好的集中不同类型的图片
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'
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)
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>你是,永远的神</title>
</head>
<body>
</body>
</html>
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
}
}
}]
}
}
15.3.1 resource 资源类型
asset/resource
可以发送一个单独的文件并导出URL
我们将.jpeg
后缀的图片设置type
为asset/resource
,我们在index.js
里面导入该图片并插入在body中,即将其当成资源显示在页面上
npx Webpack
打包之后,dist文件夹下的images文件中就出现该图片
npx Webpack-dev-server --open``自动打开浏览器,我们在控制台中查看该图片类型,发现``asset/resource``类型确实可以
导出一个文件和其
URL路径
15.3.2 inline资源类型
asset/inline
导出一个资源的data URI
仿照上面的方式,我们将.svg
后缀的图片设置type为asset/inline
,我们在index.js
里面导入该图片并插入在body中,即将其当成资源显示在页面上,同时我们简单设置一下样式
不过不同的是,npx Webpack
打包之后,dist文件夹下面并没有打包过.svg
类型的图片
npx Webpack-dev-server --open``自动打开浏览器,我们在控制台中查看该图片类型,发现
asset/inline类型确实可以
导出Data URI形式的路径`
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tdNft1xs-1669024454621)(http://5coder.cn/img/1667899086_6db380698c5f300aa8f603a96259c3ef.png)]
15.3.3 source资源类型
source
资源,导出资源的源代码
仿照上面的方式,我们创建一个.txt
后缀的文本文件,设置type为asset/source
,我们在index.js
里面导入该文本并插入在body中,即将其当成资源显示在页面上,同时我们简单设置一下样式
不过不同的是,npx Webpack
打包之后,dist文件夹下面并没有打包过.txt
类型的文本文件
npx Webpack-dev-server --open
自动打开浏览器,我们在控制台中查看该文本类型,发现asset/source
类型确实可以导出资源的源代码
15.3.4 asset通用资源类型
asset
会介于asset/resource
和asset/inline
之间,在发送一个单独的文件并导出URL
和 导出一个资源data URI
之间做选择
默认情况下,Webpack5会以8k
为界限来判断:
- 当资源大于8k时,自动按
asset/resource
来判断 - 当资源小于8k时,自动按
asset/inline
来判断
我们可以手动更改临界值,设置parser(解析)
,其是个对象,里面有个固定的属性,叫dataUrlCondition
,顾名思义,data转成url的条件,也就是转成bas64的条件,maxSize
是就相当于Limit了
module:{
rules:[
···
{
test:/\.(gif|jpg)$/,
type:'asset',
parser:{
dataUrlCondition:{
maxSize : 100 * 1024
}
}
}
···
]
}
这里我们设置100 * 1024即100kb,来作为临界值
【1b * 1024 = 1kb,1kb * 1024 = 1M】
仿照上面的方式,我们将.gif
和.jpg
后缀的图片设置type为asset
资源类型,我们在index.js
里面导入2张图片并插入在body中,即将其当成资源显示在页面上,其中.gif
大小为128.11kb(超过了100kb的临界值),.jpg
大小为12kb(未超过100kb的临界值)
npx Webpack
打包之后,dist文件夹下面有打包过的.gif
类型的图片,但是没有打包过.jpg
类型的图片
npx Webpack-dev-server --open
自动打开浏览器,我们在控制台中查看2种图片类型,发现.gif
图片是单独一个文件的URL路径,而.jpg
图片是Data URI格式的base64路径
16.asset处理图标字体
同上面所说,处理字体图标文件时,需要将其视为resource
资源直接复制,所以需要使用asset/resource
。此时准备好的字体文件及其目录如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qfOGeexu-1669024454626)(http://5coder.cn/img/1667912567_615bc7ed1f6fdaa5eb09f02bc8918739.png)]
在font
目录中,准备了iconfont.css
及其字体文件,其中iconfont.css
中对font-family
进行赋值对应的字体。
单独常见font.js
文件,并在文件中引入iconfont.css
以及自定义的index.css
文件,创建页面DOM
元素并显示。
iconfont.css
@font-face {
font-family: "iconfont"; /* Project id 2250626 */
src: url('iconfont.woff2?t=1628066777598') format('woff2'),
url('iconfont.woff?t=1628066777598') format('woff'),
url('iconfont.ttf?t=1628066777598') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-linggan:before {
content: "\e602";
}
.icon-publish:before {
content: "\e635";
}
.icon-image:before {
content: "\e629";
}
index.css
.lg-icon {
color: red;
font-size: 50px;
}
Font.js
import '../font/iconfont.css'
import '../css/index.css'
function packFont() {
const oEle = document.createElement('div')
const oSpan = document.createElement('span')
oSpan.className = 'iconfont icon-linggan lg-icon'
oEle.appendChild(oSpan)
return oEle
}
document.body.appendChild(packFont())
当然,此时直接运行yarn build
肯定会报错,因为此时Webpack
不认识ttf/woff/woff2
等资源,所以需要单独使用asset/resouce
进行打包配置。
lg.Webpack.js
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
// assetModuleFilename: "img/[name].[hash:4][ext]"
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset',
generator: {
filename: "img/[name].[hash:4][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 30 * 1024
}
}
},
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource', // 使用资源复制
generator: {
filename: 'font/[name].[hash:3][ext]' // 指定字体文件输出路径
}
}
]
}
}
此时执行yarn build
,我们发现在dist目录下新增了font目录,font目录中的字体文件为Webpack拷贝而来。打开页面可以看到iconfont.css以及自定义的index.css文件样式已经生效。
17.Webpack 插件使用
插件机制是Webpack当中另外一个核心特性,它目的是为了增强Webpack项目自动化方面的能力,loader就是负责实现各种各样的资源模块的加载,从而实现整体项目打包,plugin则是用来去解决项目中除了资源以外,其它的一些自动化工作,例如:
- plugin可以帮我们去实现自动在打包之前去清除dist目录,也就是上一次打包的结果;
- 又或是它可以用来去帮我们拷贝那些不需要参与打包的资源文件到输出目录;
- 又或是它可以用来去帮我们压缩我们打包结果输出的代码。
总之,有了plugin的Webpack,几乎无所不能的实现了前端工程化当中绝大多数经常用到的部分,这也正是很多初学者会有Webpack就是前端工程化的这种理解的原因。
clean-Webpack-plugin
:自动清空dist目录
之前的测试中,每次都需要用户手动的删除dist目录,我们希望Webpack每次打包时,先将之前的dist目录删除,再进行打包,这里使用clean-Webpack-plugin
进行处理。
同样的,需要先进行安装clean-Webpack-plugin
yarn add clean-Webpack-plugin -D
之后按照其使用方法,在lg.Webpack.js中进行插件配置。首先使用require
导入clean-Webpack-plugin
,其中导出东西过多,需要进行解构:const { CleanWebpackPlugin } = require('clean-Webpack-plugin')
。其次每个导出对象都是一个类,都有其自己的构造函数constructor
,在plugins中使用时需要new CleanWebpackPlugin
。代码如下:
const path = require('path')
const { CleanWebpackPlugin } = require('clean-Webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
// assetModuleFilename: "img/[name].[hash:4][ext]"
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset',
generator: {
filename: "img/[name].[hash:4][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 30 * 1024
}
}
},
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: {
filename: 'font/[name].[hash:3][ext]'
}
}
]
},
plugins: [
new CleanWebpackPlugin() // 每个插件就是一个类
]
}
先使用yarn build
进行打包,生成dist
目录,随后在dist
目录中手动添加一个a.txt
文件,如果再次执行yarn build
后a.txt
被删除了,说明clean-Webpack-plugin
已经正常工作了。
18.html-webapck-plugin 使用
除了清理dist的目录以外,还有一个非常常见的需求就是自动去生成使用打包结果的HTML,在这之前HTML都是通过硬编码的方式单独去存放在项目根目录下的。
index.html
每次打包完成之后手动需要修改title
,以及打包产生的文件由于分包过后文件类型或者数量比较多,需要用户手动的进行修改,这些行为都可以通过html-Webpack-plugin
进行处理
默认情况下,不需要手动创建index.html文件,Webpack
在使用html-Webpack-plugin
插件后会默认在打包结果dist
目录自动创建index.html
文件。
首先手动删除准备好的index.html
,没有使用html-Webpack-plugin
插件时,执行yarn build
进行打包,通过观察发现dist
目录中并没有生成index.html
文件。
18.1 使用默认index.html模板
在配置文件中,首先导入html-Webpack-plugin。
const HtmlWebpackPlugin = require('html-Webpack-plugin')
在plugins字段中进行使用:
const HtmlWebpackPlugin = require('html-Webpack-plugin')
...
plugins: [
new HtmlWebpackPlugin()
]
...
此时进行yarn build打包处理,可以发现dist目录中已经有了index.html文件。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mv51eLgK-1669024454630)(http://5coder.cn/img/1667915057_650596a463dae3f02e60811981b2bdb8.png)]
此时index.html
内容是html-Webpack-plugin
默认提供的,可以在node_modules
中找到html-Webpack-plugin
中的default_index.ejs
查看。
18.2 使用自定义index.html模板
其中默认模板中的占位符在官方文档中有详细描述。
Name | Type | Default | Description |
---|---|---|---|
title | {String} | Webpack App | The title to use for the generated HTML document |
filename | `{String | Function}` | 'index.html' |
template | {String} | `` | Webpack relative or absolute path to the template. By default it will use src/index.ejs if it exists. Please see the docs for details |
templateContent | `{string | Function | false}` |
templateParameters | `{Boolean | Object | Function}` |
inject | `{Boolean | String}` | true |
publicPath | `{String | ‘auto’}` | 'auto' |
scriptLoading | `{‘blocking’ | ‘defer’ | ‘module’}` |
favicon | {String} | `` | Adds the given favicon path to the output HTML |
meta | {Object} | {} | Allows to inject meta -tags. E.g. meta: {viewport: 'width=device-width, initial-scale=1, shrink-to-fit=no'} |
base | `{Object | String | false}` |
minify | `{Boolean | Object}` | true if mode is 'production' , otherwise false |
hash | {Boolean} | false | If true then append a unique Webpack compilation hash to all included scripts and CSS files. This is useful for cache busting |
cache | {Boolean} | true | Emit the file only if it was changed |
showErrors | {Boolean} | true | Errors details will be written into the HTML page |
chunks | {?} | ? | Allows you to add only some chunks (e.g only the unit-test chunk) |
chunksSortMode | `{String | Function}` | auto |
excludeChunks | {Array.<string>} | `` | Allows you to skip some chunks (e.g don’t add the unit-test chunk) |
xhtml | {Boolean} | false | If true render the link tags as self-closing (XHTML compliant) |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6LxMqoHN-1669024454640)(http://5coder.cn/img/1667915415_704784ee69f5f9963d59803a0654d2a0.png)]
对于占位符,我们可以在plugin中进行传参,赋予其默认值。
new HtmlWebpackPlugin({
title: 'html-Webpack-plugin', // title占位符
})
const path = require('path')
const { DefinePlugin } = require('Webpack')
const { CleanWebpackPlugin } = require('clean-Webpack-plugin')
const HtmlWebpackPlugin = require('html-Webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
// assetModuleFilename: "img/[name].[hash:4][ext]"
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset',
generator: {
filename: "img/[name].[hash:4][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 30 * 1024
}
}
},
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: {
filename: 'font/[name].[hash:3][ext]'
}
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'html-Webpack-plugin', // title占位符
})
]
}
再次yarn build
进行打包后,index.html
的title
已经更新了。
此时我们使用的是html-Webpack-plugin内置的html模板文件。但是在实际使用过程中,我们可能需要使用特殊的模板文件。此时使用template
字段去定义自己的index.html模板。
new HtmlWebpackPlugin({
title: 'html-Webpack-plugin',
template: './public/index.html'
}),
此时使用yarn build
打包后,就会使用自定义的index.html
模板文件。
此时,网站图标的路径使用<link rel="icon" href="<%= BASE_URL %>favicon.ico">
,再使用DefinePlugin
(Webpack默认,无需安装)进行定义全局配置的常量。
new DefinePlugin({
BASE_URL: '"./"'
})
此时,完整的配置文件如下:
const path = require('path')
const { DefinePlugin } = require('Webpack')
const { CleanWebpackPlugin } = require('clean-Webpack-plugin')
const HtmlWebpackPlugin = require('html-Webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
// assetModuleFilename: "img/[name].[hash:4][ext]"
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset',
generator: {
filename: "img/[name].[hash:4][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 30 * 1024
}
}
},
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: {
filename: 'font/[name].[hash:3][ext]'
}
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'html-Webpack-plugin',
template: './public/index.html'
}),
new DefinePlugin({
BASE_URL: '"./"' // Webpack会将常量原封不动的拿走,所以需要使用引号包裹
})
]
}
再次进行打包后,结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n0HRrQU2-1669024454644)(http://5coder.cn/img/1667916420_f344a9bd9dd0b61dbbede8ea828a5f33.png)]
除了自定义输出文件的内容,同时输出多个页面文件也是一个非常常见的需求。其实配置非常简单,配置文件中添加一个新的HtmlWebpackPlugin对象,配置如下:
plugins: [
new CleanWebpackPlugin(),
// 用于生成 index.html
new HtmlWebpackPlugin({
title: 'Webpack Plugin Sample',
meta: {
viewport: 'width=device-width'
},
template: './src/index.html'
}),
// 用于生成 about.html
new HtmlWebpackPlugin({
filename: 'about.html', // 用于指定生成的文件名称,默认值是index.html
title: 'About html'
})
]
19.copy-Webpack-plugin
在项目中,一般还有一些不需要参与构建的静态文件,它们最终也需要发布到线上,例如我们网站的favicon.icon
,一般会把这一类的文件统一放在项目的public
目录当中,希望Webpack
在打包时,可以一并将它们复制到输出目录。
对于这种需求,可以借助于copy-Webpack-plugin
,先安装一下这个插件,然后再去导入这个插件的类型,最后同样在这个plugin
属性当中去添加一个这个类型的实例,这类型的构造函数它要求传入一个数组,用于去指定需要去拷贝的文件路径,它可以是一个通配符,也可以是一个目录或者是文件的相对路径,这里使用plugin,它表示在打包时会将所有的文件全部拷贝到输出目录,再次运行Webpack指令,打包完成过后,public目录下所有的文件就会同时拷贝到输出目录。
const path = require('path')
const { CleanWebpackPlugin } = require('clean-Webpack-plugin')
const HtmlWebpackPlugin = require('html-Webpack-plugin')
const CopyWebpackPlugin = require('copy-Webpack-plugin')
module.exports = {
mode: 'none',
entry: './src/main.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist'),
// publicPath: 'dist/'
},
module: {
rules: [
{
test: /.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /.png$/,
use: {
loader: 'url-loader',
options: {
limit: 10 * 1024 // 10 KB
}
}
}
]
},
plugins: [
new CleanWebpackPlugin(),
// 用于生成 index.html
new HtmlWebpackPlugin({
title: 'Webpack Plugin Sample',
meta: {
viewport: 'width=device-width'
},
template: './src/index.html'
}),
// 用于生成 about.html
new HtmlWebpackPlugin({
filename: 'about.html'
}),
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
globOptions: {
ignore: ['**/index.html'] // 必须写入**/, ** 两个星号的意思是在当前路径
}
}
]
})
]
}
20.babel 使用
由于Webpack
默认就能处理代码当中的import
和export
,所以很自然都会有人认为Webpack
会自动编译的ES6
代码。实则不然,那是Webpack
的仅仅是对模块去完成打包工作,所以说它才会对代码当中的import
和export
做一些相应的转换,它并不能去转换我们代码当中其它的ES6
特性。
如果需要将ES6
的代码打包并编译为ES5
的代码,需要一些其它的编译形加载器。这里安装一些额外的插件。
首先,Webpack
是可以识别ES6+
的语法的,这里来测试一下,在index.js
中写入ES6+
的语法,使用yarn build
进行打包,观察打包过后的代码可以发现,Webpack
原封不动的把index.js
中的ES6+
语法代码拿了过来,并没有进行任何处理。
所以针对ES6+语法,需要使用特殊工具进行处理,这里安装@babel/core
以及命令行工具@babel/cli
进行代码测试,看babel默认是否会帮助处理ES6+语法。
yarn add @babel/core @babel/cli
yarn babel
使用后发现,babel仍然没有帮我们处理ES6+语法,这是为什么呢?原因是babel还需要使用特殊插件进行处理。
yarn babel 目标路径 --out-put 输出路径
yarn babel src --out-put build
因此,我们需要特殊的插件来对箭头函数或者const、let关键字进行处理。
@babel/plugin-transform-arrow-functions
(处理箭头函数)@babel/plugin-transform-block-scoping
(处理块级作用域)
yarn add @babel/plugin-transform-arrow-functions @babel/plugin-transform-block-scoping
# 执行babel
yarn babel src --out-dir build --plugins=@babel/plugin-transform-arrow-functions,@babel/plugin-transform-block-scoping
重新执行后,发现箭头函数和let、const
关键字作用域已经被处理成var
关键字。
但是我们发现,每次需要处理不同的特殊情况,都需要安装不同的babel
插件,特别不方便。因此babel
将绝大多数有关ES6+
语法以及stage
草案的插件组合成一个集合@babel/preset-env
,以后只需要使用这一个集合就可以处理绝大多数的ES6+
语法。
# 安装@babel/preset-env
yarn add @babel/preset-env
# 使用babel进行编译
yarn babel src --out-dir build --presets=@babel/preset-env