一位前端同学告诉我,他在使用 vite
构建项目后,并没有删除原来的 dist
目录。这让我感到困惑,因为根据我了解,每次使用 vite
进行构建时,都应该清理掉 dist
目录。为了搞清楚这个问题,我觉得阅读 vite
的源代码是非常必要的。
所以这篇文章的目的,也是记录自己学习 vite
源码的过程,如果有什么错误的地方欢迎评论指正。
从官方文档入手
你肯定和我一样,当看到这个问题的时候,第一时间想到的就是是不是配置的问题,直接打开 vite
相关的配置文档。
构建配置文档:cn.vitejs.dev/config/buil...
从 vite
的官方文档当中,可以找到下面相关的一项配置:
在 vite
的官网配置当中,可以看到 build.emptyOutDir
这个配置项,使用 vite
打包项目的时候 dist
目录默认情况是在 root
目录下,所以 vite
是会在每次执行打包的时候是会将旧的 dist 目录进行删除的。
在创建一个新项目然后打包验证之后,我还是决定自己查看一下源码,一来是加强自己阅读源码的能力,再就是想看看 vite
的源码是怎么写的。
阅读源码前的准备
当然你既然能够阅读这篇文章,说明
node/npm
这样的工具,你已经再熟悉不过了 。 这里假设你已经对 pnpm 和 monorepo 已经有了基础的了解,不过不了解也没关系,用到它们的时候我会进行说明和一些学习文档。
pnpm-workspce
学习文档: pnpm.io/zh/workspac...
vite 源码仓库地址:github.com/vitejs/vite
我们使用 git 将 vite 的源码 clone 到我们自己的电脑上,命令如下:
bash
git clone git@github.com:vitejs/vite.git
当然,如果你使用 git clone 的速度非常的慢,也可以选择直接下载压缩包或者其他方案。
克隆完毕,获得的 vite 源码如下所示:
通过整个工程的目录结构,我们可以得出 vite
是采用 pnpm workspace
的方式来管理整个项目的,接着查看 pnpm-workspace.yaml
文件。
yaml
packages:
- 'packages/*'
- 'playground/**'
从上面的 yaml 文件当中可以得到,vite 源码当中,分开了源码的包(即 packages/*
) 和示例源码(即 playgorund/**
)。
调试源码
经过上面的步骤,我们已经得到了 vite
的源码,接下来我们需要找到 vite
源码的入口。 首先先执行 pnpm install
,将整个工程所有的依赖都安装好。

注意这里安装依赖只能够使用 pnpm
,因为在 npm script
当中使用到了 script
的生命周期钩子对包管理器进行了相关限制,关于怎么限制使用包管理器,这个可以去查看 only-allow
这个包相关的文档,其原理也是非常简单,以后有机会再进行介绍。
构建 vite
通过项目的 package.josn
文件,项目里面有 dev
命令能够让让我们去调试 vite 的源码,直接在项目的根目录下执行 pnpm run dev
,能够在终端当中看到如下输出

到这里,我们已经成功的将 vite
的源码以开发的成功的跑了起来。
vite
源码当中的 pnpm run dev
命令解析

当我们执行 vite
源码工程当中的 dev
命令,其实执行的是下面这条命令。
bash
pnpm -r --parallel --filter='./packages/*' run dev
上面这条命令的意思是让 pnpm
并行在 ./packges/*
下面的各个子工程当中执行 dev
命令。
而 vite
的源码所处位置就是 packages/vite
这个工程当中。如果你使用过 node
开发过 cli
工具,那么对 vite
源码所在的工程目录结构一定非常熟悉。

通过 package.json
当中的 bin
字段,可以知道当我们执行在命令行执行 vite
命令的时候,这个命令真正执行的是 bin/vite.js
,也就是对应 vite 源码目录如下指示的文件,这里就是 vite
源码的入口位置了。

再回过头去看我们最刚开始执行的 pnpm run dev
命令对应的执行了 vite
这个工程下的 dev
命令。

可以看到当执行 dev
的时候,会先将之前打包构建出来的 vite
进行删除,然后再以监听文件变化的方式调用 rollup
对 vite
进行源码构建。
关于对 rollup 的使用,可以参考 rollup 官方文档进行学习。 rollup 官方文档:cn.rollupjs.org/
调试 vite 的源码
因为整个 vite 是采用 monorepo 的方式进行管理的,可以非常方便不同子工程相互之间的依赖和引用。

通过项目的依赖可以看到,项目依赖的 vite
就是当前工作空间的当中一个名叫 vite
的子工程,也就是 vite
的源码工程。
接下来我们切换到 playground
目录下,随便选择一个工程,这里我直接选第一个 alias
工程,执行 pnpm run dev
,可以在控制台下面的输出信息。

到这里已经同时运行了一个 vite 源码工程和一个示例项目,下面就是开始对 vite 的源码进行调试了。
回到上面 bin/vite.js
,大概在第 43 行的位置,可以看到定义了一个 start() 的函数,这个函数就是将打包好的 vite cli 相关的代码进行执行,其对应的打包之前的源码就是 src/node/clit.ts
。

带着最刚开始的问题,build 生成产物之前不会把原来的 dist 目录删掉吗?
当我们执行 vite build
的时候,其最终执行的是 src/node/cli.ts
大概第 272 行到 295 行代码相关的内容,也就是调用 build
函数。

我的跟着源码点到 build 函数当中去看,可以看到调用 prepareOutDir
这个函数,前面的判断我们可以自己增加 console.log
语句查看,当然官方文档当中已经告诉我们 options.write
这个配置默认情况下是 true
。

我们在 prepareOutDir 函数当中增加一些调试用的控制台打印代码。

然后我们在 playground/alias
这个工程当中,执行 pnpm run build

可以看到,我们添加的调试信息已经成功加到源码当中去了。
接着阅读下面的源码,就可以找到答案了,当 outDir
在 root
目录下,并且 emptOutDir
的值与 false
不相等(也就是 emptyOutDir !== false
条件成立)的时候,就会把原来的 dist
目录删掉。

所以 vite
官方文档上关于 build.emptyOutDir
默认值的描述,应该是 null
。
所以默认情况下,vite
是会在新的打包输出之前将旧的目录给清理掉的,至于那位前端同学使用的 vite 为啥会有这个问题,他说他使用的是 vite 2
,我猜大概了是 vite
的bug。
上面就是关于上手调试 vite 源码的内容啦,如果你跟着学到这里,你已经成功的学会了调试源码。