构建工具和脚手架
刀刀
4/10/2025
0 字
0 分钟
构建工具
前面讲到的都是代码层面的转换,还有工程级别的转换,例如一个工程项目通过打包后,最终 dist
文件夹内是转换好后的代码文件。
为什么需要工程转换?因为开发和维护的代码需要简单的,易开发的;而运行时需要的代码需要简洁的,易维护的。开发的工程结构和运行时的工程结构往往不同,因此需要一个构建工具来完成这个转换。
使用构建工具需要思考三个前提:
- 哪种工程更适合开发和维护
- 哪种工程更适合运行时
- 如何转换(打包)
因为这三个问题不一样,着力点不一样,因此造成很多构建工具都有各自的区别,如 Webpack、Rollup、Esbuild、Vite 等等。本质的差别也就是上面三点的不同。
学习上重点放在 Webpack、Rollup、Esbuild 上。
Webpack
对于 Webpack 来说,对于三方三个问题,它是这么认为的:
哪种工程更适合开发和维护
一切皆为模块,哪怕是一个音频、一个图片都是一个模块。
哪种工程更适合运行时
传统工程。
如何转换(打包)
通过入口文件找依赖关系,导入谁依赖谁,然后深度遍历,最终找到全部的依赖关系。最终合并,所有 CSS 文件合并在一起,所有JavaScript 文件合并在一起,资源文件如图片音频放在一起,该转换的文件转换,最终生成一个
dist
文件夹。
入口依赖分析
以 Vue 项目为例,Webpack 会从 main.js
着手,然后把整个文件的代码转为 AST
抽象语法树,通过这个抽象语法树找到导入语句,这个语句同时支持 ESM
和 CommonJS
两种导入方式,因此既支持 import
,也支持 require
。通过导入语句就找到对应的依赖关系。
通过模块查找规则找到对应的依赖文件,如 import
导入的是一个文件夹 ./core
,那么默认查找该文件夹下的 index.js
文件;再如 import
导入的是一个文件夹 jquery
但是不以 ./
和 ../
开头,那么用 Node 的规则查找,在当前文件目录有没有 node_modules
,如果没有再返回上一级查找,直到找到 node_modules
,再找到对应的模块。
如果使用了别名如 @/assets
,那么会根据配置的别名查找。
开发服务器
通过 webpack serve
命令就能启动一个开发服务器,其本质是使用了 Node 的服务。
主要流程为: Webpack 内置了一个 webpack-dev-server
包,这个包又依赖了 express
,会启动一个开发服务器,然后在内存进行打包,在内存中形成打包结果,并在终端给出一个地址,浏览器访问这个地址就会访问开发服务器,开发服务器就会响应浏览器,给出打包结果的页面。
这个服务会监听文件的变化,当文件发生变化时,会重新打包,并告知浏览器刷新页面。浏览器会重新请求,开发服务器会把最新的打包结果返回给浏览器。这里面本质是用了 WebSocket。
xximg
文件指纹
每个文件都有一个指纹(即哈希值),如果文件发生变化,打包后文件指纹会变化。如果没有文件指纹,服务器给浏览器响应资源文件时,都会让浏览器缓存起来避免重复请求,但是如果文件发生变化,浏览器会使用缓存的文件,导致页面显示的不是最新的内容。
xxximg
浏览器会根据文件指纹缓存文件,如果文件指纹没有变化,浏览器会使用缓存文件;如果文件指纹发生变化,浏览器会重新请求,开发服务器会返回最新的文件。
xxximg
css modules
为了避免类名冲突,打包后的 CSS 文件会生成一个唯一的类名,这个类名是通过哈希值生成的。 Webpack 打包后的类名,与源码的类名是一一对应的。
源码地图
Webpack 打包后的代码是经过压缩的,为了方便调试,Webpack 会生成一个源码地图文件,这个文件是一个映射文件,记录了打包后的代码和源码的对应关系。浏览器在调试时,会使用这个映射文件,把打包后的代码映射回源码,方便调试。开启方式为:
module.exports = {
devtool: 'source-map'
}
脚手架
脚手架是构建工具的配套工具,用于快速生成项目模板。它主要解决两个问题:
- 提供界面交互
- 生成工程模板