🚀🚀 飞一般的Vite-为何速度如此之快,叫人心生震撼!!🎉🎉🎉

前言: 小弟不才,大胆猜测 以后前端项目构建工具Vite是未来.所以多学学它的核心底层思想.这一点也不过分.

先说说早期:

  • 构建大型的项目,数千个模块非常普遍,开发时构建工具,也会受到性能瓶颈
  • 开发过程时,启动项目需要很长时间,HMR也需要等待几秒钟才可以,如此反复极大影响开发者的开发效率

Vite的出现,实现开发阶段无打包或者说是让浏览器去执行,多亏Es Module模块规范

  • 每个特有的模块都会发起一个请求,内部起一个服务,针对不同的模块做不同的处理.
  • 当然这也会造成请求过多,当然Vite内部实现了预构建,将一些依赖中的子依赖,最终只需请求一次即可.
  • 使用ESbuild 进行预构建处理,并且在内部中 也存在一些ts jsx等类型,也会使用ESbuild进行转化
  • 提供更快的HMR,允许一个模块进行替换,不会影响页面其他部分,
  • 当然最终打包还是使用的是Rollup 更加灵活,生态更加成熟,兼容生态,毕竟Rollup的npm包下载量过亿,生态也经营6,7年

接下来我们重点围绕3部分,来让我们更懂Vite这个东西

1.开发阶段:

首先我们简单创建一个Vite项目

执行 : yarn create Vite快速建立一个Vite+vue3模版

yarn installyarn serve

启动项目:

Vite内部帮我们起了一个服务,我们大概来看下具体做了哪些事情.

我们发现启动项目之后非常快,项目就启动完毕了 我们截图看下项目的network! ! !

非常多的请求! 🤫 🤫 🤫

果然把项目中的模块都转换成ESM模块,这样每次每个模块请求都会经过服务器拦截.然后经过不同的中间件处理

那你可能会有一个疑问,有些第三方库根本不是符合ESM的模块啊,那你咋办?

答案:当然尤大也知道,那肯定需要做一层转换啊,将UMD模块,CMD模块转换成统一ESM模块,在预构建中处理

这么多请求,这对带宽的压力也有点大啊?

答案:当然尤大也知道,那就是在一个库中,如果存在多个引入模块,都处理成一个模块中,这样只需请求一次啦!也是在预构建中处理

ok我们知道,当你需要什么模块,就去请求,此时返回给浏览器,完全不需要开发打包,实现 no bundle 概念,所以这样就快多了🐮🐮

注意⚠️ (此时的ts,vue等每个请求都以 application/javascript处理,都统一以js返回形式处理)浏览器只能识别js

Vite内部,使用connnet包创建一个服务,以中间件的形式处理不同的请求

从入口文件 index.html开始,当处理index.html时,发现内部有其他的资源的请求

比如发现main.ts 此时请求main.ts,会有专门的中间件处理,并返回给浏览器执行

返回内容:

我们发现引入的Vue模块,路径会被内部解析到.vite下.(这是访问预构建的路径,我们后面了解一下 ) 此时会去请求Vue,style.css,App.vue资源

此时运用到中间件,不同的资源,不同的中间件处理

我们重点看下遇到vue组件是怎么处理的呢?

首先我们应该知道,在创建一个新的模版中,会有一个vite.config.ts来对项目的开发与构建中做配置.

我们可以发现有一个@vitejs/plugin-vue插件

这个插件是用来处理Vue组件的,用来扩展对Vue的支持,同时还会注入热更新的代码. 简单看下

有同志就不懂了,这插件跟处理Vue组件有啥关系?

答案: 其实呢,插件机制更够让Vite内部处理更多的资源,Vite内部本身是支持像js文件处理的,那不能处理Vue组件代码,scss等资源吧,都写在Vite内部,代码只会变得膨大臃肿,使用插件这种机制,处理不同的资源,对于日后的维护性,以及扩展性是非常友好的.

重点我们说下遇到Vue组件,会怎么处理转换,遇到Sfc组件,会使用@vue/compier-sfc转化,分为三部分,template script css

我们需要处理不同的资源.

最终返回给浏览器的,查看下图

图中第一部分: 是组件的script部分

图中第二部分: 是组件的template部分,生成render函数

图中第三部分: 是组件的css部分

