vite 原理过程简析

767 阅读3分钟

什么是vite

vite 是一个基于 Vue3单文件组件的非打包开发服务器,它做到了本地快速开发启动, 实现按需编译,不再等待整个应用编译完成。

法语Vite(轻量,轻快)

面向现代浏览器,基于原生模块系统 ESModule 实现。webpack的开发环境很慢(开发时需要进行编译放到内存中)

实现原理

vite高度依赖module script特性

过程如下:

  • 执行package.json的bin字段对应的js
  • createServer
  • 初始化koa、watcher、resolver
  • 在 koa 中间件里获取请求 body
  • 通过koa middlewares的方式使用一堆plugins
  • 对js文件进行拦截,包括*.vue文件中的<template>和<script>
  • 通过 es-module-lexer 解析资源 ast 拿到 import 的内容
  • 判断 import 的资源是否是 npm 模块
  • 返回处理后的资源路径:"vue" => "/@modules/vue"
  • 预优化用户项目里dependencies依赖的模块文件

将处理的template,script,style等所需的依赖以http请求的形式,通过query参数形式区分并加载SFC文件各个模块内容。

对比vue-cli

image

传统打包工具的dev-server

本地运行时需要加载所有模块文件并导出bundle才能展示,并且应用越来,打包启动速度也越慢,而且代码分割是对于生成环境。

image

基于浏览器ES module的dev-server

使用<script type="module">,浏览器可以解析ES module 的import 并发送http 请求,dev server拦截浏览器对模块的请求处理后返回

image

什么是ES module

MDN解释说如果标示了module的话会把代码当作js模块来执行,一篇关于es6的文章也很好的介绍了:

https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/script

当浏览器解析保留了模块关键字的代码,从而会导致HTTP请求,vite通过koa拦截了这些请求:

.vue => 拦截请求 => 编译 => 返回给客户端

为什么ESbuild这么快?

image

  • 因为Esbuild是用GO语言直接编译成原生代码
  • 由于GO语言特点,它的解析和映射并发非常快
  • 避免了不必要的配置
  • 数据转换很简单很快速

虽然ESbuild是一个非常快速的打包器,但是不支持热模块更新和没有开箱即用的工具,而且要像webpack一样想做一款基于ESbuild的插件.evan you自我实现了HRM 热更新。

HMR

请求得到对应的客户端代码,在客户端中创建了一个websocket服务并与服务端建立了连接,通过chokidar这个库创建watcher实例,监听文件变化,不同的消息触发一些事件做到浏览器端的即时热模块更换

image

目录结构

├── client # 客户端运行代码,主要是客户端的 socket 通信以及 HMR 相关
│   ├── client.ts
│   ├── env.d.ts
│   ├── tsconfig.json
│   └── vueJsxCompat.ts
├── hmrPayload.ts # HMR 类型定义
└── node # 服务端运行代码
    ├── build # vite build 命令运行代码
    │   ├── buildPluginAsset.ts
    │   ├── buildPluginCss.ts
    │   ├── buildPluginEsbuild.ts
    │   ├── buildPluginHtml.ts
    │   ├── buildPluginManifest.ts
    │   ├── buildPluginReplace.ts
    │   ├── buildPluginResolve.ts
    │   ├── buildPluginWasm.ts
    │   └── index.ts
    ├── cli.ts
    ├── config.ts
    ├── esbuildService.ts # esbuild 相关代码
    ├── index.ts
    ├── optimizer # 预优化
    │   ├── entryAnalysisPlugin.ts
    │   ├── index.ts
    │   └── pluginAssets.ts
    ├── resolver.ts # 模块加载逻辑
    ├── server # koa服务使用的一些中间件
    │   ├── index.ts
    │   ├── serverPluginAssets.ts
    │   ├── serverPluginClient.ts
    │   ├── serverPluginCss.ts # 处理css和其他css预处理器文件
    │   ├── serverPluginEnv.ts
    │   ├── serverPluginEsbuild.ts
    │   ├── serverPluginHmr.ts # 服务端热模块替换相关
    │   ├── serverPluginHtml.ts
    │   ├── serverPluginJson.ts
    │   ├── serverPluginModuleResolve.ts # 处理 /@modules/:id 开头请求的文件
    │   ├── serverPluginModuleRewrite.ts
    │   ├── serverPluginProxy.ts 
    │   ├── serverPluginServeStatic.ts # koa静态服务器
    │   ├── serverPluginSourceMap.ts
    │   ├── serverPluginVue.ts # 处理.vue文件
    │   ├── serverPluginWasm.ts
    │   └── serverPluginWebWorker.ts
    ├── transform.ts
    ├── tsconfig.json
    └── utils
        ├── babelParse.ts
        ├── createCertificate.ts
        ├── cssUtils.ts
        ├── fsUtils.ts
        ├── index.ts
        ├── openBrowser.ts
        ├── pathUtils.ts
        ├── resolveVue.ts
        ├── shims.d.ts
        └── transformUtils.ts

总结

vite在浏览器端使用 export import 的方式导入和导出模块,同时实现了按需加载。vite高度依赖module script特性。

vite 大致的原理过程是这样,其中细节 比如 .vue文件怎么处理解析node_moduleskoa拦截等涉及到源码,可以去vite源码一路顺下去。

参考文献