首页 > 其他分享 >Vite原理

Vite原理

时间:2023-12-13 22:12:26浏览次数:26  
标签:缓存 浏览器 Module Vite 模块 原理 ES

当前工程化的痛点

在浏览器支持ES Module之前,JavaScript并没有提供原生机制让开发者以模块化的方式进行开发。这也是打包工具诞生的原因:使用工具抓取,处理并将源码模块串联成可以在浏览器中运行的文件。 虽然现在有webpack,Rollup等工具,极大地改善了前端开发者的体验。但是当构建的应用越来越大时,需要处理的JavaScript代码量也呈指数级增长。一个项目包含数千个模块相当普遍。开发者开始遇到性能瓶颈。使用JavaScript开发的工具通常需要很长的时间才能启动开发服务器,即使使用HMR,文件修改后的效果也需要一定的时间才能在浏览器中呈现出来,如此循环往复,极大地影响开发体验。

缓慢的服务启动

常用的打包构建工具比如webpack,主要是通过抓取-编译-构建整个应用的代码,之后生成一份编译,优化后能良好地兼容各个浏览器的代码。在开发环境的流程也基本相同,需要先将整个应用构建打包后,再把打包后的代码交给dev server,首次启动非常慢。随着前端业务的复杂化,项目的大型化,JavaScript的代码量呈指数级增长,打包构建的时间越来越久,大型项目的dev server启动时间达到几十秒甚至几分钟。 常见的打包器dev server的过程:

缓慢的更新

基于打包器启动时,重建整个包的效率很低。虽然现在打包器支持了模块的HMR热更新,但是当项目中的文件变动时,打包器会对相关的依赖(不只是当前文件)重新打包。热更新的速度会随着程序体积的增长而显著下降,已到达性能瓶颈,没有多少优化空间了。

为什么选择Vite?

Vite利用前端生态中的新进展解决上述的问题。浏览器开始原生支持ES Module,且越来越多的前端工具使用编译型语言编写,如Go(ESBuild)和Rust(SWC)。

解决缓慢的启动

Vite直接把转换后的ES Module代码,扔给支持ES Module的浏览器,利用浏览器对ES Module的支持(script标签加上属性type="module"即可),让浏览器自己去加载,浏览器直接向dev server逐个请求各个模块,而不需要提前将所有文件进行打包,从而达到项目快速启动的效果。 Vite在一开始将应用中的模块分为依赖和源码,改进了开发服务器的启动时间。
  • 依赖
  • 源码按需
Vite的dev server的过程:

解决缓慢的更新

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

Vite简介

 

Vite是一种新型的前端构建工具,基于ESBuild和Rollup。在开发环境依靠浏览器自身的ES Module编译功能去解析import,dev 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

Vite主体流程

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注入。
  • 通过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对推送消息类型的处理

Vite的优化

依赖预构建

Vite会对依赖进行预构建有两个原因:
  • CommonJS和UMD兼容性:由于Vite是基于浏览器原生支持ES Module的能力去实现的,但是在开发阶段中,Vite将所有代码视为原生ES Module模块,因此Vite必须提前将CommonJS和UMD发布的依赖项转换为ES Module,并缓存入node_modules/.vite。
  • 减少模块和请求的数量:Vite将有许多内部模块的ESM依赖关系转换为单个模块,以此来提高页面的加载性能。一些包将他们的ES Module构建作为许多独立的文件相互导入。比如lodash-es600
import { debounce } from 'lodash-es'

  

缓存

文件系统缓存

Vite会将预构建的依赖缓存到node_modules/.vite。它根据几个源来决定是否需要重新运行预构建步骤:
  • package.json中的dependencies列表。
  • 包管理器的 lockfile,例如
package-lock.json yarn.lock pnpm-lock.yaml
  • 可能在
vite.config.js 只有在上述其中一项发生更改时,才需要重新运行预构建。 如果出于某些原因,你想要强制 Vite 重新构建依赖,你可以用 --force 命令行选项启动开发服务器,或者手动删除 node_modules/.vite 目录。

浏览器缓存

源码模块的请求会根据304 not Modified进行协商缓存,而依赖模块则会通过Cache-Control:max-age=3253600,immutable进行强制缓存,一旦被缓存它们将不再需要再次请求。

当前存在的问题

  • 对React的支持不如Vue全面。
  • 移动端浏览器的兼容性问题。
  • Vite生态不如webpack强大。

 

 

