前言
vite的出现原因就不多赘述了,就是模块化的发展响应的产物。
如果说,webpack的出现是为了解决模块化打包的问题,那vite的出现可以说是,借助了官方的模块化规范的基础上去实现更好的开发体验。 这里我们再简单回顾下模块化发展:
JavaScript 在最初并没有模块化的概念,但随着前端业务复杂度的增加,模块化逐渐变得至关重要。于是,社区涌现了多种模块化方案,这些方案在相互借鉴的同时也引发了许多争议,形成了多个流派。从最初的 CommonJS 到 ES6 推出的 ES Modules 规范,经过不断的讨论和演变,最终 ES Modules 成为前端开发的重要基础设施。
- CommonJS:现主要用于Node.js(Node@13.2.0开始支持直接使用ES Module)
- AMD:
require.js依赖前置,市场存量不建议使用 - CMD:
sea.js就近执行,市场存量不建议使用 - ES Module:ES语言规范,标准,趋势,未来
那由于 ES Module 是未来,是趋势,是大家都共同去遵循的规范,也就是现代浏览器都支持。在此基础上,更好的打包工具 vite 诞生,Vite依靠浏览器对 ES Module 规范的实现来提升开发阶段的体验和效率。
为什么需要vite?vite解决什么问题?
这几年,打包构建工具层出不穷,我们比较熟悉的有gulp rollup webpack vite 等等,但下载量最大的还是webpack。
那在vite出现之前,这些打包构建工具都有什么问题亟待解决呢?
当前常见的构建工具如 Webpack,主要通过抓取、编译和构建整个应用的代码(即打包过程),生成一份经过编译和优化,能够良好兼容各大浏览器的生产环境代码。在开发环境中,流程大体相同:首先将整个应用进行构建打包,然后将打包后的代码交给 dev server(开发服务器)进行处理。
Webpack 等构建工具的出现极大地简化了前端开发流程,但随着前端业务的复杂性增加,JS 代码量呈指数增长,打包构建时间也不断加长,导致 dev server(开发服务器)在性能上遇到了瓶颈:
- 服务启动缓慢:在大型项目中,
dev server启动时间可能长达几十秒甚至几分钟。 - HMR 热更新缓慢:即便启用了 HMR 模式,随着应用规模的增加,热更新速度也显著变慢,性能瓶颈日益显现,优化空间有限。
缓慢的开发环境显著降低了开发者的工作体验和效率,在这种背景下,Vite 应运而生。
什么是vite?
就是在开发环境依赖 esbuild,生产环境依赖 rollup,同时借助浏览器ESM的编译功能实现开发效率大幅提升的新一代打包构建工具。
说人话:就是相比别的打包工具,开发环境的编译快的一批,因为它不需要打包了。
先介绍一些概念:
- 依赖:指的是开发过程中不会频繁变动的部分(如 npm 包、UI 组件库等),这些部分会通过 esbuild 进行预构建。
- 源码:指的是浏览器无法直接执行的非 JavaScript 代码(如 .jsx、.css、.vue 等文件)。Vite 只会在浏览器请求相关源码时进行转换,提供 ESM 格式的源码。
在开发环境:
利用浏览器原生的 ESM(ES Modules)编译能力,Vite 省去了繁琐的编译过程,直接将开发环境的源码交给浏览器。dev server 只需提供轻量级的服务。在浏览器执行 ESM 的 import 时,它会向 dev server 发起 AJAX 请求,服务器会对源码进行简单处理后返回给浏览器。
Vite 的热模块替换(HMR)是基于原生 ESM 实现的。当我们编辑一个文件时,Vite 只需精准地使已编辑的模块失效,从而确保无论应用多大,HMR 始终能够保持快速更新。
通过使用 esbuild 来处理项目的依赖,esbuild 是用 Go 语言编写的,比基于 Node.js 的编译器速度更快几个数量级。
在生产环境: Vite 集成了 Rollup 来打包生产环境代码,依赖 Rollup 生态系统的成熟稳定性以及其简洁的插件机制。
优势和不足:
优势
- 极速开发体验: 快速启动、快速加载、快速更新!
- 开箱即用: 高度集成,无需额外配置即可投入使用。
- 高效热更新: 基于原生 ESM,实现无需打包编译的极速热更新。
- 依赖预处理: 采用
esbuild进行依赖预构建,性能相比基于 Node.js 的编译器(如 Webpack)提升了几个数量级。 - 插件支持: 兼容 Rollup 的插件生态,插件开发更加简洁高效。
- 框架无关性: 虽然对 Vue 支持最佳,但同样支持 React 等其他框架,是一个独立的构建工具。
- 内置功能丰富: 提供开箱即用的 SSR 支持,并且对 TypeScript 具备天然支持。
不足
- Vue 优先支持: 由于为 Vue 量身打造,编译插件的优化主要针对 Vue,对 React 的支持稍显逊色。
- 生产环境实践较少: 尽管 2.0 正式版已发布并可用于生产环境,但市场实际使用案例相对较少。
- 开发与生产的不一致: 开发环境基于原生 ESM,生产环境集成 Rollup 打包,导致开发环境与最终生产代码执行逻辑存在差异。
vite运行流程
Webpack 是完整打包整个应用,再把打包后的代码产物提供给 dev server,这样浏览器才能访问 dev server 的资源。
Vite 将源码直接交付给浏览器,使开发服务器 dev server 能够秒级启动。当浏览器需要加载某个模块以显示页面时,会向开发服务器发起请求。服务器 dev server 对该模块进行简单处理后(编译处理.vue .jsx的文件资源),将其返回给浏览器,从而实现真正的按需加载。
Vite 的处理流程可以分为两大阶段:开发环境和生产环境。下面是 Vite 在这两个阶段的详细处理流程:
开发环境处理流程
在开发环境中,Vite 主要依靠浏览器原生的 ESM(ES Modules) 特性,避免了传统构建工具中的繁重编译和打包过程。具体步骤如下:
1. 启动 Vite 开发服务器
- 启动 Vite 时,Vite 启动一个开发服务器
dev server,通过该服务器提供给浏览器需要的源码。 - 开发服务器并不直接提供所有文件,而是采用按需加载的方式,确保开发过程的快速响应。
2. 请求模块
- 当浏览器执行
import或export时,浏览器会发送 ESM 请求 到dev server,请求某个模块的源码(例如.js、.ts、.vue等文件)。 - 请求的模块可能包含依赖,这些依赖会继续通过
dev server被动态请求。
3. 处理源码
-
非 JavaScript 代码:(如
.jsx、.css、.vue等),Vite 会在浏览器请求相关文件时进行即时转换。这种转换是由 Vite 内置的esbuild实现的,esbuild会快速将这些文件转换为浏览器可以执行的 ESM 格式。 -
依赖:对于项目中的静态依赖(如 npm 包或 UI 组件库),Vite 会利用
esbuild在启动时进行预构建,将这些依赖转换为兼容的 ESM 格式。
4. 模块热更新 (HMR)
- 当开发者修改文件时,Vite 会触发 热模块替换(HMR) 。由于 Vite 基于原生 ESM 实现 HMR,只会重新加载发生变化的模块,而不是整个应用。
- Vite 在模块级别进行精准更新,这意味着它只会使编辑过的模块失效,确保更新保持快速,无论应用的大小。
5. 使用 esbuild 处理依赖
- Vite 使用
esbuild进行项目依赖的处理,esbuild作为一个基于 Go 的编译器,比传统的基于 Node.js 的编译器速度快上几个数量级。它用于预构建依赖,以提高启动速度和模块加载效率。
生产环境处理流程
在生产环境中,Vite 主要依赖 Rollup 来处理代码的打包和优化。具体步骤如下:
1. 集成 Rollup 打包
- 当你准备打包生产环境代码时,Vite 使用 Rollup 来进行最终的代码打包。Rollup 是一个非常高效的 JavaScript 打包工具,尤其适合处理模块化的代码。
- Rollup 会优化并合并所有的模块,生成一个最终的生产环境代码,这些代码可以在各种浏览器中高效运行。
2. 代码优化
-
在打包过程中,Vite 会进行多种优化,例如:
- 树摇(Tree Shaking) :移除无用代码,减少最终打包后的文件体积。
- 代码分割(Code Splitting) :将应用分割成多个更小的文件,按需加载,提高加载速度。
- 插件处理:Vite 利用 Rollup 插件机制进行进一步的代码优化、压缩和兼容性处理。
3. 生成生产环境代码
最终,Vite 会生成经过优化和打包后的生产环境代码,通常包括:
- 压缩后的 JavaScript 文件。
- 优化后的静态资源(如 CSS、图片等)。
- 可以直接在浏览器中高效运行的代码。
在开发环境,Vite 利用浏览器原生的 ESM 和 esbuild,省去了繁琐的打包步骤,直接为浏览器提供原生的模块化代码,支持按需加载和快速的热模块替换(HMR)。而在生产环境:Vite 集成了 Rollup 进行最终的代码打包,进行多种优化,如代码分割、树摇、插件支持等,生成适合生产环境的高效代码。
这个流程的关键是 Vite 的开发环境通过浏览器原生的 ESM 机制来实现快速开发,而生产环境则依赖 Rollup 进行高效的打包和优化。
怎么使用vite?
废话不多说,直接上配置
vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue'; // 示例中使用 Vue 框架
import viteCompression from 'vite-plugin-compression'; // 用于 gzip 压缩
import path from 'path';
export default defineConfig({
// 项目根目录
root: './',
// 插件配置
plugins: [
vue(),
viteCompression({
algorithm: 'gzip', // 使用 gzip 压缩
threshold: 10240, // 超过 10KB 的文件才会压缩
ext: '.gz', // 压缩文件的扩展名
}),
],
// 开发服务器配置
server: {
port: 3000, // 指定开发服务器端口
open: true, // 自动打开浏览器
hmr: {
overlay: false, // 禁用错误的全屏覆盖提示
},
},
// 依赖优化配置
optimizeDeps: {
include: ['axios', 'lodash'], // 强制预构建的依赖
exclude: ['some-large-lib'], // 排除不需要预构建的依赖
},
// 静态资源处理
assetsInclude: ['**/*.gltf', '**/*.glb'], // 自定义资源类型
build: {
target: 'esnext', // 使用现代浏览器的最新语法
minify: 'esbuild', // 使用 esbuild 进行压缩,速度更快
sourcemap: false, // 关闭生产环境的 sourcemap
assetsInlineLimit: 4096, // 小于 4KB 的资源内联为 Base64
// Rollup 相关配置
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
return 'vendor'; // 将第三方依赖打包到 vendor.js
}
},
entryFileNames: 'assets/[name].[hash].js',
chunkFileNames: 'assets/[name].[hash].js',
assetFileNames: 'assets/[name].[hash].[ext]',
},
},
},
// 路径别名
resolve: {
alias: {
'@': path.resolve(__dirname, './src'), // 配置 @ 指向 src 目录
},
},
});
配置详解
插件配置
- Vue 插件:支持 Vue.js 的开发。
- Gzip 压缩插件:压缩静态文件,减小生产环境的文件大小。
开发服务器优化
- 指定端口、自动打开浏览器。
- 禁用全屏错误覆盖,避免影响开发体验。
依赖预构建
- include:指定需要提前预构建的依赖库(如常用的第三方库)。
- exclude:排除某些大型库的预构建,按需加载。
静态资源处理
- 设置小于 4KB 的资源内联到 JavaScript 中。
- 指定自定义资源类型(如 3D 模型文件)。
生产环境优化
- 使用
esbuild进行代码压缩,提升构建速度。 - 关闭生产环境的 sourcemap,减小构建输出体积。
- 代码分割:将第三方依赖打包到单独的
vendor.js中。
路径别名
配置 @ 为路径别名,方便在代码中引用 src 目录下的文件。
使用上面的配置可以达到的优化效果:
在开发环境:预构建依赖提升启动速度;按需加载模块加快页面加载;开发服务器配置提高开发体验。
在生产环境:压缩和代码分割减少文件体积; 静态资源处理优化加载效率;文件名加哈希实现缓存优化。
核心原理
ESbuild 编译
esbuild 使用go编写,cpu密集下更具性能优势,编译速度更快,以下摘自官网的构建速度对比:
依赖预构建
-
模块化兼容: 如前文所述,目前依然存在多种模块化规范并存的情况。在预构建阶段,
Vite会将依赖中使用的各种模块化标准(如 CommonJS、UMD)转换为 ESM 格式,方便浏览器直接加载。 -
性能优化: 许多 npm 包内部使用 ESM 格式,含有大量的
import请求,这可能导致网络请求过多而造成拥堵。Vite借助esbuild工具,将这些复杂的 ESM 依赖关系打包成单一模块,减少网络请求次数,从而提升加载性能。
按需加载
- 服务器仅在收到
import请求时才会编译对应文件,并将 ESM 格式的源码返回给浏览器,从而实现真正的按需加载。
缓存
- HTTP 缓存: 为优化性能,充分利用 HTTP 缓存机制。对依赖(通常不会频繁更改的代码)启用
max-age和immutable的强缓存策略,而对源码部分采用 304 协商缓存策略,有效提升页面加载速度。 - 文件系统缓存: 在预构建阶段,
Vite会将构建后的依赖缓存至node_modules/.vite目录。只有在相关配置更改或手动触发时才会重新构建,进一步加快预构建的速度。
模块路径重写
浏览器的 import 语法仅支持相对路径或绝对路径,而开发中通常通过包名直接引入 node_modules 中的模块,因此需要进行路径转换,使浏览器能够正确加载。
- 使用
es-module-lexer扫描并解析import语句。 - 利用
magic-string对模块路径进行重写,确保兼容性和正确性。
// 开发代码
import { createApp } from 'vue'
// 转换后
import { createApp } from '/node_modules/vue/dist/vue.js'