# 简单配置
该部分需要掌握:
Webpack 常规配置项有哪些?
常用 Loader 有哪些?如何配置?
常用插件(Plugin)有哪些?如何的配置?
Babel 的如何配置?Babel 插件如何使用?
# 1.1 安装依赖
毫无疑问,先本地安装一下 webpack 以及 webpack-cli
npm install webpack webpack-cli -D # 安装到本地依赖
安装完成 ✅
- webpack-cli@4.7.2
- webpack@5.44.0
# 1.2 工作模式
webpack 在 4 以后就支持 0 配置打包,我们可以测试一下 新建 ./src/index.js 文件,写一段简单的代码
const a = 'Hello ITEM'
console.log(a)
module.exports = a;
此时目录结构
webpack_work
├─ src
│ └─ index.js
└─ package.json
直接运行 npx webpack,启动打包
打包完成,我们看到日志上面有一段提示:The 'mode' option has not been set,...
意思就是,我们没有配置 mode(模式),这里提醒我们配置一下
只需在配置对象中提供 mode 选项:
module.exports = {
mode: 'development',
};
从 CLI 参数中传递:
$ webpack --mode=development
# 1.3 自动清空打包目录
每次打包的时候,打包目录都会遗留上次打包的文件,为了保持打包目录的纯净,我们需要在打包前将打包目录清空
这里我们可以使用插件 clean-webpack-plugin 来实现
安装
$ npm install clean-webpack-plugin -D
配置
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 引入插件
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
// ...
plugins:[ // 配置插件
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin() // 引入插件
]
}
# 1.4 区分环境
本地开发和部署线上,肯定是有不同的需求
- 本地环境:
需要更快的构建速度 需要打印 debug 信息 需要 live reload 或 hot reload 功能 需要 sourcemap 方便定位问题 ...
- 生产环境:
需要更小的包体积,代码压缩+tree-shaking 需要进行代码分割 需要压缩图片体积 ...
针对不同的需求,首先要做的就是做好环境的区分
- 本地安装 cross-env [https://www.npmjs.com/package/cross-env]
npm install cross-env -D
- 配置启动命令 打开 ./package.json
"scripts": {
"dev": "cross-env NODE_ENV=dev webpack serve --mode development",
"test": "cross-env NODE_ENV=test webpack --mode production",
"build": "cross-env NODE_ENV=prod webpack --mode production"
},
- 在 Webpack 配置文件中获取环境变量
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
console.log('process.env.NODE_ENV=', process.env.NODE_ENV) // 打印环境变量
const config = {
entry: './src/index.js', // 打包入口地址
output: {
filename: 'bundle.js', // 输出文件名
path: path.join(__dirname, 'dist') // 输出文件目录
},
module: {
rules: [
{
test: /\.css$/, //匹配所有的 css 文件
use: 'css-loader' // use: 对应的 Loader 名称
}
]
},
plugins:[ // 配置插件
new HtmlWebpackPlugin({
template: './src/index.html'
})
]
}
module.exports = (env, argv) => {
console.log('argv.mode=',argv.mode) // 打印 mode(模式) 值
// 这里可以通过不同的模式修改 config 配置
return config;
}
# 1.5 启动 devServer
- 安装 webpack-dev-server [https://webpack.docschina.org/configuration/dev-server/#devserver]
npm intall webpack-dev-server@3.11.2 -D
注意:本文使用的 webpack-dev-server 版本是 3.11.2,当版本 version >= 4.0.0 时,需要使用 devServer.static 进行配置,不再有 devServer.contentBase 配置项。
- 配置本地服务
// webpack.config.js
const config = {
// ...
devServer: {
contentBase: path.resolve(__dirname, 'public'), // 静态文件目录
compress: true, //是否启动压缩 gzip
port: 8080, // 端口号
// open:true // 是否自动打开浏览器
},
// ...
}
module.exports = (env, argv) => {
console.log('argv.mode=',argv.mode) // 打印 mode(模式) 值
// 这里可以通过不同的模式修改 config 配置
return config;
}
为什么要配置 contentBase ? 因为 webpack 在进行打包的时候,对静态文件的处理,例如图片,都是直接 copy 到 dist 目录下面。但是对于本地开发来说,这个过程太费时,也没有必要,所以在设置 contentBase 之后,就直接到对应的静态目录下面去读取文件,而不需对文件做任何移动,节省了时间和性能开销。
webpack-dev-server v4.0.0+ 要求 node >= v12.13.0、webpack >= v4.37.0(但是我们推荐使用 webpack >= v5.0.0)和 webpack-cli >= v4.7.0。
devServer: {
static: {
directory: path.join(__dirname, 'public'),
},
compress: true, //是否启动压缩 gzip
port: 9000,
open: true, // 是否自动打开浏览器
},
- 启动本地服务
$ npm run dev
为了看到效果,我在 html 中添加了一段文字,并在 public 下面放入了一张图片 logo.png
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ITEM</title>
</head>
<body>
<p>ITEM</p>
</body>
</html>
public
└─ logo.png
打开地址 http://localhost:8080/
接着访问 http://localhost:8080/logo.png
# 1,6 引入 CSS
上面,我们在 Loader 里面讲到了使用 css-loader 来处理 css,但是单靠 css-loader 是没有办法将样式加载到页面上。这个时候,我们需要再安装一个 style-loader 来完成这个功能 style-loader 就是将处理好的 css 通过 style 标签的形式添加到页面上
安装 style-loader [文档地址]
npm install style-loader -D
配置 Loader
const config = {
// ...
module: {
rules: [
{
test: /\.css$/, //匹配所有的 css 文件
use: ['style-loader','css-loader']
}
]
},
// ...
}
⚠️注意: Loader 的执行顺序是固定从后往前,即按 css-loader --> style-loader 的顺序执行
引用样式文件
在入口文件 ./src/index.js 引入样式文件 ./src/main.css
// ./src/index.js
import './main.css';
const a = 'Hello ITEM'
console.log(a)
module.exports = a;
/* ./src/main.css */
body {
margin: 10px auto;
background: cyan;
max-width: 800px;
}
重启一下本地服务,访问 http://localhost:8080/
这样样式就起作用了,继续修改一下样式
body {
margin: 10px auto;
background: cyan;
max-width: 800px;
/* 新增 */
font-size: 46px;
font-weight: 600;
color: white;
position: fixed;
left: 50%;
transform: translateX(-50%);
}
保存之后,样式就自动修改完成了
style-loader 核心逻辑相当于:
const content = `${样式内容}`
const style = document.createElement('style');
style.innerHTML = content;
document.head.appendChild(style);
通过动态添加 style 标签的方式,将样式引入页面
# 1.7 CSS 兼容性
使用 postcss-loader (opens new window),自动添加 CSS3 部分属性的浏览器前缀 上面我们用到的 transform: translateX(-50%);,需要加上不同的浏览器前缀,这个我们可以使用 postcss-loader 来帮助我们完成
- 安装
npm install postcss postcss-loader postcss-preset-env -D
- 添加 postcss-loader 加载器
const config = {
// ...
module: {
rules: [
{
test: /\.css$/, //匹配所有的 css 文件
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}
]
},
// ...
}
- 创建 postcss 配置文件 postcss.config.js
// postcss.config.js
module.exports = {
plugins: [require('postcss-preset-env')]
}
- 创建 postcss-preset-env 配置文件 .browserslistrc
# 换行相当于 and
last 2 versions # 回退两个浏览器版本
> 0.5% # 全球超过0.5%人使用的浏览器,可以通过 caniuse.com 查看不同浏览器不同版本占有率
IE 10 # 兼容IE 10
# 1.8 引入 Less 或者 Sass
less 和 sass 同样是 Webpack 无法识别的,需要使用对应的 Loader 来处理一下
Less less-loader
Sass sass-loader node-sass 或 dart-sass
Less 处理相对比较简单,直接添加对应的 Loader 就好了 Sass 不光需要安装 sass-loader 还得搭配一个 node-sass,这里 node-sass 建议用淘宝镜像来安装,npm 安装成功的概率太小了 🤣 这里我们就使用 Sass 来做案例
- 安装
$ npm install sass-loader -D
# 淘宝镜像
$ npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
- 新建 ./src/sass.scss Sass 文件的后缀可以是 .scss(常用) 或者 .sass
$color: rgb(190, 23, 168);
body {
p {
background-color: $color;
width: 300px;
height: 300px;
display: block;
text-align: center;
line-height: 300px;
}
}
- 引入 Sass 文件
import './main.css';
import './sass.scss' // 引入 Sass 文件
const a = 'Hello ITEM'
console.log(a)
module.exports = a;
- 修改配置
const config = {
// ...
rules: [
{
test: /\.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件
use: [
'style-loader',
'css-loader',
'postcss-loader',
'sass-loader',
]
},
]
},
// ...
}
来看一下执行结果
# 1.9 分离样式文件
前面,我们都是依赖 style-loader 将样式通过 style 标签的形式添加到页面上 但是,更多时候,我们都希望可以通过 CSS 文件的形式引入到页面上
- 安装 mini-css-extract-plugin
$ npm install mini-css-extract-plugin -D
- 修改 webpack.config.js 配置
// ...
// 引入插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const config = {
// ...
module: {
rules: [
// ...
{
test: /\.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件
use: [
// 'style-loader',
MiniCssExtractPlugin.loader, // 添加 loader
'css-loader',
'postcss-loader',
'sass-loader',
]
},
]
},
// ...
plugins:[ // 配置插件
// ...
new MiniCssExtractPlugin({ // 添加插件
filename: '[name].[hash:8].css'
}),
// ...
]
}
// ...
- 查看打包结果
dist
├─ avatar.d4d42d52.png
├─ bundle.js
├─ index.html
├─ logo.56482c77.png
└─ main.3bcbae64.css # 生成的样式文件
# 1.10 图片文件
常用的处理图片文件的 Loader 包含:
- file-loader 解决图片引入问题,并将图片 copy 到指定目录,默认为 dist
- url-loader解依赖 file-loader,当图片小于 limit 值的时候,会将图片转为 base64 编码,大于 limit 值的时候依然是使用 file-loader 进行拷贝
- img-loader压缩图片
- 安装 file-loader
npm install file-loader -D
- 修改配置
const config = {
//...
module: {
rules: [
{
// ...
},
{
test: /\.(jpe?g|png|gif)$/i, // 匹配图片文件
use:[
'file-loader' // 使用 file-loader
]
}
]
},
// ...
}
- 引入图片
<!-- ./src/index.html -->
<!DOCTYPE html>
<html lang="en">
...
<body>
<p></p>
<div id="imgBox"></div>
</body>
</html>
样式文件中引入
/* ./src/sass.scss */
$color: rgb(190, 23, 168);
body {
p {
width: 300px;
height: 300px;
display: block;
text-align: center;
line-height: 300px;
background: url('../public/logo.png');
background-size: contain;
}
}
js 文件中引入
import './main.css';
import './sass.scss'
import logo from '../public/avatar.png'
const a = 'Hello ITEM'
console.log(a)
const img = new Image()
img.src = logo
document.getElementById('imgBox').appendChild(img)
启动服务,我们看一下效果
显示正常 ✌️
我们可以看到图片文件的名字都已经变了,并且带上了 hash 值,然后我看一下打包目录
dist
├─ 56482c77280b3c4ad2f083b727dfcbf9.png
├─ bundle.js
├─ d4d42d529da4b5120ac85878f6f69694.png
└─ index.html
dist 目录下面多了两个文件,这正是 file-loader 拷贝过来的
如果想要修改一下名称,可以加个配置
const config = {
//...
module: {
rules: [
{
// ...
},
{
test: /\.(jpe?g|png|gif)$/i,
use:[
{
loader: 'file-loader',
options: {
name: '[name][hash:8].[ext]'
}
}
]
}
]
},
// ...
}
再次打包看一下
dist
├─ avatard4d42d52.png
├─ bundle.js
├─ index.html
└─ logo56482c77.png
- 安装 url-loader
$ npm install url-loader -D
- 配置 url-loader 配置和 file-loader 类似,多了一个 limit 的配置
const config = {
//...
module: {
rules: [
{
// ...
},
{
test: /\.(jpe?g|png|gif)$/i,
use:[
{
loader: 'url-loader',
options: {
name: '[name][hash:8].[ext]',
// 文件小于 50k 会转换为 base64,大于则拷贝文件
limit: 50 * 1024
}
}
]
},
]
},
// ...
}
看一下,我们两个图片文件的体积
public
├─ avatar.png # 167kb
└─ logo.png # 43kb
我们打包看一下效果
很明显可以看到 logo.png 文件已经转为 base64 了👌
# 1.11 字体文件
首先,从 iconfont.cn (opens new window) 下载字体文件到本地
在项目中,新建 ./src/fonts 文件夹来存放字体文件 然后,引入到入口文件
// ./src/index.js
import './main.css';
import './sass.scss'
import logo from '../public/avatar.png'
// 引入字体图标文件
import './fonts/iconfont.css'
const a = 'Hello ITEM'
console.log(a)
const img = new Image()
img.src = logo
document.getElementById('imgBox').appendChild(img)
- 在 ./src/index.html 中使用
<!DOCTYPE html>
<html lang="en">
...
<body>
<p></p>
<!-- 使用字体图标文件 -->
<!-- 1)iconfont 对应 font-family 设置的值-->
<!-- 2)icon-member 图标 class 名称可以在 iconfont.cn 中查找-->
<i class="iconfont icon-member"></i>
<div id="imgBox"></div>
</body>
</html>
- 增加字体文件的配置
const config = {
// ...
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 匹配字体文件
use: [
{
loader: 'url-loader',
options: {
name: 'fonts/[name][hash:8].[ext]', // 体积大于 10KB 打包到 fonts 目录下
limit: 10 * 1024,
}
}
]
},
// ...
}
打包一下,看看效果
webpack5,内置了资源处理模块,file-loader 和 url-loader 都可以不用安装
# 1.12 资源模块的使用
webpack5 新增资源模块(asset module),允许使用资源文件(字体,图标等)而无需配置额外的 loader。
资源模块支持以下四个配置:
- asset/resource 将资源分割为单独的文件,并导出 url,类似之前的 file-loader 的功能.
- asset/inline 将资源导出为 dataUrl 的形式,类似之前的 url-loader 的小于 limit 参数时功能.
- asset/source 将资源导出为源码(source code). 类似的 raw-loader 功能.
- asset 会根据文件大小来选择使用哪种类型,当文件小于 8 KB(默认) 的时候会使用 asset/inline,否则会使用 asset/resource
贴一下修改后的完整代码
// ./src/index.js
const config = {
// ...
module: {
rules: [
// ...
{
test: /\.(jpe?g|png|gif)$/i,
type: 'asset',
generator: {
// 输出文件位置以及文件名
// [ext] 自带 "." 这个与 url-loader 配置不同
filename: "[name][hash:8][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 50 * 1024 //超过50kb不转 base64
}
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
type: 'asset',
generator: {
// 输出文件位置以及文件名
filename: "[name][hash:8][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 10 * 1024 // 超过100kb不转 base64
}
}
},
]
},
// ...
}
module.exports = (env, argv) => {
console.log('argv.mode=',argv.mode) // 打印 mode(模式) 值
// 这里可以通过不同的模式修改 config 配置
return config;
}
# 1.13 JS 兼容性(Babel)
在开发中我们想使用最新的 Js 特性,但是有些新特性的浏览器支持并不是很好,所以 Js 也需要做兼容处理,常见的就是将 ES6 语法转化为 ES5。
这里将登场的“全场最靓的仔” -- Babel
- 未配置 Babel 我们写点 ES6 的东西
// ./src/index.js
import './main.css';
import './sass.scss'
import logo from '../public/avatar.png'
import './fonts/iconfont.css'
// ...
class Author {
name = 'ITEM'
age = 18
email = 'lxp_work@163.com'
info = () => {
return {
name: this.name,
age: this.age,
email: this.email
}
}
}
module.exports = Author
为了方便看源码,我们把 mode 换成 development
接着执行打包命令
打包完成之后,打开 bundle.js 查看打包后的结果
虽然我们还是可以找打我们的代码,但是阅读起来比较不直观,我们先设置 mode 为 none,以最原始的形式打包,再看一下打包结果
打包后的代码变化不大,只是对图片地址做了替换,接下来看看配置 babel 后的打包结果会有什么变化
- 安装依赖
$ npm install babel-loader @babel/core @babel/preset-env -D
- babel-loader 使用 Babel 加载 ES2015+ 代码并将其转换为 ES5
- @babel/core Babel 编译的核心包
- @babel/preset-env Babel 编译的预设,可以理解为 Babel 插件的超集
- 配置 Babel 预设
// webpack.config.js
// ...
const config = {
entry: './src/index.js', // 打包入口地址
output: {
filename: 'bundle.js', // 输出文件名
path: path.join(__dirname, 'dist'), // 输出文件目录
},
module: {
rules: [
{
test: /\.js$/i,
use: [
{
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env'
],
}
}
]
},
// ...
]
},
//...
}
// ...
配置完成之后执行一下打包
刚才写的 ES6 class 写法 已经转换为了 ES5 的构造函数形式
尽然是做兼容处理,我们自然也可以指定到底要兼容哪些浏览器
为了避免 webpack.config.js 太臃肿,建议将 Babel 配置文件提取出来
根目录下新增 .babelrc.js
// ./babelrc.js
module.exports = {
presets: [
[
"@babel/preset-env",
{
// useBuiltIns: false 默认值,无视浏览器兼容配置,引入所有 polyfill
// useBuiltIns: entry 根据配置的浏览器兼容,引入浏览器不兼容的 polyfill
// useBuiltIns: usage 会根据配置的浏览器兼容,以及你代码中用到的 API 来进行 polyfill,实现了按需添加
useBuiltIns: "entry",
corejs: "3.9.1", // 是 core-js 版本号
targets: {
chrome: "58",
ie: "11",
},
},
],
],
};
好了,这里一个简单的 Babel 预设就配置完了
常见 Babel 预设还有: @babel/preset-flow @babel/preset-react @babel/preset-typescript
- 配置 Babel 插件 对于正在提案中,还未进入 ECMA 规范中的新特性,Babel 是无法进行处理的,必须要安装对应的插件,例如:
// ./ index.js
import './main.css';
import './sass.scss'
import logo from '../public/avatar.png'
import './fonts/iconfont.css'
const a = 'Hello ITEM'
console.log(a)
const img = new Image()
img.src = logo
document.getElementById('imgBox').appendChild(img)
// 新增装饰器的使用
@log('hi')
class MyClass { }
function log(text) {
return function(target) {
target.prototype.logger = () => `${text},${target.name}`
}
}
const test = new MyClass()
test.logger()
← 学习指南 SourceMap 配置选择 →