Vite 基础
vite 优势
1 .浏览器内置ES Module 的支持,script标签加上type='module',浏览器直接向dev server逐个请求,而不需要提前把所有文件打包
2 .借助esbuild 超快的编译速度可以将第三方库进行预构建,一方面将零散文件打到一起,减少网络请求,另一方面全面转换为ESM模块语法,适配浏览器内置的ESM支持
3 .这里的编译也回把一些commonjs导出方式的包做兼容,转换成ES Module,放置在node_modules/.vite 文件夹下
image.png
开箱即用
极速启用
<script type="module">
import { a } from './a.js'
</script>
1 .当声明一个script标签类型为module时,浏览器将对其内部的import引用发起HTTP请求获取模块内容。比如上述,浏览器会发起对a.js的请求,获取到内容之后在执行
2 .Vite会劫持这些请求,在后端进行相应的处理。(将vue文件拆分为template,style,script三个部分),然后返回浏览器。会把.vue的文件拆分为3个请求。然后解析到template和style的路径后,在此发起HTTP请求来请求对应的资源。
3 .由于浏览器只会对用到的模块发起HTTP请求,所以vite没必要对项目里面的所有文件先打包后返回,而是需要编译浏览器发起http请求的模块即可
4 .Vite 在打包的时候,将代码模块分成2个部分,依赖和源码
4.1 依赖
1 .开发中不会改变的js代码。比如组件库,一些较大的依赖,这一部分使用esbuild 进行预构建依赖
2 .esbuild用的是go语言进行,比js编写的打包器预构件依赖快10-100倍
4.2 源码
1 .一般是一些修改机率比较大的文件,比如我们自己的业务代码。
2 .这些文件不是一股脑全部加载,而是按照需要加载。Vite会降这些文件转换,然后以es module的方式直接交给浏览器。
5 .webpack需要价格所有文件打包成一个文件bundle.js.然后html引入。多文件->bundle.js的过程是非常耗时间的
6 . Vite 运行Dev命令之后只做了两件事,一是启动本地服务器并注册了一些中间件,二是使用ESbuild预构件模块。之后就一直躺着,直到浏览器以http方式发来ESM规范的模块请求时,Vite才开始按需编译被请求的模块
7 .webpack启动之后会做一堆事情,经历一条很撑的编译打包链条,从入口开始需要逐步经历语法解析,依赖收集,代码转译,打包合并,代码优化,最终将高版本,离散的源码编译打包成低版本,高兼容性的产物代码,在node运行下性能必然有问题。特别是在做一些库项目的时候。
预编译是什么?
1 .vite在开发阶段会对项目中使用的第三方依赖如react,react-dom,lodash-es等做预构件操作。
2 .因为vite是基于浏览器原生的ESM规范来实现dev server 的,所以一些库如果不符合ESM规范,就需要代码帮他转换,将非ESM规范的代码转换为符合ESM规范的代码
3 .还有一些第三方依赖已经符合ESM规范,但是是由多个子文件组成的,比如lodash-es,如果不做处理就直接使用,会引发请求瀑布流,影响页面性能。所以通过预构件,我们将第三方依赖的多个文件合并为一个,减少请求数量,优化页面加载性能
4 .通过配置optimizeDeps.excluede=['react'],可以在dev server 的过程中不对react进行预构建,由于不是comminjs编译的,所以这里是不行的,会请求报错
5 .如果不对lodas-es进行预编译,那么请求的时候会请求600多个散文件
6 .预构建的结果会保存到node-modules 的 .vite/deps 目录下。当在此启动dev server的时候,如果项目的第三方依赖,.lock文件没有变化和vite.config没有改变,那么vite会重复上一次预构建的结果。如果不想让vite复用上一次预构建的结果,可以设置optimizeDeps.force=true,这样每次启动都会强制进行预构件
二次预构建
1 .如何快速获取项目中所有的第三方依赖?
2 .vite 借助esbuild可以比webpack更快的打包能力,打包的时候,通过分析依赖关系,得得项目中所有的源文件的url,然后分离出所有第三方依赖
1 .从入口文件开始
2 .找到源文件中静态依赖,import xxx from 'xxx',以及动态依赖 import('xx')对应的url,收集到一个对象中
3 .遍历文件,知道没有
3 .如果第三方依赖没有找到,或者出现一下情况,会进行二次预构建
1 .plugin 在运行过程中,动态给与源码注入了新的第三方依赖
2 .动态依赖在运行代码的时候,才能确定最终的url,是拼接出来的
vite 对预构件的优化
1 .在开发过程中,如果频繁的触发二次预构建,那么势必会进行重复的reload操作,如果一直这样,开发环境简直是一种折磨
2 .实际过程中,在首屏期间,即使有未构建的第三方依赖,也不会发生页面reload
3 .但实际结果是,dev server 会等首屏期间涉及的所有模块依赖关系全部解析完毕之后,才会给浏览器发送response.所以实际表现是,如果发现有未预构建的第三方依赖,那么第三方依赖的请求会一直被阻塞,知道二次预构建完成为止
极速热更新
1 .热更新的速度不会随着模块增多而变慢
2 .vite在开发模式下不需要打包,只需要编译浏览器发出的http请求对应的文件即可
3 .如果 a 发生了改变,只需要更新 a 以及用到 a 的 c。由于 b 没有发生改变,所以 Vite 无需重新编译 b,可以从缓存中直接拿编译的结果。这样一来,修改一个文件 a,只会重新编译这个文件 a 以及浏览器当前用到这个文件 a 的文件,而其余文件都无需重新编译。所以理论上热更新的速度不会随着文件增加而变慢。有点像虚拟dom的diff更新patch
4 .Vite通过Websocket来实现热更新
5 .客户端部分监听以下几种消息
1 .connected,websocket链接成功
2 .vue-reload:vue组件重新加载(修改了script里的内容)
3 .vue-rerender:vue组件重新渲染(修改了template里的内容)
4 .style-update:样式更新
5 .style-remove:样式移除
6 .js-update:js文件更新
7 .full-update:fallback机制,网页重新刷新
6 .更新逻辑有用到timestamp刷新重新执行的方法来达到更新的目的
7 .服务端:核心是监听项目文件的变更,然后根据不同文件类型来做不同的处理
1 .如果是Vue文件有变化,主要是重新编译Vue文件,检测template,script,style的改动,如果有改动就通过ws服务端发起对应的热更请求
2 .他这个如果是嵌套复杂的,会不会只重新编译和更改某一个子的.vue文件。
8 .在项目启动的时候,将模块分成依赖和源码,当更新代码的时候,依赖部分是不需要重新加载的。只需要更新源码的修改文件即可
9 .vite也会利用http头协议来缓存页面,比如304 not Modified进行协商缓存,而依赖模块请求则会通过Cache-Control:max-age=315636000,immutable进行强缓存
问题
1 .项目迁移
1 .痛点
2 .启动时间长
3 .热更新慢。热更新失效
4 .过度封装,自定义插件难度大
5 .统计当前项目中遇到的技术栈,看新工具的支持程度
2 .生产环境
3 .打包和编译的区别
1 .打包
2 .