前言
通过上篇文章——弄懂这几个概念后,我对webpack有了更新的理解 的讲解,我们大致了解了:
- 使用
webpack
过程中出现的一些概念 - 结合打包过程中出现的概念,浅析了
webpack
打包流程,让大家对打包流程有个大致的理解
我们都知道,我们在实际开发过程中,使用webpack
无非就是为了最后输出浏览器能运行的css
、img
、js
、html
等前端资源。
那么,为了更贴近实际,我们这篇文章,就以如何输出css
、img
、js
、html
等前端资源为目的,学习一下如何配置webpack
。
学习大纲
这篇文章主要讲解以下基础配置:
- 处理
css、less
文件 - 处理图片(
img
)、字体(font
)、音乐(audio
)等前端资源文件 - 编译
es6+
语法及API
- 处理
html
文件
webpack
文档对配置写的算挺详细了,所以这里我也不想复制粘贴又写一遍,这样不如大家直接看文档。
授人以鱼不如授人以渔,学习不能硬学,应该要有一些方法跟技巧。因此,我更想在讲解配置的过程中,总结一些方法,让我们更好的理解webpack
的配置项。这样,一些类似的一些配置,我们照葫芦画瓢,看文档就可以配出来。
备注
文章涉及到的案例已经上传到 github:
- 为了阅读方便,文章只贴了相关代码,建议
fork
一下,看看完整代码;或者跟着文章一起边看边敲,这样印象会更深刻一些 - 创作不易,如果觉得有帮助的话,欢迎
star
处理css、less
文件
我们先用 learn-03 这个案例来看看webpack
如何处理样式文件。
我们安装相应的loader
解析:
less-loader
:解析less
为css
css-loader
:将css
解析为webpack
能识别的有效模块style-loader
:将解析出来的css
插入到
中
npm install less less-loader css-loader style-loader -D
然后配置一下:
// webpack.config.js
module.exports = {
...,
module: {
rules: [
{
test: /.(css|less)$/,
use: [
'style-loader',
'css-loader',
'less-loader',
],
},
]
}
}
loader
的解析是逆序执行(或从右到左),因此这个配置会按照(less-loader
→ css-loader
→ style-loader
)以下顺序执行:
- 将
less
解析成css
- 再将结果传给
css-loader
,解析成webpack
认识的有效模块 - 再将结果传给
style-loader
,将解析出来的样式插入到
)的顺序执行。
less
、css
、入口文件代码为:
// index.less
*{
margin: 0;
padding: 0;
}
h1{
color: red;
margin-top: 20px;
}
/* index.css */
h2{
color: green;
}
// index.js
import './assets/index.less';
import './assets/index.css';
输出:
我们会发现我们写的样式处理成功,并且被插入到中。
总结
我们 上篇文章 说过:
- 我们通过
import
或require
进来的资源文件,或我们项目中的每个文件,都可以看作为一个独立的模块 Loader
的作用就是把这些模块(module
)转换成webpack
能够识别的有效模块
webpack
人性化的暴露了module
配置项,就是专门用来配置相关loader
,以此解析相应的module
。因此,如果我们想解析我们源码中使用的某些模块,我们就应该:
- 先下载相应的
loader
- 直接找到
module
配置项,根据loader
使用方法直接配置
所以,我们按照上面总结的方法跟思想,可以试试自己配置处理一下.scss
或者.txt
文件
ok,我们成功得到了我们项目需要的样式(css
),接下来我们再看看如何处理图片(img
)等前端资源文件
处理前端资源文件
前端资源文件很多,在这里,我们粗略的把它们先分成两类:
- 常用类:一般就是
img
、font
、video
等文件 - 特殊类:
.csv
、.xml
等文件
这些资源只要import
或者require
进我们的源码,那么它一样也是模块。既然是模块,我们就需要安装相应的loader
进行解析,然后在module
配置项配置。
在wepack5
之前,对于常用类的资源,我们一般需要安装以下loader
来解析:
raw-loader
:将文件解析成字符串file-loader
:将import/require()
进来的文件,解析为一个完整引用的url
,并将文件发送到输出目录中url-loader
:可以把文件转成base64
我们经常用到的是file-loader
、url-loader
这两个。因为常用类资源,经常以url
形式引入到我们项目中;或者为了减少http
请求,直接转换成base64
但是在webpack5
后,webpack
已经内置上述几个loader
的功能,所以我们可以不用再安装以上的loader
了;我们可以直接使用webpack
自带的 asset module。
webpack5
之后内置的资源模块:
asset/source
:等同于raw-loader
asset/resource
: 等同于file-loader
asset/inline
:等同于url-loader
asset
:比较灵活,同时拥有asset/resource
与asset/inline
的功能。如果设置了文件大小限制,文件没有超出这个大小的话,会转为base64
;如果没设置大小限制,则功能跟file-loader
一样
这是我十分爱webpack5
的原因,因为我们不要安装那么多乱七八糟的loader
我们用 learn-04 这个案例,看看怎么使用资源模块来解析我们的前端资源。
我们把上一章解析样式的配置一起加进来,在less
跟js
中分别使用图片资源。
我们有两个图片:
preview.gif
:大小为349kb
ecj-cli.png
:大小为4kb
// webpack.config.js
module.exports = {
...,
module: {
rules: [
{
test: /.(css|less)$/,
use: [
'style-loader',
'css-loader',
'less-loader',
],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 1024 * 10 // 当图片 < 10kb 转成base64
}
},
generator: {
filename: '[name][ext]' // 设置输出的文件名字
}
}
]
}
}
入口文件:
// index.js
import './assets/index.less';
const img = new Image();
img.src = require('./assets/ecj-cli.png');
img.width = 500;
document.querySelector('#js_logo').append(img);
样式:
.bg_logo{
width: (1434/3px);
height: (1212/3px);
background-image: url('./preview.gif');
background-size: 100%;
}
输出结果:
我们会发现样式跟js
都处理图片成功:
preview.gif
大于10kb
,所以没有被转为base64
,而是返回了一个完整的引用路径(相当于file-loader
的功能)ejc-cli.png
小于10kb
的则被转为了base64
(相当于url-loader
的功能)。
上面的gif图,是我开发的一个在日常项目中十分实用的命令行工具 ejc-cli 。它可以将对接人员收集的
Excel
数据,按一定格式转成我们代码所需要的json
数据。它十分方便我们开发维护数据,并且还有跟其他人员对接数据。感兴趣的朋友可以看看 了解 ejc-cli
总结
通过import
或者require
进我们源码的资源,一样是一种模块。所以需要下载相应的loader
,然后到module
配置项进行配置。
对于常用类资源(img
、font
、video
、audio
等等):
- 因为常用,所以
webpack5
把处理它们的功能内置了,这样我们不用额外安装loader
,我们直接使用asset module
来管理 - 我们一般都使用
asset/resource
、asset/inline
、asset
这几个模块来管理常用类资源。因为我们常用类的资源,经常会在我们项目中引入使用,我们需要把它们解析成引入完整的url
,或者为了减少http
请求,直接转换成base64
对于特殊类资源(.csv
、.xml
等等):
- 我们可以理解为,因为不常用,所以
webpack
没有把它们的功能内置 - 因此,我们需要安装相应的
loader
来解析。例如我们想解析.csv
文件,我们需要安装csv-loader
来解析
按照上面总结的方法,可以试试自己配置处理一下video
或者font
等文件
好,到目前,我们已经得到了css
、img
。我们接下来看看,如何通过webpack
处理得到我们的js
编译es6+
时至今日,js
发展迅速,为了提高开发效率和代码质量,每年都会有一些新的语法或者新的API
出现。
例如,在语法方面出现了:
- 箭头函数:
()=>
- 类语法糖:
class People {}
- 解构赋值:
const [x, y] = [1, 2];
- ...
在API
方面又有:
Promise
Array.prototype.includes()
Object.keys()
- ...
但是这些新的内容在一些低版本浏览器上是不支持的,所以我们需要通过webpack
来将我们的es6+
编译成这些低版本浏览器能支持的版本。
我们先用 learn-05 这个案例,看看我们怎么配置webpack
编译es6+
。
我们安装相关依赖:
...
"devDependencies": {
"@babel/core": "^7.22.8",
"@babel/plugin-transform-runtime": "^7.22.7",
"@babel/preset-env": "^7.22.7",
"babel-loader": "^9.1.3",
"webpack": "^5.88.1",
"webpack-cli": "^5.1.4"
},
"dependencies": {
"core-js": "^3.31.1"
},
入口文件。使用了class
、Promise
、()=>{}
等es6+
语法及API
:
// index.js
class People {
constructor(name) {
this.name = name;
}
sayHi() {
return Promise.resolve(this.name);
}
}
const Lee = new People('Lee');
Lee.sayHi().then(name => console.log(`Hi, I am ${name}.`));
配置webpack
:
// webpack.config.js
module.exports = {
...,
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader'
}
}
]
}
}
配置Babel
。在项目根目录新建babel.config.js
文件:
// babel.config.js
const presets = [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {
version: require('core-js/package.json').version,
proposals: true
}
}
]
];
const plugins = [
'@babel/plugin-transform-runtime',
];
module.exports = {presets, plugins}
设置要兼容的目标浏览器。在package.json
添加browserslist
字段:
"browserslist": [
"ie 11"
]
在ie 11
运行的结果:
我们会发现:
- 我们写的
es6+
在ie 11
运行成功 Promise
这个es6+
API
,也被添加到了ie 11
里
说明我们编译的es6+
是成功的。
关于Babel
在上面配置中,大家会发现,如果我们想用webpack
编译es6+
,我们还需要在根目录添加babel.config.js
文件,步骤比处理其他模块要复杂麻烦的多。这是因为,编译es6+
的核心,是需要Babel
来配合的。Babel
是编译es6+
的一个工具。
由于本篇文章主要是讲解webpack
,所以在这里,我们对Babel
就大概提一下。以下是Babel
常用的一些配置。
如果我们开发的项目是应用程序或大型的项目,我们可以这么配置:
const presets = [ [ '@babel/preset-env', { modules: false, useBuiltIns: 'entry', // or useBuiltIns: 'usage', corejs: { version: '3.27.2', proposals: true } } ] ]; const plugins = [ '@babel/plugin-transform-runtime' ]; module.exports = {plugins, presets}; // index.js // If use useBuiltIns: 'entry' import 'core-js/stable'
如果我们是想开发一个第三方库,我们可以这么配置:
// Babel配置 const presets = [ [ '@babel/preset-env', { modules: false } ] ]; const plugins = [ [ '@babel/plugin-transform-runtime', { corejs: { version: 3, proposals: true } } ] ]; module.exports = {plugins, presets};
如果你想对Babel
已经有个了解,但处于懵懵懂懂的状态;又或者你想学习Babel
,可以看看我之前写的关于Babel
的专栏 Babel专栏。
相信我,看完你一定会有所收获。
总结
js
一样是import
或者require
进我们的源码,那么它也是模块。所以我们也要下载对应的loader
(babel-loader
)并且在module
配置项配置它。- 此外,我们除了配置
babel-loader
,最重要的是还需要配置Babel
(babel.config.js
)。
所以,如果想要“编译es6+
”,我们除了要配置webapck
,最重要的是我们还要配置babel.config.js
。如果你不想只是一味的复制粘贴Babel
配置,你还必须得学习Babel
相关知识。
还记得我们使用webpack
的目的吗?输出浏览器能运行的css
、img
、js
、html
等前端资源。
OK,到目前我们已经通过webpack
处理了css
、img
、js
。我们再来看看如何通过webpack
处理得到我们的html
处理html
文件
在上面的例子中,我们想在浏览器看效果,我们需要:
- 在根目录新建一个
html
文件 - 在新建的
html
文件中,手动引入打包出来的资源文件
这个过程太麻烦了,有没有一种办法,我们只用提供一个html
模板文件,然后通过webpack
编译后,它自动帮我们把打包后的文件都引入好。这样,我们打包完直接运行html
就能在浏览器看效果就好了呢?
webpack
这么人性化的东西,当然是有的。
我们先用 learn-06 这里例子看看如何利用webpack
达到我们上述的效果。
安装相关插件:
npm i html-webpack-plugin -D
这次webpack
配置来点不一样的:
我们设置打包后的js
储存目录为js
文件夹;为打包后的js
名字增加5个
长度的hash
值。
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
entry: {
index: './src/index.js'
},
output: {
path: path.resolve(__dirname, './dist'),
filename: './js/[name]-[chunkhash:5].js',
clean: true
},
plugins: [
new HtmlWebpackPlugin({
filename: path.resolve(__dirname, './dist/[name].html'),
template: path.resolve(__dirname, './index.html'),
title: 'HtmlWebpackPlugin',
})
],
}
打包后的文件结构为:
dist
├── index.html
└── js
└── index-efbfd.js
打包后的html
如下:
我们会发现:
- 我们的
html
、js
都被打包输出到dist
文件夹下,这十分方便我们部署(之前我们是在根目录下新建index.html
,因此打包后dist
文件夹内只有img
、js
等文件) - 打包后的
html
会帮我们自动引入好我们打包后的js
文件
分析
从上面的例子我们可以看出,想要webpack
处理html
文件,我们是需要在webpack
提供的plugins
配置项来配置的。可能有些人会疑惑html
也是一个模块,为什么是在plugins
配置,而不是在module
里面配置?我们来分析一下。
我们项目所有文件当然可以看作是一个模块,但是在module
里面配置还是在plugins
里面配置,取决于我们对这些模块的使用目的。一般我们在自己的源码中使用到这个模块,则需要相应的Loader
来解析。
在上文讲解如何处理img
、js
时,我们的目的是为了在源码中解析这些文件:
img
,我们需要将它转化成base64
js
,我们需要将es6+
的东西编译成es5
及以下
并且,我们的文件都是通过import
或是require()
的方式,引入到我们的项目中,因此这时候当然需要相应的Loader
来转换这些模块。
但是在处理html
文件时,我们的目的并不是为了要解析html
,我们只是想让打包出来的html
自动引用我们的js
、img
,这更像是使用自动引入的功能;而且,我们也没有把我们的html
引入进我们的项目,因此我们当然不需要相应的Loader
来解析。
还记得我们 上篇文章 我们讲解过:Loader
用于转化模块,Plugin
则用来加强webpack
打包编译时的功能。
在此处我们处理html
文件,目的更多侧重的是想要让打包出来的html
自动引用我们的js
、img
这个功能。因此我们需要在plugins
配置项来配置,增强webpack
打包编译时的功能。
同理,假设我们想要在项目中解析某个html
文件(在我们项目中,import html from './x.html'
),那我们就得安装相应的loader
(html-loader
),并且在module
配置项目中配置。
总结
在module
还是plugins
配置项里配置,取决于我们对这些模块的使用目的。
至此,我们已经成功通过配置webpack
拿到了一个项目中需要的css
、img
、js
、html
;这也相当于学会了webpack
的一些基本配置。
大家可以通过上面总结的一些方法,自己完成对sass
、font
、video
等资源的处理,加深一下印象。
学习总结
通过上面对一些配置项的讲解,我们可以有以下总结:
- 想要解析
css
,需要安装相应的loader
,并且在module
里配置 - 想要解析
img
、font
、video
等常用类资源,我们不用安装loader
(webpack
已经内置解析他们的功能)。我们一般在module
配置项里,使用asset/resource
、asset/inline
、asset
这三个内置模块来解析 - 想要解析
js
,我们除了要安装相应的loader
(babel-loader
),在module
配置项里配置,最重要的是我们还要学习Babel
的相关知识(感兴趣可以看看我的 Babel专栏) - 想要解析
html
文件,我们需要在plugins
配置项里配置html-webpack-plugin
插件 - 在
module
还是plugins
配置项里配置,取决于我们对这些模块的使用目的
通过上面的学习,我们整理一下,输出一个完整的webpack
基础配置,让大家加深印象:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
entry: {
index: './src/index.js'
},
output: {
path: path.resolve(__dirname, './dist'),
filename: './js/[name]-[chunkhash:5].js',
clean: true
},
module: {
rules: [
{
test: /.(css|less)$/,
use: [
'style-loader',
'css-loader',
'less-loader',
],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 1024 * 10 // When the image size is < 10kb it will be converted to base64
}
},
generator: {
filename: '[name]-[hash:5][ext]' // Set the name of the output file
}
},
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader'
}
}
]
},
plugins: [
new HtmlWebpackPlugin({
filename: path.resolve(__dirname, './dist/[name].html'),
template: path.resolve(__dirname, './src/index.html'),
title: 'Webpack Basic Configuration',
})
],
}
完整的基础配置在 learn-07 ,建议大家看看完整版,这里只贴了webpack.config.js
最后
- 希望这篇文章的一些方法总结,学习目的能让大家更好的学会如何配置
webapck
- 后面的文章将会深入配置。现实项目中,大多分开发环境、生产环境,因此我们将学习如何针对开发环境、生产环境不同的环境,进行不同
webpack
的配置,这更贴近我们现实项目。如果感兴趣的话可以关注一下这个 专栏 - 文章涉及到的
Babel
知识,如果你感兴趣,想学习,也可以看看 Babel专栏 - 文章涉及到的案例已经上传到 github,欢迎
star
或fork
学习
最后的最后,如果大家觉得文章有帮助到,创作不易,还请大家多点赞收藏评论,如果有异同点,欢迎评论讨论。