Análisis del principio de Vite

Puntos débiles de ingeniería actuales

Antes de que los navegadores admitieran ES Module, JavaScript no proporcionaba un mecanismo nativo para que los desarrolladores desarrollaran de forma modular. Es por eso que nacieron las herramientas de empaquetado: herramientas que capturan, procesan y concatenan módulos de código fuente en archivos que se pueden ejecutar en un navegador.

Aunque ahora existen herramientas como webpack, Rollup, etc., que mejoran mucho la experiencia de los desarrolladores front-end. Pero a medida que las aplicaciones creadas crecen, la cantidad de código JavaScript que debe procesarse crece exponencialmente. Es bastante común que un proyecto contenga miles de módulos. Los desarrolladores comienzan a encontrar cuellos de botella en el rendimiento. Las herramientas desarrolladas con JavaScript suelen tardar mucho en iniciar el servidor de desarrollo. Incluso si se utiliza HMR, el efecto modificado del archivo tardará cierto tiempo en mostrarse en el navegador. Este ciclo afectará en gran medida al desarrollo. experiencia.

inicio lento del servicio

Las herramientas de empaquetamiento y construcción de uso común, como el paquete web, capturan, compilan y construyen principalmente el código de toda la aplicación y luego generan una compilación, que puede ser compatible con el código de cada navegador después de la optimización. El proceso en el entorno de desarrollo es básicamente el mismo. Primero debe compilar y empaquetar la aplicación completa y luego entregar el código empaquetado al servidor de desarrollo. El primer inicio es muy lento. Con la complejidad del negocio front-end y los proyectos a gran escala, la cantidad de código JavaScript aumenta exponencialmente, y el tiempo de empaquetado y construcción es cada vez más largo. El tiempo de inicio del servidor de desarrollo para proyectos grandes alcanza decenas de segundos o incluso minutos.

Proceso de servidor de desarrollo de empaquetador común:

manojo

actualización lenta

La reconstrucción de todo el paquete es ineficiente cuando se inicia en función del empaquetador. Aunque el empaquetador ahora admite la actualización activa de módulos HMR, cuando los archivos en el proyecto cambian, el empaquetador volverá a empaquetar las dependencias relevantes (no solo el archivo actual). La velocidad de la actualización en caliente disminuirá significativamente a medida que crezca el tamaño del programa, y ​​ha alcanzado el cuello de botella de rendimiento, y no hay mucho espacio para la optimización.

¿Por qué elegir Vite?

Vite utiliza nuevos desarrollos en el ecosistema front-end para resolver los problemas anteriores. Los navegadores han comenzado a admitir módulos ES de forma nativa, y cada vez más herramientas de front-end están escritas en lenguajes compilados, como Go (ESBuild) y Rust (SWC).

Arreglar el inicio lento

Vite直接把转换后的ES Module代码,扔给支持ES Module的浏览器,利用浏览器对ES Module的支持(script标签加上属性type="module"即可),让浏览器自己去加载,浏览器直接向dev server逐个请求各个模块,而不需要提前将所有文件进行打包,从而达到项目快速启动的效果。

Vite在一开始将应用中的模块分为依赖源码,改进了开发服务器的启动时间。

  • 依赖大多为开发时不会变动的纯JavaScript。一些较大的依赖(比如有上百个模块的组件库)处理的代价很高。并且依赖也通常会存在多种模块化格式比如CommonJS,ES Module等等。Vite使用ESBuild预构建依赖。借助ESBuild超快的编译速度对第三方库进行构建,一方面将零散的文件预构建打包到一起,减少网络请求,另一方面全面转换ES Module语法,以适配浏览器内置的ES Module支持。ESBuild使用Go编写,比用纯JavaScript编写的打包工具的预构建依赖快10~100倍。
  • 源码通常包含一些并非直接是JavaScript的文件,比如CSS等。这些文件需要转换,且时常会被编辑。同时并不是所有的源码模块都需要同时被加载。Vite以原生ES Module的方式提供源码。这实际上是让浏览器接管了打包的部分工作,Vite只需要在浏览器请求源码时进行转换并按需提供源码即可。

Vite的dev server的过程:

esm

解决缓慢的更新

在Vite中,HMR是在原生ES Module上执行的。当编辑一个文件时,Vite只需要精确地使已编辑的模块与其最近的HMR边界之间的连接失活(大多数只是模块本身),不用重新构建整个应用,就可完成模块的热更新,使得无论应用的大小如何,HMR都能始终保持快速更新。同时Vite利用浏览器缓存来加速整个页面的重新加载:源码模块的请求会根据304 not Modified进行协商缓存,而依赖模块会通过Cache-control进行强制缓存,一旦被缓存它们将不会再次请求。

Vite简介

Vite是一种新型的前端构建工具,基于ESBuild和Rollup。在开发环境依靠浏览器自身的ES Module编译功能去解析importdev server对模块进行编译,按需返回,完全去掉了打包的步骤。同时在开发环境拥有高效的模块热更新(HMR),且热更新的速度不会随着模块的增多而变慢,显著提升开发体验。

