初始化项目
首先新建一个项目然后初始化。
shell
npm init -y
pnpm add webpack webpack-cli -D
创建一个 src/main.js 文件,随便写点啥 在根目录创建 build/webpack.common.js 文件
js
const path = require("path");
module.exports = {
entry: path.resolve(__dirname, "../src/main.js"),
output: {
filename: "[name].bundle.js",
path: path.resolve(__dirname, "../dist"),
clean: true,
},
}
在 package.json 加上打包命令
json
"scripts": {
"build": "webpack --config build/webpack.common.js"
}
通过执行 pnpm run build
进行打包,执行后会生成一个 dist
目录,里面就是我们打包的产物。
打包时控制台会报
The 'mode' option has not been set
的警告,意思就是我们需要设置一个 mode 它可以是development
开发模式、production
生产模式、none
无模式。
创建 HTML
打包好的产物我们希望能通过html在浏览器中打开看效果,需要通过 html-webpack-plugin
插件实现
shell
pnpm add -D html-webpack-plugin
在配置文件中写入
js
const htmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
//...
plugins: [
new htmlWebpackPlugin({
// 设置模板,需要在根目录创建一个 index.html 文件
template: path.resolve(__dirname, "../index.html")
})
]
}
plugins 就是我们声明插件来对 webpack 的功能进行扩展的地方。这时候再打包,发现产物目录下有一个 html 文件,并且通过 script 引入了我们打包的 js 文件。
打包时显示进度
shell
pnpm add progress-bar-webpack-plugin -D
pnpm add chalk@4.1.2 -D
这里的 chalk 最新版本(5.3.0)使用 esm 引入,我们使用 4.1.2 版本
使用
js
const chalk = require("chalk");
const ProgressBarPlugin = require("progress-bar-webpack-plugin");
module.exports = {
// ...
plugins: [
// ...
new ProgressBarPlugin({
format: ` :msg [:bar] ${chalk.green.bold(":percent")} (:elapsed s)`,
}),
]
}
再进行打包就能看到进度条了,另外可以通过 chalk 来修改进度条的颜色等。
处理样式
我们定义一个 main.css 文件,在 main.js 引入,进行打包时会发现控制台报错,并提示我们需要用 loader 来处理这个类型的文件。
处理 css 类型文件需要用到以下两个 loader。
shell
pnpm add -D style-loader css-loader
在配置文件中声明使用这两个 loader 来处理 css 类型文件。
js
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
}
]
}
module 是我们用来定义某种资源通过指定的 loader 来处理,如上代码我们通过 "style-loader"
和 "css-loader"
来处理后缀名为 css
的文件,"css-loader"
做的是解析 css 文件,翻译成 JavaScript 代码,使得 webpack 能够如同处理 JS 代码一样解析 CSS,"style-loader"
做的是将解析出来的文件通过 style
标签插入到页面中。
另外 loader 的书写顺序也是有要求的,一般情况下,会按照由右到左,由上到下的顺序执行。所以上面代码才是先解析再插入到页面。
通过打包的产物发现我们的 css 已经被打包进 js 文件中,下面我们把 css 代码抽离出来。安装MiniCssExtractPlugin插件
shell
pnpm add -D mini-css-extract-plugin
然后进行配置
js
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"]
}
]
},
plugins: [
// ...
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../index.html"),
}),
new MiniCssExtractPlugin()
]
}
这时候再打包可以看到产物中多一个 css 文件,这样我们就把 css 文件抽离出来了
再看产物中的 index.html,多了一行 link 标签,所以这个插件的作用就是把 css 文件抽离并通过 link 标签的形式插入到页面中,这也是为什么在上面的配置中我们去掉了 style-loader。
html
<link href="main.css" rel="stylesheet">
使用 MiniCssExtractPlugin 插件的好处在于,避免了 js 和 css 只能同步加载,对性能有影响,并且其中任意一个更新都会导致缓存失效。另外该插件必须和上面写到的 HTMLWebpackPlugin 插件一起使用(不然连 html 文件都没有怎么插入到页面)。
预处理器
我们以 less 为例
shell
pnpm add -D less less-loader
然后新建一个 less 文件,随便写点样式,在打包入口文件导入,接着进行对 less 的配置
js
module: {
rules: [
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"]
}
]
}
无非在处理 css 文件的基础上,多了一个 less-loader ,这个 loader 做的就是把 less 翻译成 css。
postcss
安装 postcss 和 postcss-loader
shell
pnpm add -D postcss postcss-loader
自动添加前缀
postcss 有强大的插件生态来处理各种问题,常用的如 autoprefixer,他能根据配置的浏览器兼容性,检测出需要添加前缀的 css 属性,构建的时候进行添加。
shell
pnpm add -D autoprefixer
对 postcss 配置
css
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
// 添加 autoprefixer 插件
plugins: [require("autoprefixer")],
},
},
},
"less-loader"
]
}
在 less 文件中写入
less
::placeholder {
color: gray;
}
打包后可以看到产物已经增加了前缀
关于系统支持的浏览器可以通过 package.json 的 browserslist 或者
.browserslistrc
文件进行配置另外postcss-preset-env也可以做同样的事情,甚至更强大,他还能将现代 CSS 转换成大多数浏览器都能理解的东西。
处理 js
webpack 默认是支持处理 js 文件的,但在一些情况下,js 代码会存在兼容性的问题,在低版本的浏览器无法运行的情况,所以我们需要对这部分代码进行处理。这里用到的是 babel
对代码进行转译。
shell
pnpm add -D @babel/core @babel/preset-env babel-loader
@babel/core:babel核心库
@babel/preset-env 根据目标环境转译 JavaScript 的预设
babel-loader webpack 中的一个 loader,将 babel 集成到 webpack 构建过程中
配置
js
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
]
}
在入口文件我们声明一个箭头函数并执行,然后分别看使用 babel-loader 进行语法降级和 不使用的效果,可以看到用 babel 后,箭头函数被转译成如下的形式。
js
var fun = function () {/*...*/}
处理 ts
使用 ts-loader
shell
pnpm add -D typescript ts-loader
配置
js
{
test: /\.ts$/,
use: ["ts-loader"]
},
还需要创建 ts 的配置文件 tsconfig.json
json
{
"compilerOptions": {
"noImplicitAny": true,
"moduleResolution": "node"
}
}
这时候就可以执行构建了
使用 babel
shell
pnpm add -D @babel/preset-typescript
配置
js
{
test: /\.ts$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-typescript'],
},
},
],
},
两者区别
使用 @babel/preset-typescript
只是进行了代码转换,而 ts-loader
还会对代码进行类型检查。例如我们将 number 赋值给一个 string 类型。ts-loader
在构建时会报错。
处理资源
file-loader
安装
shell
pnpm add -D file-loader
file-loader 将资源重命名,在代码中插入 url 地址。
url-loader
安装
shell
pnpm add -D url-loader
配置
js
{
test: /\.(png|jpg)$/,
use: [
{
loader: "url-loader",
options: {
limit: 1024
}
}
]
},
url-loader 会对比资源大小和配置的限制大小,如果没超出限制就转为 Base64 编码,超出的话就同 file-loader 一致。
raw-loader
不转译,直接将文件复制到产物。
Webpack5 通过 module.rules.type
指定资源类型。
asset/resource
对标 file-loader
asset
对标 url-loader
,module.rules.parser.dataUrlCondition
用于限定文件大小阈值
asset/source
对标 raw-loader
/ asset/inline
区分开发环境和生产环境
我们上面写到的 webpack.common.js 可以作为一个基础的配置,在开发环境和生产环境中配置也是有差异的,所以我们还需要分别去配置。
创建 build/webpack.dev.js
和 build/webpack.prod.js
因为开发环境和生产环境都基于基础配置,我们需要通过 webpack.merge 将他们连接起来。
shell
pnpm add -D webpack-merge
webpack-dev-server
一般开发环境我们希望项目构建后能直接在浏览器运行,这一点通过 webpack-dev-server
来实现
shell
pnpm add -D webpack-dev-server
配置
js
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");
const path = require("path");
module.exports = merge(common, {
mode: "development",
devServer: {
hot: true,
open: true,
port: 3000,
},
});
重写启动命令
json
{
"scripts": {
"dev": "webpack serve --config build/webpack.dev.js",
}
}
执行 pnpm run dev
将会在 3000 端口启动。