跳转到内容

构建工具和脚手架

刀刀

4/10/2025

0 字

0 分钟

构建工具

前面讲到的都是代码层面的转换,还有工程级别的转换,例如一个工程项目通过打包后,最终 dist 文件夹内是转换好后的代码文件。

为什么需要工程转换?因为开发和维护的代码需要简单的,易开发的;而运行时需要的代码需要简洁的,易维护的。开发的工程结构和运行时的工程结构往往不同,因此需要一个构建工具来完成这个转换。

使用构建工具需要思考三个前提:

  1. 哪种工程更适合开发和维护
  2. 哪种工程更适合运行时
  3. 如何转换(打包)

因为这三个问题不一样,着力点不一样,因此造成很多构建工具都有各自的区别,如 WebpackRollupEsbuildVite 等等。本质的差别也就是上面三点的不同。

学习上重点放在 WebpackRollupEsbuild 上。

Webpack

对于 Webpack 来说,对于三方三个问题,它是这么认为的:

  1. 哪种工程更适合开发和维护

    一切皆为模块,哪怕是一个音频、一个图片都是一个模块。

  2. 哪种工程更适合运行时

    传统工程。

  3. 如何转换(打包)

    通过入口文件找依赖关系,导入谁依赖谁,然后深度遍历,最终找到全部的依赖关系。最终合并,所有 CSS 文件合并在一起,所有JavaScript 文件合并在一起,资源文件如图片音频放在一起,该转换的文件转换,最终生成一个 dist 文件夹。

入口依赖分析

Vue 项目为例,Webpack 会从 main.js 着手,然后把整个文件的代码转为 AST 抽象语法树,通过这个抽象语法树找到导入语句,这个语句同时支持 ESMCommonJS 两种导入方式,因此既支持 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 会生成一个源码地图文件,这个文件是一个映射文件,记录了打包后的代码和源码的对应关系。浏览器在调试时,会使用这个映射文件,把打包后的代码映射回源码,方便调试。

开启方式为:

js
module.exports = {
  devtool: 'source-map'
}

脚手架

脚手架是构建工具的配套工具,用于快速生成项目模板。它主要解决两个问题:

  1. 提供界面交互
  2. 生成工程模板