Vite知识体系
浅谈构建工具
前端为什么需要构建工具
前端项目的核心要素--资源
- JS
- TS
- JSX
- CSS
- SCSS
- LESS
- PNG
- JPEG
- WEBP
- ...
前端功能化的问题
-
模块化
- 常用规范:ESM,CommonJS,UMD
-
资源编译:高级语法的编译
-
产物质量:代码体积,代码性能,产物的语法兼容性
-
开发效率:热更新
前端构建工具的意义
-
解决模块化的问题
- 提供模块加载方案
- 兼容不同模块规范
-
语法转译
- 高级语法转译,如Sass、TypeScript
- 资源加载,如图片、字体、worker
-
解决产物质量的问题
- 产物压缩、无用代码删除、语法降级
-
解决开发效率的问题
- 提供一个热更新的系统来支持改动代码后立即可以看到代码效果
传统构建工具存在的问题
-
缓慢的启动
- 项目编译等待成本高
-
缓慢的热更新
- 修改代码后不能实时更新
瓶颈
- bundle带来的性能开销
- JavaScript语言的性能瓶颈
两大行业趋势
-
全球浏览器对原生ESM的普遍支持(92%以上)
-
两大要素
-
script标签增加type="module"属性
-
使用ESM模块导入导出语法
import{ xxx } from './xx.js'export const xxx='';
-
-
-
基于原生语言(Go, Rust)编写前端编译工具链
例如,Go语言编写的Esbuild,Rust编写的SWC
-
Esbuild -- 基于Golang开发的前端工具,具备如下能力:
- 打包器 Bundler
- 编译器 Transformer
- 压缩器 Minifier
-
性能极高,在Vite中被深度使用
-
基于原生ESM的开发服务优势
- 无需打包项目源代码
- 天然的按需加载
- 可以利用文件级的浏览器缓存
Vite概要介绍
-
定位:新一代前端构建工具
-
两大组成部分:
- No-bundle开发服务,源文件无需打包
- 生产环境基于Rollup的Bundler
-
核心特征
- 高性能,dev启动速度和热更新速度非常快
- 简单易用,开发者体验好
-
内置的Web构建能力
Vite 开箱即用的功能等价于
- webpack
- webpack-dev-server
- css-loader
- style-loader
- less-loader
- sass-loader
- postcss-loader
- file-loader
- MiniCssExtractPlugin
- HTMLWebpackPlugin
- ......
Vite上手实战
项目初始化
-
三个命令
"scripts": { "dev": "vite",//开发阶段的启动 "build": "tsc -b && vite build",//生产环境下进行项目的打包 "preview": "vite preview"//生产之后显示出产物的内容 }
使用Sass/Scss & CSS Modules
使用静态资源
除了常见的图片格式, Vite也内置了对于JSON, Worker, WASM资源的加载支持
使用HMR
HMR
是 Hot Module Replacement(热模块替换)的缩写,这是一种在开发过程中无需刷新整个页面就能更新应用的技术。HMR 允许开发者在修改代码后,只重新加载那些发生变化的部分,从而极大地提高了开发效率和用户体验。
主要特点:
- 即时更新:开发者在保存文件后,浏览器会自动更新界面,无需手动刷新页面。
- 状态保持:在模块热替换过程中,应用的状态和用户输入可以保持不变。
- 开发效率:开发者可以立即看到代码更改的效果,加快了开发和调试的速度。
- 资源优化:只加载变化的部分,减少了数据传输,节省了带宽和加载时间。
HMR 在不同工具和框架中的应用:
- Webpack:Webpack 提供了 HMR 的支持,可以在开发服务器配置中启用。Webpack 会监听文件变化,并只重新打包那些发生变化的模块。
- React:在使用 Create React App 创建的项目中,默认支持 HMR。当你修改组件后,只有该组件会被重新加载。
- Vue:Vue CLI 也支持 HMR,允许开发者在开发过程中享受热替换的便利。
HMR可以保存组件的局部状态
生产环境Tree Shaking
Tree Shaking在Vite中无需配置, 默认开启
import React from 'react';
import styles from './index.module.scss'
import { add } from '../../util';
export function Header(){
return <div className={styles.header}>header{add(1, 2)}</div>
export const add = (a: number, b: number) => a + b;
export const sub = (a: number, b: number) => a - b;//打包时这段代码会被删除
CommonJS格式不能做到Tree Shaking:require的部分可能依赖运行时计算的结果
优化原理
- 基于ESM的import/export语句依赖关系, 与运行时状态无关
- 在构建阶段将未使用到的代码进行删除
Vite整体架构
依赖预打包
为什么要进行预打包?
- 避免 node_modules 过多的文件请求
- 将 CommonJS 格式转换为 ESM 格式
实现原理:
- 服务启动前扫描代码中用到的依赖
- 用 Esbuild 对依赖代码进行预打包
- 改写 import 语句,指定依赖为预构建产物路径
// 改写前
import React from "react";
// 改写后
import React from '/node_modules/.vite/react.js'
单文件编译
用 Esbuild 编译 TS/JSX
-
优势:
- 编译速度提升10-100x
-
局限性:
- 不支持类型检查
- 不支持语法降级到ES5 放到低版本浏览器会报错
代码压缩
Esbuild作为默认压缩工具,替换传统的Terser,Uglify.js等压缩工具
插件机制
- 开发阶段 -> Plugin Pipeline模拟Rollup 插件机制
- 生产环境 -> 直接使用Rollup
插件兼容性具体可查阅: vite-rollup-plugins.patak.dev/
Vite进阶路线
深入双引擎
esbuild
esbuild 是一个高性能的构建工具,用于打包 JavaScript、TypeScript 和其他语言的代码。它以其快速的构建速度和简洁的API而闻名。esbuild 自动处理许多现代前端开发中的常见任务,如代码压缩、树摇(tree-shaking)、代码分割和最小化。
特点:
- 快速构建:
esbuild的构建速度非常快,通常比传统的打包器如Webpack快得多。 - 单一二进制文件:
esbuild是一个单一的二进制文件,不需要额外的构建依赖。 - 多种语言支持:除了JavaScript和TypeScript,
esbuild还支持多种语言,包括JSX、TSX和其他预处理器。 - 插件系统:
esbuild允许开发者通过插件扩展其功能。 - 跨平台:
esbuild支持Windows、macOS和Linux。
参考资料
esbuild - 快速入门 | esbuild中文文档 | esbuild中文网
Rollup.js
Rollup 是一个模块打包器,用于将小块代码合并成更大的代码包,特别是用于库和框架的打包。它特别适合用于打包那些需要在多个环境中运行的JavaScript库,因为Rollup可以生成各种模块格式的代码,如ES6、UMD、CommonJS等。
特点:
- 树摇:Rollup 支持树摇,这意味着它只会包含最终打包文件中实际用到的代码,从而减少文件大小。
- 多种模块格式:Rollup 支持多种模块格式,使得打包后的代码可以在不同的环境中运行。
- 插件系统:Rollup 拥有一个强大的插件系统,允许开发者添加新的功能,如支持新的语法或转换代码。
- 代码分割:Rollup 支持代码分割,可以将代码拆分成多个文件,按需加载。
- 兼容性:Rollup 支持各种JavaScript版本,包括ES6+。
参考资料
简介 | rollup.js 中文文档 | rollup.js中文网 (rollupjs.com)
推荐学习顺序:
- 先了解基本使用,动手尝试各项常用配置;
- 然后学习其插件开发。
Vite插件开发
为什么需要插件机制
- 抽离核心逻辑
- 易于拓展
插件的钩子
"钩子"(Hook)
是一种特殊类型的函数,它允许用户或开发者介入或“钩住”某个事件、操作或程序的执行流程。钩子函数通常被用于拦截和修改系统或库的正常行为,以便在特定事件发生时执行自定义的代码。
钩子的作用
- 扩展功能:允许插件扩展宿主应用程序的功能,而不需要修改宿主应用程序的代码。
- 自定义行为:允许用户或开发者根据需要自定义应用程序的行为。
- 拦截和修改:在数据被处理或保存之前,钩子可以拦截和修改这些数据。
- 调试和日志记录:钩子可以用来插入调试代码或记录日志,帮助开发者理解应用程序的运行流程。
通过上述的Hook,我们可以在不同的构建阶段插入自定义的逻辑
例子
- 开发Vite插件
- 配置文件引入插件
//Vite插件示例
const fileRegex = /. (my-file-ext)$/
export default function myPlugin() {
return {
name: 'transform-file',
transform(src, id) {
if (fileRegex.test(id)) {
return {
code: compileFileToJS(src),
map: null //如果可行将提供 source map
}
}
}
}
}
//使用插件
// vite.config.js
import plugin from './myPlugin'
export default defineConfig({
plugins: [plugin()]
})
参考资料
插件 API | Vite 官方中文文档 (vitejs.dev)
- 复杂度较低的插件:vite/packages/vite/src/node/plugins/json.ts at main · vitejs/vite · GitHub
- 复杂度中等的插件:File not found (github.com)
- 复杂度较高的插件:github.com
代码分割(拆包)
-
拆包前
-
问题
- 无法进行并发请求
- 缓存复用率低
-
拆包后
JS编译工具(Babel)
-
出现的原因
- JavaScript 语法标准繁多,浏览器支持程度不一
- 开发者需要用到高级语法
-
原理
语法安全降级
例子
-
以Promise语法为例,IE11没有支持
-
如何在构建产物中避免这类问题?
-
底层原理
- 借助Babel进行语法自动降级
- 提前注入Polyfill 实现,如 core-js、regenerator-runtime
-
参考资料
服务端渲染(SSR)
一种常见的渲染模式,用于提升首屏性能和SEO优化
-
参考文献
深入了解底层标准
Vite社区生态
- Github 40k+star(可参考webpack 61.3 K,rollup 21.8k),并且目前还在持续维护
-
官方提供插件:
- @vitejs/plugin-vue,提供Vue 3 支持
- @vitejs/plugin-vue-jsx,提供 Vue 3 JSX 支持
- @vitejs/plugin-react,提供 React 支持
- @vitejs/plugin-legacy,提供低版本浏览器降级支持
- 海量社区插件 github.com/vitejs/awes…
-
被众多框架内置
- Nuxt
- SvelteKit
- Astro
- Vitepress