前言
本文的目的不是讲解Vite的源码,目的在于用通俗的语言,带大家从底层了解vite的实现原理。让大家在脑海中建立一个vite的大致流程。就像看美女一样,从头到脚先打量一番,至于内在,还是要大家自己去要微信号,慢慢接触。
发现美女
首先来打造一个学习环境,创建一个vite的应用,并启动以下命令
pnpm create vite my-vue-app --template vue
cd my-vue-app
pnpm install
pnpm run dev
大家来围观
在项目的package.json文件中,我们可以看到如下内容。
精致的脸蛋
找到 Vite 源码,路径packages\vite\bin\vite.js
我们可以看到此处会导入cli.js文件,我们进入该文件看看,相对路径为:packages\vite\src\node\cli.ts
cli文件内通过cac(是一个用于构建CLI应用程序的JavaScript库)读取命令行参数,然后执行 action 内的 createServer 函数。
S级身材
胸围--本地服务
createServer方法会返回ViteDevServer对象,内部会开启http服务,实现对浏览器请求的响应。
通过浏览器打开 http://127.0.0.1:5173/ 得到的内容是应用的index.html文件内容。如图:
依据ESM规范在浏览器script标签中的实现,对于<script type="module" src="/src/main.js"></script>内容,当script标签的type属性为module时,浏览器会将请求模块相应内容。
浏览器会发起HTTP请求,ViteDevServer处理http://127.0.0.1:5173/src/main.js 请求后,最终会返回以下内容,如图
我们看看main.js源码是什么样子的
两者对比一下:
import { createApp } from 'vue'变成了import { createApp } from '/node_modules/.vite/deps/vue.js?v=161dd9e1'
腰围--中间件
由于 import vue 这种模块引入方式,使用的是 Nodejs 特有的模块查找算法(到 node_modules 中取查找),浏览器无法使用,因此 Vite 会将 vue 替换成 /node_modules/.vite/deps/vue.js?v=b92a21b7,当浏览器解析到这行 import 语句时,会发送一个 /node_modules/.vite/deps/vue.js?v=b92a21b7 的请求。
所有请求都会在 ViteDevServer 的中间件处理,而这个请求,会被 static 中间件处理:用于访问静态文件,到会到该目录下,查找文件并返回。
模块的路径是在什么时候被替换的呢?
我们知道,浏览器处理 import 时,会发送一个请求到 Vite Dev Server,然后在中间件处理后,返回模块的内容。
预构建依赖的路径,正是在 transform 中间件处理过程中被替换的。这里再总结一下:
- 所有的类 JS 模块(包括 Vue),CSS 模块,都会在
transfrom中间件中进行处理 - 每个模块都会经过
resolveId、load、transform三个流程,这三个流程,可以通过 Vite 插件去扩展,可以在这三个过程中做一些特殊处理 - 模块
transform流程的作用:对代码进行转换,模块路径的替换,正是在这里被修改。
路径替换的插件伪代码:
import { parse } from 'es-module-lexer' // 实现一个 Vite 插件,在 transform 钩子中替换
export default function myPlugin() {
return {
// 实现 transform 钩子,code 为当前模块的代码,需要 return 修改过后的代码
transform(code) {
// 用 es-module-lexer 解析出模块使用的 import 和 export,里面的信息包含import 语句所在的行数,模块名所在的列数等信息
// 这些信息可以用来做字符串替换
let [imports, exports] = parseImports(source) // 根据 import 信息,执行路径替换
let resCode = /* 路径替换过后的代码 */
return resCode
}
}
}
实际上这部分得逻辑,是写在 importAnalysis 插件的,但该插件过于复杂,包含了非常多的功能,因此不会展开叙述,感兴趣的同学也可以自己去查看。
臀围--预构建
路径处理完成后,你会发现这个/node_modules/.vite/deps/vue.js?v=161dd9e1,deps目录下为啥有vue.js文件的。
这就要引出传说中的vite预构建了。
为什么要预构建
首先说明这个vue.js是预构建的产物,那为什么要预构建呢?直接读取node_modules下的vue文件不行吗?
原因有如下两点:
- 处理兼容性
- 减少请求,提高性能
看到上图最后标红的框框没,没错我们只有一个HTTP请求!!!
从项目入口遍历代码,结合AST深度扫描所有绝对路径的import xxx from 'xxx',然后标记所有依赖的第三方库,通过大名鼎鼎的esbuild,将依赖的第三方npm库,打包到各自的文件内!!!然后放到/node_modules/.vite/deps/目录下。
这就是/node_modules/.vite/deps/vue.js?v=161dd9e1文件的由来!斯国一~~
不相信的话,带大家看一下文件内容是否一致
浏览器请求的vue.js文件
项目中的文件
是不是一模一样!!
下面你应该会问怎么解析.vue后缀的文件呢?
这就要看vite的编译能力了。
点击浏览器查看一下.vue文件里面都是些什么
再看看原本长啥样
没错它已经被编译成了纯js文件了,只是后缀还是.vue,不信你看
Content-Type:在HTTP协议消息头中,使用Content-Type来表示请求和响应中的媒体类型信息。它用来告诉服务端如何处理请求的数据,以及告诉客户端(一般是浏览器)如何解析响应的数据,比如显示图片,解析并展示html等等。
vite偷偷的将.vue文件的 Content-Type 定义为了 application/javascript,也就是告诉浏览器,这个.vue文件其实就是个JS文件。按照JS文件去解析就可以了。
总结
高清无码:
最后祝大家新年快乐~
参考:
-
Vite官网
-
前端架构师--基础建设与架构设计思想