🚀🚀 飞一般的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))

[configResolved](插件 API | Vite 官方中文文档 (vitejs.dev))

....具体查看[Vite独有](插件 API | Vite 官方中文文档 (vitejs.dev))

我们直接快速导入我们的配置文件中. 我们重点看下,transform load resolveId,这三个钩子,大部分的插件都使用到,能运用到80%的场景中

  • transform: 简单来说 转换模块的哪痛,进行自定义转换,比如babel转换,或者有一个需求我们可能需要将当引入的svg文件.转化为一个Vue组件

  • load: 加载模块的内容

  • resolveId: 解析文件的路径

插件执行的顺序,不懂就戳

  • Alias
  • 带有 enforce: 'pre' 的用户插件
  • Vite 核心插件
  • 没有 enforce 值的用户插件
  • Vite 构建用的插件
  • 带有 enforce: 'post' 的用户插件
  • Vite 后置构建插件(最小化,manifest,报告)

我们可以发现可以利用 enforce = 'pre''post'来进行对插件的顺序调整

使用apply 属性指明它们仅在 'build''serve' 模式时调用,也就是在预览构建期间有条件地应用

列举一些插件,同志们可以去看看

图片优化,压缩

Commonjs转Esm

更多插件,自行翻阅

一瓶水 一个Debugger 解决无数问题🤪

附上最近做的一个小图标库,有需要的伙伴们用起来,后面还会更新!

总结: 项目中越来越多使用Vite进行构建,学习构建要同时学习EsbuildRollup 打包工具,当然在打包过程中使用Rollup进行打包构建,还是多多少少会有性能的差异,毕竟还是使用js来.尤大 正在开发一种RollDown 底层使用Rust来进行构建(包含预构建与构建过程,二者合一),狠狠期待了! 速度应该会有飞速的提升

相关推荐
清灵xmf33 分钟前
在 Vue 中实现与优化轮询技术
前端·javascript·vue·轮询
大佩梨43 分钟前
VUE+Vite之环境文件配置及使用环境变量
前端
GDAL1 小时前
npm入门教程1:npm简介
前端·npm·node.js
小白白一枚1112 小时前
css实现div被图片撑开
前端·css
薛一半2 小时前
PC端查看历史消息,鼠标向上滚动加载数据时页面停留在上次查看的位置
前端·javascript·vue.js
@蒙面大虾2 小时前
CSS综合练习——懒羊羊网页设计
前端·css
MarcoPage2 小时前
第十九课 Vue组件中的方法
前端·javascript·vue.js
.net开发2 小时前
WPF怎么通过RestSharp向后端发请求
前端·c#·.net·wpf
**之火3 小时前
Web Components 是什么
前端·web components
顾菁寒3 小时前
WEB第二次作业
前端·css·html