Webpack 4.0 学习笔记(一)
phao 在路上的前端开发

一、前言

在之前的开发中多多少少都接触过一下 Webpack,但每次都是通过百度找到别人的文章去修改配置,知其然而不知其所以然。后来在慕课网看到 Dell Lee 老师的一个 课程 ,抱着想要好好学学入个门的心态把这门课程给购买了。因为各种事情的耽搁,最近才回头来看这门课程,去进行学习与记录。

在 2020 年,Webpack 5.0 就正式发布了,听说改动很大,但手上有门 Webpack 4.0 的入门课程个人觉得还是有必要学习一下的,入门了 4.0 再去学 5.0 也不晚。因为该课程视频录制过程中用的各种 npm 依赖包版本相对于现在来说都有些旧,贸然用新版会出现一堆报错需要去调试,毕竟初衷是为了学习及了解 Webpack,就尽量采用与课程内一致的版本去学习。

环境

环境 版本
OS Windows
node 16.13.1
npm 6.14.16
webpack 4.26.0
webpack-cli 3.1.2

二、什么是 Webpack?

Webpack 是基于 NodeJS 开发的模块打包工具,本质上是由 node 去实现的。
它不仅能打包 js 文件,还可以打包 css 文件、png 图片等各种文件。
高版本的 Webpack 会利用新版本 NodeJS 的特性去提高打包速度。

三、使用场景

开发场景

一个最基础的项目结构

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!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>demo1</title>
</head>
<body>
<div id="app"></div>

<script src="header.js"></script>
<script src="content.js"></script>
<script src="footer.js"></script>
<script src="index.js"></script>
</body>
</html>
header.js
1
2
3
4
5
6
function Header() {
var header = document.createElement('div')
header.innerHTML = '这是网页头部内容'
var dom = document.getElementById('app')
dom.append(header)
}
content.js
1
2
3
4
5
6
function Content() {
var content = document.createElement('div')
content.innerHTML = '这是网页中间内容'
var dom = document.getElementById('app')
dom.append(content)
}
footer.js
1
2
3
4
5
6
function Footer() {
var footer = document.createElement('div')
footer.innerHTML = '这是网页底部内容'
var dom = document.getElementById('app')
dom.append(footer)
}
index.js
1
2
3
new Header()
new Content()
new Footer()

浏览器直接访问index.html

问题产生

用上述这种项目结构比较麻烦,index.html 里面需要引入多个 js 文件,但最后调用方法的地方则位于 index.js 文件,文件之间的依赖关系不明确,出现报错不能及时定位错误。这时候我们可以采用 ES Module 的形式把这些文件的代码做些调整,index.html 里面只引入 index.js,其他 js 文件导出相关模块,在 index.js 里面去引入需要使用的模块。

index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!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>demo1</title>
</head>
<body>
<div id="app"></div>

<script src="index.js"></script>
</body>
</html>
header.js 为例,content.js 及 footer.js 以此类推
1
2
3
4
5
6
7
8
9
function Header() {
var header = document.createElement('div')
header.innerHTML = '这是网页头部内容'
var dom = document.getElementById('app')
dom.append(header)
}

// ES Module 导出模块
export default Header
index.js
1
2
3
4
5
6
7
8
// ES Module 引入模块
import Header from './header.js'
import Content from './content.js'
import Footer from './footer.js'

new Header()
new Content()
new Footer()

但这样写的代码在浏览器是无法直接运行的,这时候我们需要就借助 Webpack 来帮助我们进行一个“翻译”(对这些模块进行打包,使其能在浏览器上运行)。

四、Webpack 的安装与使用

初始化项目

本文会以上面的 demo1 文件夹作为项目文件夹,也就是根路径。

首先,在项目根路径下,运行命令生成 package.json 文件。

加 -y 按照默认配置去生成 package.json
1
npm init -y

这时候在项目文件夹内就会多出一个 package.json 文件,它描述了这个 node 项目或者说 node 包的一些信息。

默认配置的 package.json
1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "demo1",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

对默认配置的 package.json 进行一些修改,为了方便讲解,我会在 package.json 里编写一些注释,实际在 json 文件中是不能这样去写注释的。

修改后的 package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"name": "demo1",
"version": "1.0.0",
// 配置 "private" 为 true,意思是该项目不会被发布到 npm 的线上仓库去。
"private": true,
"description": "",
// 去掉 "main": "index.js",因为当前项目不会被外部引用,没有必要向外部暴露一个 js 文件。
"scripts": {
// "scripts" 里用不上的命令也可以去掉。
},
"keywords": [],
// "author" 可以写上自己的名字。
"author": "phao",
// "license" 想开源可以写成 "MIT" 或 "ISC",这里保持默认即可。
"license": "ISC"
}

Webpack 的安装

(1)全局安装(不建议)