主要有两部分组成:

  • 开发构建:基于浏览器原生的ES Module能力来提供源文件,同时内置了高效的HMR。
  • 生产构建:生产环境使用Rollup进行打包,Vite提供指令来优化构建打包过程。

Vite特点

  • 快速的冷启动:No Bundle + ESBuild预构建。
  • 即时的模块热更新:基于ES Module的HMR,同时利用浏览器缓存策略提升速度。
  • 真正的按需加载:利用浏览器对ES Module的支持,实现真正的按需加载。

浏览器的支持

  • 开发环境:Vite需要在支持原生ES Module的导入的浏览器上使用。
  • 生产环境:浏览器需要支持通过script标签引入原生ES Module。可以通过官方插件 @vitejs/plugin-legacy支持旧浏览器。

imagen-20220320223455897

Vite主体流程

imagen-20220322000610880

Vite原理

当声明一个script标签类型为module时,比如:

 <script type="module" src="/src/main.js"></script>
复制代码

浏览器解析资源的时候,会往当前域名发起一个HTTP请求,获取main.js文件。然后发现main.js内部含有import引入的模块,所以又会发起HTTP请求获取模块的内容。而Vite在启动项目阶段,会启动一个Koa dev server拦截浏览器对ES Module的请求,dev server对模块进行处理后,将模块加载到浏览器中即可。因此跳过了整体打包的阶段,而且是按需加载的。

请求拦截原理

当碰到import时,浏览器会发起一个HTTP请求获取模块内容。而dev server在收到请求之后,会拦截该请求,并对模块进行处理之后返回相应的结果。

重写模块路径

由于浏览器只能识别 ./、../ 这样开头的相对路径以及 / 开头的绝对路径,而开发过程中经常会引入node_modules下的模块,浏览器无法识别和加载。因此Vite的dev server在请求拦截时通过es-module-lexer和magic-string这两个库对模块的路径进行重写。

HMR原理

Vite的热更新原理,其实就是在客户端与服务端之间建立了一个websocket链接监听文件的改变,当文件被修改时,dev server发送消息通知客户端修改相应的代码。

热更新主体流程:

  • 创建一个websocket服务端和client文件,启动服务。

    项目启动后Vite会在处理html时将client注入。

    imagen-20220323000909624

  • 通过chokidar的watch方法创建一个监听对象watcher,监听文件的变更。

       const watcher = chokidar.watch(path.resolve(root), {
         ignored: [
           '**/node_modules/**',
           '**/.git/**',
           ...(Array.isArray(ignored) ? ignored : [ignored])
         ],
         ignoreInitial: true,
         ignorePermissionErrors: true,
         disableGlobbing: true,
         ...watchOptions
       }) as FSWatcher
       ....
       watcher.on('change', async (file) => {
     ​
       })
       watcher.on('add', (file) => {
       })
       watcher.on('unlink', (file) => {
       })
    复制代码
  • 当代码变更后,服务端进行判断处理并推送到客户端。

    客户端根据推送消息的类型执行不同的更新操作。

client对推送消息类型的处理

imagen-20220323001117945

Vite的优化

依赖预构建

Vite会对依赖进行预构建有两个原因:

  • CommonJS和UMD兼容性:由于Vite是基于浏览器原生支持ES Module的能力去实现的,但是在开发阶段中,Vite将所有代码视为原生ES Module模块,因此Vite必须提前将CommonJS和UMD发布的依赖项转换为ES Module,并缓存入node_modules/.vite。
  • 减少模块和请求的数量:Vite将有许多内部模块的ESM依赖关系转换为单个模块,以此来提高页面的加载性能。一些包将他们的ES Module构建作为许多独立的文件相互导入。比如lodash-es有超过600个内置模块。当解析 import { debounce } from 'lodash-es' 时,浏览器同时发出 600 多个 HTTP 请求!这些请求会造成网络拥堵,影响页面的加载。因此Vite通过预构建将lodash-es构建为一个模块,所以只需要一个HTTP请求即可。

缓存

文件系统缓存

Vite会将预构建的依赖缓存到node_modules/.vite。它根据几个源来决定是否需要重新运行预构建步骤:

  • package.json中的dependencies列表。
  • 包管理器的 lockfile,例如 package-lock.json, yarn.lock,或者 pnpm-lock.yaml
  • 可能在 vite.config.js 相关字段中配置过的。

只有在上述其中一项发生更改时,才需要重新运行预构建。

如果出于某些原因,你想要强制 Vite 重新构建依赖,你可以用 --force 命令行选项启动开发服务器,或者手动删除 node_modules/.vite 目录。

浏览器缓存

Las solicitudes de módulos de origen se almacenarán en caché de acuerdo con 304 no modificado, mientras que los módulos dependientes se verán obligados a almacenarse en caché a través de Cache-Control: max-age=3253600, inmutable. Una vez almacenados en caché, ya no será necesario volver a solicitarlos.

problemas actuales

  • El soporte de React no es tan completo como el de Vue.
  • Problemas de compatibilidad con navegadores móviles.
  • El ecosistema Vite no es tan poderoso como el paquete web.

Supongo que te gusta

Origin juejin.im/post/7078294891737907214
Recomendado
Clasificación