标签:缓存,浏览器,Module,Vite,模块,原理,ES
From: https://www.cnblogs.com/zhenjianyu/p/17900055.html

相关文章

  • ThreadLocal原理
    ThreadLocal主要起到线程隔离作用,使得每个线程拥有自己独立的一份数据,经过threadLocal处理的数据是线程独享的,不与其它线程分享或者干扰,因此能起到线程之间数据隔离的作用。ThreadLocal的几个核心方法:方法声明描述publicvoidset(Tvalue)设置当前线程绑定的局部变量pu......
  • 理解Mysql索引原理及特性
    作为开发人员,碰到了执行时间较长的sql时,基本上大家都会说”加个索引吧”。但是索引是什么东西,索引有哪些特性,下面和大家简单讨论一下。1索引如何工作,是如何加快查询速度索引就好比书本的目录,提高数据库表数据访问速度的数据库对象。当我们的请求打过来之后,如果有目录,就会快速的......
  • 视频流的含义、定义及其工作原理分析
    流媒体是一种通过互联网传输,将音频、视频等多媒体内容从存储设备传输到另一个设备的技术。与传统下载方式不同,流媒体可以实现边下边播,用户无需等待完整文件下载即可开始观看,同时具有流畅体验。流媒体的优点在于方便快捷,用户只需要网络连接和播放设备就能随时随地观看或听取所需内......
  • vite编译为什么会报错“__vite-browser-external:node:path、fs、url...”
    当你在使用Vite打包时,遇到类似于`__vite-browser-external`的错误消息,通常是因为在代码中尝试导入浏览器不支持的模块。`__vite-browser-external`是Vite内部的一个机制,用于替换浏览器环境中无法直接访问的Node.js核心模块。例如,浏览器不具备文件系统访问能力,因此Node.......
  • 从根上理解elasticsearch(lucene)查询原理(2)-lucene常见查询类型原理分析
    大家好,我是蓝胖子,在上一节我提到要想彻底搞懂elasticsearch慢查询的原因,必须搞懂lucene的查询原理,所以在上一节我分析了lucene查询的整体流程,除此以外,还必须要搞懂各种查询类型内部是如何工作,比如比较复杂的查询是将一个大查询分解成了小查询,然后通过对小查询的结果进行合并得到......
  • Istio从入门到精通—— 流量治理的原理 —— VirutalService —— HTTPMatchRequest
    流量治理的原理——VirutalService——HTTPMatchRequestHttpMatchRequestspecifiesasetofcriteriontobemetinorderfortheruletobeappliedtotheHTTPrequest.Forexample,thefollowingrestrictstheruletomatchonlyrequestswheretheURLpaths......
  • SAP ABAP 显式增强技术之 New BAdI 的技术原理介绍试读版
    本教程之前的文章,对SAPABAP各种增强技术做了一个概述:122.SAPABAP各种增强技术(Enhancement)概述-所谓第一代,第二代,第三代增强技术的出处是?然后第62篇文章,针对下图红色区域的基于EnhancementFramework增强技术中的隐式增强之ABAP报表增强,做了详细介绍:62.如何通过增......
  • 硬件开发笔记(十六):RK3568底板电路mipi摄像头接口原理图分析、mipi摄像头详解
    前言  本篇继续分析底板原理图mipi电路原理图、mipi摄像头输入硬件接口详解。<br>RK3568芯片摄像头接口  查看RK3568的芯片手册,摄像头接口并不支持直接sensor模拟信号输入,只能接收mipi信号,RK3568的摄像头接口引脚如下:    只支持mipi的数字信号摄像头。  本来计划......
  • 硬件开发笔记(十六):RK3568底板电路mipi摄像头接口原理图分析、mipi摄像头详解
    前言  本篇继续分析底板原理图mipi电路原理图、mipi摄像头输入硬件接口详解。 RK3568芯片摄像头接口  查看RK3568的芯片手册,摄像头接口并不支持直接sensor模拟信号输入,只能接收mipi信号,RK3568的摄像头接口引脚如下:    只支持mipi的数字信号摄像头。  本......
  • 火星探测器背后的人工智能:从原理到实战的强化学习
    本文详细探讨了强化学习在火星探测器任务中的应用。从基础概念到模型设计,再到实战代码演示,我们深入分析了任务需求、环境模型构建及算法实现,提供了一个全面的强化学习案例解析,旨在推动人工智能技术在太空探索中的应用。关注TechLead,分享AI全维度知识。作者拥有10+年互联网服务......