全局安装
1
npm install webpack webpack-cli -g
查看是否安装成功(能正常显示版本号就行)
1
webpack -v
全局卸载
1
npm uninstall webpack webpack-cli -g

当我们有多个项目使用了 Webpack,它们使用的 Webpack 的版本一般是不一致的,这时候使用全局安装的 Webpack 来运行部分项目会出现报错。

(2)局部安装(建议)

因为要与课程内使用的版本尽量一致,所以安装时会使用 @ 来指定相应的版本。还是在项目根路径下运行命令。

局部安装
1
npm install webpack@4.26.0 webpack-cli@3.1.2 -D
查看是否安装成功(能正常显示版本号就行)
1
npx webpack -v

当安装完成,我们的项目文件夹内会多出 node_modules 目录以及 package-lock.json 文件,node_modules 目录里面存放的就是我们安装的 npm 依赖包。与此同时,我们查看 package.json,会发现里面多出一些内容,devDependencies 内就是我们这个项目本地开发环境用到的依赖包。

package.json 新增内容
1
2
3
4
"devDependencies": {
"webpack": "^4.26.0",
"webpack-cli": "^3.1.2"
}

Webpack 的使用

首先我们要位于项目根路径下去使用命令,全局安装与局部安装在运行的命令有些许区别,我们后面的内容都会以局部安装去进行讲述。

全局安装后使用 Webpack(不使用)
1
webpack index.js
局部安装后使用 Webpack(使用)
1
npx webpack index.js

这个命令的意思是用 Webpack 去打包当前目录下的 index.js。

不带 npx 时 NodeJS 会去全局的模块目录中寻找 Webpack 这个依赖包。加了 npx 命令后会去当前项目的 node_modules 目录里寻找 Webpack 这个依赖包,找不到的话会进行安装。

运行命令后,会有一些警告,我们暂时忽略。这时候我们会发现项目文件夹内会多出一个 dist 目录,里面有一个 main.js 文件,这个文件里的代码就是 Webpack 把 index.js 及其引入的模块一起打包压缩生成的代码。

现在,我们把 index.html 里引入的 index.js 的路径修改一下。

index.html
1
<script src="./index.js"></script>

改为

index.html
1
<script src="./dist/main.js"></script>

然后把 index.html 放到浏览器内运行,就可以看到网页正常显示了。

五、配置文件 webpack.config.js

为了方便接下来的讲述以及让这个项目的结构看起来更规范,我们调整下文件及目录。在项目根路径下创建 src 目录,并把 index.js、header.js、content.js、footer.js 放入 src。

改为

随着 index.js 位置的更改,这时候的打包命令就变为了

1
npx webpack ./src/index.js

当在项目中运行打包命令的时候,Webpack 并不知道自己要如何打包,它会去找默认的配置文件 webpack.config.js,然后知道自己该怎么打包怎么输出。至于现在没创建该配置文件也可以进行打包是因为 Webpack 已经做了一些默认的配置处理。

我们先把打包生成的 dist 目录给删除了,在项目根路径下创建 webpack.config.js 文件,并进行相应配置的编写。

webpack.config.js
1
2
3
4
5
6
7
8
9
10
11
12
const path = require('path') // NodeJS 的一个核心模块

module.exports = {
entry: {
main: './src/index.js' // 要打包的文件入口
},
output: {
filename: 'bundle.js', // 打包后生成的文件名
path: path.resolve(__dirname, 'bundle') // 打包后的文件放到哪一个目录下,这里要写绝对路径。
// __dirname 这个 node 的变量,指的是 webpack.config.js 所在当前目录的路径。
}
}

因为在 webpack.config.js 中指定了打包的入口文件,所以这次的命令可以省略掉 index.js 的所在位置,依然是在项目根路径下运行打包命令。

1
npx webpack

这时候就会在项目根路径下生成一个 bundle 目录,里面有一个 bundle.js 文件就是打包后的文件内容。

在本文的打包过程中,控制台一直在报一个 WARNING,这个警告的出现是因为我们在打包的时候没有在配置里指定一个 mode,也就是打包的模式或者说环境。我们需要去 webpack.config.js 里新增一个配置项 mode,它能配置两个值,一个是开发环境的 ‘development’,一个是生产环境的 ‘production’。当配置为 ‘production’,打包后的代码会进行压缩,配置为 ‘development’ 则不会去进行压缩,这点我们可以通过配置不同的 mode 去进行尝试,运行打包命令后观察打包生成的文件。

下面我们来操作一下:

webpack.config.js
1
2
3
4
5
6
7
8
9
10
11
12
const path = require('path')

module.exports = {
mode: 'production', // 配置 mode,默认就是 'production'。
entry: {
main: './src/index.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'bundle')
}
}

运行打包命令

1
npx webpack

