上一篇的最后提出了对模块化打包方案或工具的诉求:
- 能够将散落的模块打包到一起;
- 能够编译代码中的新特性;
- 能够支持不同种类的前端资源模块。
目前,前端领域有一些工具能够很好的满足以上这 3 个需求,其中最为主流的就是 Webpack、Parcel 和 Rollup。
如何使用 Webpack 实现模块化打包?
- Webpack 作为一个模块打包工具,本身就可以解决模块化代码打包的问题,将零散的 JavaScript 代码打包到一个 JS 文件中。
- 对于有环境兼容问题的代码,Webpack 可以在打包过程中通过
Loader
机制对其实现编译转换,然后再进行打包。- 对于不同类型的前端模块类型,Webpack 支持在 JavaScript 中以模块化的方式载入任意类型的资源文件,例如,我们可以通过 Webpack 实现在 JavaScript 中加载 CSS 文件,被加载的 CSS 文件将会通过 style 标签的方式工作。
- Webpack 还具备代码拆分的能力,它能够将应用中所有的模块按照我们的需要分块打包。
🌰
└─ webpack_test
├── src
│ ├── heading.js
│ └── index.js
└── index.html
// ./src/heading.js
export default () => {
const element = document.createElement('h2')
element.textContent = 'Hello webpack'
element.addEventListener('click', () => alert('Hello webpack'))
return element
}
// ./src/index.js
import createHeading from './heading.js'
const heading = createHeading()
document.body.append(heading)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webpack - 快速上手</title>
</head>
<body>
<script type="module" src="src/index.js"></script>
</body>
</html>
1. 安装 Webpack 的核心模块以及它的 CLI 模块
npm init --yes #先初始化一个package.json文件,用来管理npm依赖版本
npm i webpack webpack-cli --save-dev
webpack
是 Webpack 的核心模块,webpack-cli
是 Webpack 的 CLI 程序,用来在命令行中调用 Webpack。
安装完成之后,webpack-cli
所提供的 CLI 程序就会出现在 node_modules/.bin
目录当中,我们可以通过 npx
快速找到 CLI 并运行它,具体操作如下:
npx webpack --version
# v4.43.0
npx
是 npm 5.2 以后新增的一个命令,可以用来更方便的执行远程模块或者项目 node_modules 中的 CLI 程序。
2. 运行 Webpack
有了 Webpack 后,就可以直接运行 webpack 命令来打包 JS 模块代码:
npx webpack
这个命令在执行的过程中,Webpack 会自动从 src/index.js
文件开始打包,然后根据代码中的模块导入操作,自动将所有用到的模块代码打包到一起。
完成之后,控制台会提示:顺着 index.js 有两个 JS 文件被打包到了一起。与之对应的就是项目的根目录下多出了一个 dist
目录,我们的打包结果就存放在这个目录下的 main.js
文件中。
修改 inde.html
中的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webpack - 快速上手</title>
</head>
<body>
<script src="dist/main.js"></script>
</body>
</html>
成功使用。
对于 Webpack 最基本的使用,总结下来就是:先安装 webpack 相关的 npm 包,然后使用 webpack-cli 所提供的命令行工具进行打包。
配置 Webpack 的打包过程
🌰:需要它的打包入口是 src/main.js
。
那就需要通过配置文件的方式修改 Webpack 的默认配置,在项目的根目录下添加一个 webpack.config.js
,具体结构如下:
└─ 02-configuation
├── src
│ ├── heading.js
│ └── main.js
├── index.html
├── package.json
+ └── webpack.config.js ···················· Webpack 配置文件
webpack.config.js 是一个运行在 Node.js 环境中的 JS 文件,也就是说我们需要按照 CommonJS 的方式编写代码,这个文件可以导出一个对象,我们可以通过所导出对象的属性完成相应的配置选项。
添加一个 entry
属性,这个属性的作用就是指定 Webpack 打包的入口文件路径。我们将其设置为 src/main.js
,具体代码如下所示:
// ./webpack.config.js
module.exports = {
entry: './src/main.js'
}
配置完成之后,回到命令行终端重新运行打包命令,此时 Webpack 就会从 src/main.js
文件开始打包。
还可以通过 output
属性设置输出文件的位置。
output
属性的值必须是一个对象,通过这个对象的filename
指定输出文件的文件名称,path
指定输出的目录。
具体代码如下所示:
// ./webpack.config.js
const path = require('path')
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'output')
}
}
Webpack 工作模式
Webpack 4 新增了一个工作模式的用法,这种用法大大简化了 Webpack 配置的复杂程度。你可以把它理解为针对不同环境的几组预设配置:
production
模式下,启动内置优化插件,自动优化打包结果,打包速度偏慢;development
模式下,自动优化打包速度,添加一些调试过程中的辅助插件;none
模式下,运行最原始的打包,不做任何额外处理。
修改 Webpack 工作模式的方式有两种:
- 通过
CLI --mode
参数传入; - 通过配置文件设置
mode
属性。
如何通过 Loader 实现特殊资源加载?
🌰:通过 Webpack 打包项目中的一个 CSS 文件。
$ npm install css-loader --save-dev
// ./src/webpack.config.js
module.exports = {
entry: './src/main.css',
output: {
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.css$/, // 根据打包过程中所遇到文件路径匹配是否使用这个 loader
use: 'css-loader' // 指定具体的 loader
}
]
}
}
在配置对象的 module
属性中添加一个 rules
数组。这个数组就是我们针对资源模块的加载规则配置,其中的每个规则对象都需要设置两个属性:
test
:它是一个正则表达式,用来匹配打包过程中所遇到文件路径,这里我们是以 .css 结尾;use
:它用来指定匹配到的文件需要使用的 loader,这里用到的是 css-loader。
配置完成过后,回到命令行终端重新运行打包命令,打包过程就不会再出现错误了,因为这时 CSS 文件会交给 css-loader 处理过后再由 Webpack 打包。
不过此时 main.css 模块并没有工作。只需要再额外添加一个 style-loader
,样式就可以正常工作了。
$ npm install style-loader --save-dev
module: {
rules: [
{
test: /\.css$/,
// 对同一个模块使用多个 loader,注意顺序
use: [
'style-loader',
'css-loader'
]
}
]
}
如何利用插件机制横向扩展 Webpack 的构建能力
clean-webpack-plugin
自动清除输出目录的插件
$ npm install clean-webpack-plugin --save-dev
安装过后,在 Webpack 的配置文件中导入 clean-webpack-plugin
插件,这个插件模块导出了一个叫作 CleanWebpackPlugin
的成员,我们先把它解构出来,具体代码如下:
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
回到配置对象中,添加一个 plugins
属性,这个属性就是专门用来配置插件的地方,它是一个数组,添加一个插件就是在这个数组中添加一个元素。
绝大多数插件模块导出的都是一个类型,CleanWebpackPlugin
也不例外,使用它,就是通过这个类型创建一个实例,放入 plugins 数组中,具体代码如下:
// ./webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new CleanWebpackPlugin()
]
}
测试 clean-webpack-plugin 插件的效果:回到命令行终端,再次运行 Webpack 打包,此时之前的打包结果就不会存在了,dist 目录中存放的就都是我们本次打包的结果。
html-webpack-plugin
用于生成HTML的插件
$ npm install html-webpack-plugin --save-dev
安装完成过后,回到配置文件,载入这个模块。
html-webpack-plugin 插件默认导出的就是插件类型,不需要再解构内部成员。
具体如下:
const HtmlWebpackPlugin = require('html-webpack-plugin')
有了这个类型过后,回到配置对象的 plugins 属性中,同样需要添加一下这个类型的实例对象,完成这个插件的使用,具体配置代码如下:
// ./webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin()
]
}
copy-webpack-plugin
用于复制文件的插件
安装 copy-webpack-plugin
插件,安装完成后,回到配置文件中,导入这个插件类型。然后同样在 plugins
属性中添加一个这个类型的实例,具体代码如下:
// ./webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Webpack Plugin Sample',
template: './src/index.html'
}),
new CopyWebpackPlugin([
'public' // 需要拷贝的目录或者路径通配符
])
]
}
这个插件类型的构造函数需要传入一个字符串数组,用于指定需要拷贝的文件路径。它可以是一个通配符,也可以是一个目录或者文件的相对路径。我们这里传入的是 public 目录,表示将这个目录下所有文件全部拷贝到输出目录中。还可以在这个数组中继续添加其它路径,这样它在工作时可以同时拷贝。
配置完成以后回到命令行终端,再次运行 Webpack,此时 public 目录下的文件就会同时拷贝到输出目录中。
名称 | 链接 |
---|---|
file-loader | https://webpack.js.org/loaders/file-loader |
url-loader | https://webpack.js.org/loaders/url-loader |
babel-loader | https://webpack.js.org/loaders/babel-loader |
style-loader | https://webpack.js.org/loaders/style-loader |
css-loader | https://webpack.js.org/loaders/css-loader |
sass-loader | https://webpack.js.org/loaders/sass-loader |
postcss-loader | https://webpack.js.org/loaders/postcss-loader |
eslint-loader | https://github.com/webpack-contrib/eslint-loader |
vue-loader | https://github.com/vuejs/vue-loader |
Webpack 为每一个工作环节都预留了合适的钩子,我们在扩展时只需要找到合适的时机去做合适的事情就可以了。
探索 Webpack 运行机制与核心工作原理
工作过程
Webpack 启动后,会根据配置,找到项目中的某个指定文件(一般这个文件都会是一个 JS 文件)作为入口。然后顺着入口文件中的代码,根据代码中出现的 import(ES Modules)或者是 require(CommonJS)之类的语句,解析推断出来这个文件所依赖的资源模块,然后再分别去解析每个资源模块的依赖,周而复始,最后形成整个项目中所有用到的文件之间的依赖关系树。
有了这个依赖关系树过后, Webpack 会遍历(递归)这个依赖树,找到每个节点对应的资源文件,然后根据配置选项中的 Loader 配置,交给对应的 Loader 去加载这个模块,最后将加载的结果放入 bundle.js(打包结果)中,从而实现整个项目的打包。
工作原理
Webpack 核心工作过程中的关键环节,明确“查阅”源码的思路:
- Webpack CLI 启动打包流程;
- 载入 Webpack 核心模块,创建 Compiler 对象;
- 使用 Compiler 对象开始编译整个项目;
- 从入口文件开始,解析模块依赖,形成依赖关系树;
- 递归依赖树,将每个模块交给对应的 Loader 处理;
- 合并 Loader 处理完的结果,将打包结果输出到 dist 目录。
🔗 相关链接:
- 上一篇:Webpack(一):背景介绍
- 下一篇:Webpack(三):高阶内容