Vite是伴随着Vue3正式版一起发布的,最开始Vite 1.0的版本是为Vue3服务的,并不是跨框架的。之后半年时间左右,出现了Vite 2.0版本,Vite 2.0真正脱离了和Vue3的强关联,以插件的方式,可以集成到目前流行的主流前端框架(Vue3、React)里。
打包优化核心
目前的技术发展来说,不论是什么打包工具,为了在生产环境中获得最佳的加载性能,最好还是将代码进行 tree-shaking、懒加载和 chunk 分割(以获得更好的缓存)。
组成
-
一个开发服务器,它基于 原生 ES 模块 提供了 丰富的内建功能,如速度快到惊人的 模块热更新(HMR)。
-
一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。
选用原因
在浏览器支持 ES 模块之前,JavaScript 并没有提供原生机制让开发者以模块化的方式进行开发。这也正是我们对 “打包” 这个概念熟悉的原因:使用工具抓取、处理并将我们的源码模块串联成可以在浏览器中运行的文件。
当我们开始构建越来越大型的应用时,需要处理的 JavaScript 代码量也呈指数级增长。包含数千个模块的大型项目相当普遍。基于 JavaScript 开发的工具就会开始遇到性能瓶颈:通常需要很长时间(甚至是几分钟!题主遇见过5分钟的!)才能启动开发服务器,即使使用模块热替换(HMR),文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感。
Vite 旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持 ES 模块,且越来越多 JavaScript 工具使用编译型语言编写。
特点优势
Vite 通过在一开始将应用中的模块区分为 依赖 和 源码 两类,改进了开发服务器启动时间。
-
依赖 大多为在开发时不会变动的纯 JavaScript。一些较大的依赖(例如有上百个模块的组件库)处理的代价也很高。依赖也通常会存在多种模块化格式(例如 ESM 或者 CommonJS)。
Vite 将会使用 esbuild 预构建依赖。esbuild 使用 Go 编写,并且比以 JavaScript 编写的打包器预构建依赖快 10-100 倍。
-
源码 通常包含一些并非直接是 JavaScript 的文件,需要转换(例如 JSX,CSS 或者 Vue/Svelte 组件),时常会被编辑。同时,并不是所有的源码都需要同时被加载(例如基于路由拆分的代码模块)。
Vite 天然支持引入 .ts
文件
Vite 仅执行 .ts
文件的转译工作,并不执行 任何类型检查。并假定类型检查已经被你的 IDE 或构建过程处理了。
动态模块热替换(HMR)
Vite 以 原生 ESM 方式提供源码。这实际上是让浏览器接管了打包程序的部分工作:Vite 只需要在浏览器请求源码时进行转换并按需提供源码。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理。
HTTP缓存
Vite 同时利用 HTTP 头来加速整个页面的重新加载(再次让浏览器为我们做更多事情):源码模块的请求会根据 304 Not Modified 进行协商缓存,而依赖模块请求则会通过 Cache-Control: max-age=31536000,immutable进行强缓存,因此一旦被缓存它们将不需要再次请求。
依赖预构建
当你首次启动 vite
时,Vite 在本地加载你的站点之前预构建了项目依赖。
目的
-
CommonJS 和 UMD 兼容性: 在开发阶段中,Vite 的开发服务器将所有代码视为原生 ES 模块。因此,Vite 必须先将以 CommonJS 或 UMD 形式提供的依赖项转换为 ES 模块。
在转换 CommonJS 依赖项时,Vite 会进行智能导入分析,这样即使模块的导出是动态分配的(例如 React),具名导入(named imports)也能正常工作:
js// 符合预期 import React, { useState } from 'react'
-
性能: 为了提高后续页面的加载性能,Vite将那些具有许多内部模块的 ESM 依赖项转换为单个模块。
有些包将它们的 ES 模块构建为许多单独的文件,彼此导入。例如,lodash-es 有超过 600 个内置模块!当我们执行
import { debounce } from 'lodash-es'
时,浏览器同时发出 600 多个 HTTP 请求!即使服务器能够轻松处理它们,但大量请求会导致浏览器端的网络拥塞,使页面加载变得明显缓慢。通过将
lodash-es
预构建成单个模块,现在我们只需要一个HTTP请求!
缓存
文件系统缓存
vite 将预构建的依赖项缓存到 node_modules/.vite
中。它会基于以下几个来源来决定是否需要重新运行预构建步骤:
- 包管理器的锁文件内容,例如
package-lock.json
,yarn.lock
,pnpm-lock.yaml
,或者bun.lockb
; - 补丁文件夹的修改时间;
vite.config.js
中的相关字段;NODE_ENV
的值。
只有在上述其中一项发生更改时,才需要重新运行预构建。
如果出于某些原因你想要强制 Vite 重新构建依赖项,你可以在启动开发服务器时指定 --force
选项,或手动删除 node_modules/.vite
缓存目录。
浏览器缓存
已预构建的依赖请求使用 HTTP 头 max-age=31536000, immutable
进行强缓存,以提高开发期间页面重新加载的性能。一旦被缓存,这些请求将永远不会再次访问开发服务器。如果安装了不同版本的依赖项(这反映在包管理器的 lockfile 中),则会通过附加版本查询自动失效。如果你想通过本地编辑来调试依赖项,您可以:
- 通过浏览器开发工具的 Network 选项卡暂时禁用缓存;
- 重启 Vite 开发服务器指定
--force
选项,来重新构建依赖项; - 重新载入页面。
Vite构建Vue3项目
npm create vite@latest
目录结构
webpack项目的编译入口文件是main.js,配置文件是vue.config.js;而我们用Vite构建的项目的编译入口文件是index.html,配置文件是vite.config.js。其他的目录结构基本和webpack构建的项目是一致的。
vite.config.js配置文件
插件
vite使用一些三方依赖基本都是以plugins插件的方式引入的。
例如在webpack的项目中,我们要使用svg相关的图标,我们是通过引入loader的方式来使用的,然后在vue.config.js配置文件里设置svg-sprite-loader。
然而在vite里是通过集成插件的方式来使用svg图标的,我们得先安装vite-plugin-svg-icons的依赖,然后在plugins数组里,设置要缓存的svg图标的目录。
构建过程
Vite一开始会去加载index.html文件,然后通过script标签加载main.js,这其实和Vite的编译特性有关,传统的例如webpack的构建打包方式,启动必须优先抓取并构建你的整个应用,然后才能提供服务。这样的打包方式有一个明显的缺点,就是重建整个包的效率很低。而且更新速度会随着应用体积增长而直线下降。但在 Vite 中,当编辑一个文件时,Vite支持动态模块热替换(HMR),允许一个模块 “热替换” 它自己,而不会影响页面其余部分,使得无论应用大小如何,HMR 始终能保持快速更新。