观察生成的打包文件 bundle.js 的内容,是被压缩了的。

把 webpack.config.js 的 mode 改为 ‘development’,运行打包命令,再次观察 bundle.js,发现是没有压缩处理的。

尝试完毕,我们把 mode 设回 ‘production’。

六、浅析 Webpack 打包输出内容

Hash 这次打包生成的唯一一个哈希值。
Version 这次打包使用的 Webpack 版本。
Time 和 Built at 指的是打包耗时以及完成打包时的时间。
Asset 下的文件指的是打包生成的文件。
Size 指的是生成文件的大小。
Chunks 指的生成文件对应的 id 值,当打包多个文件时每个文件都有一个 id 值,Chunks 里放的不仅是自身这个文件的 id 值,还包括与自身有关系的其他文件的 id 值。
Chunk Names 和 Chunks 差不多,只是存放的是文件的名称。这里的 Chunk Name 为 main,是因为 webpack.config.js 的 entry 里指定为 main。
Entrypoint main = bundle.js 指的是入口文件为 bundle.js。

七、补充与拓展

配置还原

当进行完上面的尝试,我们可以把一些配置改回常规的设置,例如 webpack.config.js 里 output 相关的配置,把生成的目录名改为 dist,生成的打包文件名改为 main.js。

webpack.config.js
1
2
3
4
5
6
7
8
9
...

module.exports = {
...
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
}
}

定义脚本命令

在我们常用的一些框架中,总有 npm run xxx 这样的命令,现在我们也来配置一个类似的命令。需要在 package.json 里面使用 scripts 来定义脚本命令。

package.json
1
2
3
4
5
6
7
{
...
"scripts": {
"bundle": "webpack"
},
...
}

至于这里为什么写 webpack 而不是写 npx webpack,是因为在这里写它会先到项目根路径下的 node_modules 目录里去找 Webpack。

把原先的 dist 以及 bundle 目录删除,在项目根路径下运行命令 npm run bundle,成功打包后把 index.html 放到浏览器里运行能够正常显示页面,整个流程就算完成了。

避免残留文件

建议每次重新打包的时候,都把原先打包生成 dist 之类的目录给删除,避免有时候改了打包生成的文件名而导致产生一些冗余文件。后面的学习中,我们可以通过 Webpack 去完成这个步骤。

webpack-cli

webpack-cli 是命令行工具,方便我们在命令行执行 webpack 这个命令。

模块规范支持

Webpack 对其他模块的引入以及导出方式也能识别,例如 CommonJS 模块规范。

1
2
3
4
5
6
header.js 导出:
function Header () {}
module.exports = Header

index.js 引入:
var Header = require('./header.js')

但 webpack.config.js 这个文件本身只支持 CommonJS 规范书写,配置对象需要通过 module.exports 导出去。这是因为这个配置文件,包括 webpack 这个命令,都是在 node 下被启动的,实际上它就是一段 node 代码,node 里面当然就只能使用 CommonJS 规范了。而打包的代码,是被 Webpack 做转化的,所以支持多种模块规范来编写。

指定配置文件

当我们执行打包命令不想用 webpack.config.js 作为配置文件,想指定 test.js 作为配置文件来进行打包。

1
npx webpack --config test.js

官方文档

学习完以上内容,可以前往 Webpack 4.0 官网 阅读相关中文文档。

建议阅读的内容为:
(1)文档 > 概念,页面左侧的 模块(module)。
(2)文档 > API,页面左侧的 Modules。
(3)文档 > 指南,页面左侧的 起步。

英语比较好的小伙伴可以直接阅读 Webpack 4.0 官网 的英文文档。

八、总结

不得不说,想要写好博客还真的不是一件容易的事情。本篇文章最后的代码我会上传到 码云(gitee.com/phao97)上,只需要把项目 clone 到本地,进入项目根路径下(本文中是 demo1 文件夹内),运行 npm ci 安装依赖包即可。至于为什么用 npm ci,而不是 npm i,感兴趣的小伙伴百度了解下区别即可。

通过本篇文章的学习,我们应该能够对 Webpack 相关内容有一个基础的认识。Webpack 是一个模块打包工具,模块可以理解为我们编写代码时引入的各种资源文件,在项目的开发中我们可以通过 webpack.config.js 去配置属于自己的项目打包结构。它的功能非常强大,让我们在后面的学习中慢慢去熟悉它吧~

如果觉得本篇文章对你有帮助,不妨点个赞或者给相关的 Git 仓库一个 Star,你的鼓励是我持续更新的动力!

  • 本文标题:Webpack 4.0 学习笔记(一)
  • 本文作者:phao
  • 创建时间:2022-04-26 16:53:28
  • 本文链接:http://phaode.cn/2022/04/26/Webpack-4-0-学习笔记(一)/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!