我们需要知道引入一个组件,会导出组件实例,实例包含组件的render渲染函数渲染选项 | Vue.js (vuejs.org)

我们可以通过Vue文档了解一下渲染的机制渲染机制 | Vue.js (vuejs.org)

最终得到了一个组件开发的最终结果.

特别说明: Vue中的样式,最终处理成一个ESM,import快速导入.方便维护扩展.

import "/src/App.vue?vue&type=style&index=0&scoped=7a7a37b1&lang.css";

2.预构建

预构建做了什么事? 我们知道开发阶段,需要根据不同请求编辑返回.

那如果这么多请求需要怎么处理?

过多的请求会造成带宽的压力,造成请求的加载等待,严重影响用户的体验💣💣

预构建因此而生:

将过多的请求,处理成只有一个请求,这样只需请求一次,例如,lodash-es 有超过 600 个内置模块!当我们执行 import { debounce } from 'lodash-es' 时,浏览器同时发出 600 多个 HTTP 请求!即使服务器能够轻松处理它们,但大量请求会导致浏览器端的网络拥塞,使页面加载变得明显缓慢。通过将 lodash-es 预构建成单个模块,现在我们只需要一个HTTP请求!详情Vite官方文档(cn.vitejs.dev/guide/dep-p...)

并且将一些模块.将CommonJs或者UMD的依赖转为Es模块, Vite开发的服务器 将所有的模块都视为ES模块处理.

什么条件模块下,才会预构建.

答:只有一些裸模块,也就是第三方库,没有相对路径,像一些antd,dayjs,vue 等.....

预构建,将处理的模块放在node_modules/.vite,也就是每次引入这些模块都会去这些已经预处理的模块中去拿取.

并且 请求时设置 HTTP 头 max-age=31536000, immutable 进行强缓存,不用每次都去处理,不会再经过 Vite Dev Server,直接用缓存结果.

预构建分为手动开启与自动开启.

手动: 启动开发服务器时指定 --force 选项,或手动删除 node_modules/.vite 缓存目录

自动: 首次开启项目,我们会发现

new dependencies optimized 新的依赖正在预构建.

这样会把一些缓存到,.vite/ 文件夹下:

