ToC
前置工作
下载源码
1# --depth=1 只保留最后一次 commit 记录,加快克隆下载的速度2git clone http://github.com/vitejs/vite.git --depth=13cd vite4pnpm i && code ./packages/vite
ViteJS 使用的方式是 vite
,那说明这个包一定包含了一个可执行的文件,在现有的 node npm package 体系中的体现则是它一定是有一个 bin
命令的,通过查看 package.json 可以看到入口是 bin/vite.js
,所以我们从它开始。
判断使用方式,收集 bin 参数
第一行的 #!/usr/bin/env node
表示从当前用户的可执行环境中查找 node 来作为这个文件的解析器,然后又引入了 import { performance } from 'node:perf_hooks'
依赖,performance
其实和浏览器中的 performance
类似,performance.now()
表示了从调用 node 开始到执行这一条语句中间间隔了多长时间,单位是毫秒。
下一行有一个 if 判断语句 if (!import.meta.url.includes('node_modules'))
,其中使用到了 import.meta.url
,这个属性保存的是当前模块在磁盘中的文件路径。需要注意的是,import.meta
属性只能在 es6 及以上的版本才可以使用,因为它只对 JavaScript Module 提供,所以你在 package.json
中也能找到一个 "type": "module"
属性,这是为了表示,vite 这个包是现代 ESModule 格式,同时 Vite 本身也是一个主打 "Next generation frontend tooling.(下一代前端工具。)"
的开发依赖,所以肯定还是会以现代浏览器的标准和兼容性来编写代码。回到代码中,会发现代码中判断了当前文件是否包含 node_modules
,这么做的目的是为了在使用 npx vite
的时候也能获取到 vite 对应的 sourcemap
以定位问题。同时这里还使用到了一个比较新的特性:`“top-level await”,即:在模块的顶级使用 await 语句,而非将其包含在 async 函数中。
global.__vite_start_time = performance.now()
则是将服务启动时间保存到全局环境中,在 node 中,global
等同于 window
,也是所有的开始。
1const debugIndex = process.argv.findIndex((arg) => /^(?:-d|--debug)$/.test(arg)) // 判断是否有设置 --debug || -d,并找到其对应的 index 位置2const filterIndex = process.argv.findIndex((arg) => /^(?:-f|--filter)$/.test(arg)) // 判断是否有设置 --filter || -f,并找到其对应的 index 位置3const profileIndex = process.argv.indexOf('--profile') // 判断是否有设置 --profile,并找到对应的索引位置
bin debug params
你可以自己新建一个 vite 的示例项目,或是直接使用源码中的 playground
文件夹中的示例来进行测试,都可以。vite 命令可以接收三个调试类的命令行参数,分别为:--debug
--filter
--profile
—debug/-d
1{2 "scripts": {3 "dev": "vite --debug"4 }5}
在项目的 package.json/scripts/dev
命令后添加 --debug
命令可以看到 vite 在此次运行时加载的所有配置项和服务启动后加载每个文件所需要的耗时。
1 vite:spa-fallback Rewriting GET / to /index.html +0ms2 vite:time 25.28ms /index.html +0ms3 vite:spa-fallback Rewriting GET / to /index.html +34ms4 vite:time 2.92ms /index.html +15ms5 vite:load 5.08ms [fs] /@vite/client +0ms6 vite:load 5.21ms [fs] /src/main.js +4ms7 vite:transform 4.10ms /src/main.js +0ms8 vite:time 11.45ms /src/main.js +35ms
日志的类型多且杂,各个文件和各个阶段的操作日志都会输出出来,非常不方便查看,但同时,它还可以指定一个过滤属性,用于在打印日志时只输出某一类型的日志:--debug load
:
1 vite:load 6.73ms [fs] /@vite/client +0ms2 vite:load 7.78ms [fs] /src/main.js +5ms3 vite:load 12.16ms [fs] /src/style.css +18ms4 vite:load 13.69ms [fs] /src/App.vue +1ms5 vite:load 0.18ms [plugin] /src/App.vue?vue&type=style&index=0&scoped=7a7a37b1&lang.css +23ms6 vite:load 0.79ms [plugin] plugin-vue:export-helper +1ms7 vite:load 37.98ms [plugin] /node_modules/.vite/deps/vue.js?v=eb57b1a7 +1ms8 vite:load 7.85ms [fs] /src/components/HelloWorld.vue +3ms9 vite:load 0.04ms [plugin] /src/components/HelloWorld.vue?vue&type=style&index=0&scoped=e17ea971&lang.css +13ms
还可以指定多个类型:--debug load,resolve,transform
—filter/-f
开启这个参数的前提是需要设置 debug
,因为这个参数的作用是屏蔽掉众多文件的加载信息,只保留参数后面的文件路径的加载信息,包括 debug 中的 vite 配置项也会一并屏蔽,只保留对应文件的加载/转换日志,如下:
1{2 "scripts": {3 "dev": "vite -d -f /src/style.css"4 }5}
1vite:resolve 0.19ms ./style.css -> /Volumes/code/sourcecode/test/vite-process-test/src/style.css +0ms2vite:resolve 0.10ms /src/style.css -> /Volumes/code/sourcecode/test/vite-process-test/src/style.css +1ms3vite:load 10.10ms [fs] /src/style.css +0ms4vite:transform 72.46ms /src/style.css +0ms5vite:time 79.41ms /src/style.css +0ms
不过这个参数只支持对单个文件的过滤,不能同时设置对多个文件加载信息的过滤,也就是不能以 /src/style.css,/src/main.js
这种形式设置。
—profile
这个参数是一个布尔类型的 flag,后面无需再接其他参数(即便增加了其他参数,也会在运行时被删除,比如:vite --profile test
,服务启动后,会将 --profile
和 test
一并删除,最终变成 vite
)。增加这个参数可以在服务启动以后,引入 NodeJS 的 inspector
内置模块,并通过 new inspector.Session()
创建一个 session
实例与 Node 的 V8 引擎建立连接,最后在启动服务的项目根路径下创建一个 vite-profile.cpuprofile
文件,里面保存着 Node 处理文件的耗时。这段代码的处理在这:
1if (profileIndex > 0) {2 console.log(process.argv)3 process.argv.splice(profileIndex, 1)4 const next = process.argv[profileIndex]5
6 console.log(next)7 if (next && !next.startsWith('-')) {8 process.argv.splice(profileIndex, 1)9 }10 const inspector = await import('node:inspector').then((r) => r.default)10 collapsed lines
11 console.log('inspector', inspector)12 const session = (global.__vite_profile_session = new inspector.Session())13 console.log('session', session)14 session.connect()15 session.post('Profiler.enable', () => {16 session.post('Profiler.start', start) // 在开始记录后启动服务器17 })18} else {19 start()20}
在完成以上工作后,就准备启动服务器了,启动服务器的代码在 ../dist/node/cli.js
文件中,bin/vite.js 文件中以 start()
函数的形式将这个文件动态加载了进来,而 ../dist/node/cli.js
文件对应的源文件路径是:packages/vite/src/node/cli.ts
,这个文件则是对 cli 参数的进一步解析和设置命令别名,因为这个文件没有被任何作用域或是函数包裹着,所以只要把文件引入就会被立即执行。
以上。