注意: 依赖预构建仅适用于开发模式,并使用 esbuild 将依赖项转换为 ES 模块。在生产构建中,将使用 `@rollup/plugin-commonjs(依赖预构建 | Vite 官方中文文档 (vitejs.dev))

会有一个问题,那就是动态引入模块的时候,根本不知道你要引入什么模块,只能在文件被浏览器请求并转换后才能发现.也就是执行了才知道引入什么模块!

正如我们上面看到的,意思发现了新的模块lodash-es,这时需要重新二次预构建,而且在有必要时候,会刷新页面. 这也就是为什么,我们常常在开发阶段,点击新的组件菜单的时候,发现莫名刷新一下,其实此时再预构建,等到第二次你在点击该页面就不会刷新了.

那我们有什么办法可以防止这种不好的体验呢?

答:有的,Vite支持使用optimizeDeps.includeoptimizeDeps.exclude

include: 使用此选项可强制预构建链接的包。

我们可以发现一开始就会预构建了这些include的依赖,查看.metadata.json文件

js 复制代码
   "optimized": {
    "jszip": {
      "src": "../../jszip/dist/jszip.min.js",
      "file": "jszip.js",
      "fileHash": "0e9ea30c",
      "needsInterop": true
    },
    "lodash-es": {
      "src": "../../lodash-es/lodash.js",
      "file": "lodash-es.js",
      "fileHash": "86bcc851",
      "needsInterop": false
    }
   }

后面打开该引入的模块,不会强制刷新了,体验杠杠的🎉🎉

exclude: 在预构建中强制排除的依赖项。

我们将jsZip模块进行过滤,不构建,也就是不处理.这样会造成什么问题呢?

直接给你报错了,jsZip 根本不是ES模块,我们知道在开发阶段,是只能处理ES模块的.你排除了对jsZip的预构建.💥💥💥

3.插件系统

sql 复制代码
开发: Vite内部都是在Rollup的插件机制上,去实现一套类似的插件机制,大部分可以兼容rollup钩子.

生产: 使用Rollup做生产的构建.直接使用Rollup成熟的打包能力进行扩展和优化

也就是说其实大部分的rollup插件,可以直接在Vite插件中使用,详细[插件 API | Vite 官方中文文档 (vitejs.dev)]

插件钩子: 以下为通用钩子

以下钩子在服务器启动时被调用:

以下钩子会在每个传入模块请求时被调用:

以下钩子在服务器关闭时被调用:

开发阶段实现了一套自己的插件容器来调度Roolup钩子.

以下为Vite特有的钩子

`config`\]([插件 API \| Vite 官方中文文档 (vitejs.dev)](https://link.juejin.cn?target=https%3A%2F%2Fcn.vitejs.dev%2Fguide%2Fapi-plugin.html%23config "https://cn.vitejs.dev/guide/api-plugin.html#config")) \[`configResolved`\]([插件 API \| Vite 官方中文文档 (vitejs.dev)](https://link.juejin.cn?target=https%3A%2F%2Fcn.vitejs.dev%2Fguide%2Fapi-plugin.html%23configresolved "https://cn.vitejs.dev/guide/api-plugin.html#configresolved")) ....具体查看\[Vite独有\]([插件 API \| Vite 官方中文文档 (vitejs.dev)](https://link.juejin.cn?target=https%3A%2F%2Fcn.vitejs.dev%2Fguide%2Fapi-plugin.html%23vite-specific-hooks "https://cn.vitejs.dev/guide/api-plugin.html#vite-specific-hooks")) 我们直接快速导入我们的配置文件中. 我们重点看下,`transform` `load` `resolveId`,这三个钩子,大部分的插件都使用到,能运用到80%的场景中 * **transform:** 简单来说 转换模块的哪痛,进行自定义转换,比如babel转换,或者有一个需求我们可能需要将当引入的svg文件.转化为一个Vue组件 * **load:** 加载模块的内容 * **resolveId:** 解析文件的路径 插件执行的顺序,[不懂就戳](https://link.juejin.cn?target=https%3A%2F%2Fcn.vitejs.dev%2Fguide%2Fapi-plugin.html%23plugin-ordering "https://cn.vitejs.dev/guide/api-plugin.html#plugin-ordering") * Alias * 带有 `enforce: 'pre'` 的用户插件 * Vite 核心插件 * 没有 enforce 值的用户插件 * Vite 构建用的插件 * 带有 `enforce: 'post'` 的用户插件 * Vite 后置构建插件(最小化,manifest,报告) 我们可以发现可以利用 `enforce = 'pre'` 与 `'post'`来进行对插件的顺序调整 使用`apply` 属性指明它们仅在 `'build'` 或 `'serve'` 模式时调用,也就是在**预览** 或**构建期**间有条件地应用 列举一些插件,同志们可以去看看 [图片优化,压缩](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2FFatehAK%2Fvite-plugin-image-optimizer%2Fblob%2Fmain%2Fsrc%2Findex.ts "https://github.com/FatehAK/vite-plugin-image-optimizer/blob/main/src/index.ts") [Commonjs转Esm](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Foriginjs%2Fvite-plugins%2Fblob%2Fmain%2Fpackages%2Fvite-plugin-commonjs%2Fsrc%2Findex.ts "https://github.com/originjs/vite-plugins/blob/main/packages/vite-plugin-commonjs/src/index.ts") 更多插件,[自行翻阅](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fvitejs%2Fawesome-vite%23plugins "https://github.com/vitejs/awesome-vite#plugins") ***一瓶水 一个Debugger 解决无数问题🤪*** [附上最近做的一个小图标库](https://link.juejin.cn?target=https%3A%2F%2Fwww.wwcattle.site%2F "https://www.wwcattle.site/"),有需要的伙伴们用起来,后面还会更新! > 总结: 项目中越来越多使用Vite进行构建,学习构建要同时学习**Esbuild** 与**Rollup** 打包工具,当然在打包过程中使用Rollup进行打包构建,还是多多少少会有性能的差异,毕竟还是使用js来.尤大 正在开发一种**RollDown** 底层使用Rust来进行构建(包含预构建与构建过程,二者合一),狠狠期待了! [速度应该会有飞速的提升](https://link.juejin.cn?target=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DhrdwQHoAp0M%26feature%3Dyoutu.be "https://www.youtube.com/watch?v=hrdwQHoAp0M&feature=youtu.be")

相关推荐
恋猫de小郭1 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊10 小时前
jwt介绍
前端