1.Vite的基本概要
1.为什么需要构建工具?
前端工程的痛点:
- 模块化:ESM、CommonJS、UMD
- 资源编译:高级语法的编译
- 产物质量:代码体积、代码性能
- 开发效率:热更新
2.前端构建工具的意义
3.什么是 Vite ?
定位:新一代前端构建工具,可以显著改善开发体验
两大组成部分:
- 一个开发服务器:No-bundle 开发服务,源文件无需打包
- 一套构建指令:生产环境基于 Rollup 的 Bundler
核心特征:
- 高性能,dev 启动速度和热更新速度非常快(比较重的框架安装速度也非常快)
- 功能丰富,简单易用,开发者体验好
3.1深入Vite:浅谈ESM的高阶特性
前言:总所周知,Vite是借助浏览器原生的ESM解析能力(type = "module")实现了开发阶段的 no-bundle,即不用打包也可以构建Web应用。当然,ESM也不仅仅是一个模块规范,还代表了前端社区生态的走向以及各项前端基础设施的未来。
1.高阶特性:
1.import map
在浏览器中我们可以使用包含type="module"属性的script标签来加载 ES 模块,而模块路径主要包含三种:
- 绝对路径,如 cdn.skypack.dev/react
- 相对路径,如./module-a
- bare import即直接写一个第三方包名,如react、lodash
对于前两种模块路径浏览器是原生支持的,而对于 bare import,在 Node.js 能直接执行,因为 Node.js 的路径解析算法会从项目的 node_modules 找到第三方包的模块路径,但是放在浏览器中无法直接执行。而这种写法在日常开发的过程又极为常见,除了将 bare import 手动替换为一个绝对路径,还可以通过现代浏览器内置的 import map 解决上述的问题。
2.Nodejs包导入导出策略
在Node.js 中(>=12.20版本) 有一般如下几种方式可以使用原生 ES Module:
- 文件以 .mjs 结尾
- package.json 中声明 type:"module";
那么,Node.js在处理 ES Module 导入导出的时候,如果是处理 npm 包级别的情况,其中的细节可能比你想象中更加复杂。
导出:
首先来看看如何导出一个包,你有两种方式可以选择: main 和 exports 属性。这两个属性均来自于package.json,并且根据 Node 官方的 resolve 算法,exports 的优先级高于 main ,也就是说如果你同时设置了这两个属性,那么 exports会优先生效。并且,main 的使用比较简单,设置包的入口文件路径即可; export 属性就会更复杂些,包含了多种导出形式:默认导出、子路径导出 和 条件导出
导入:
导入包一般都是使用 package.json中的 imports 属性
2.Pure ESM
Pure ESM: 一方面是指让 npm 包都提供 ESM 格式的产物,另一方面是仅留下 ESM 产物,抛弃 CommonJS 等其他格式的产物
由于npm 上大部分的包还是属于基础库的范畴,那对于大部分包,我们采用导出ESM/CommonJS 两种产物的方案,会不会对项目的语法产生限制呢?
首先,在 ESM 中无法使用 CommonJS 中的 __dirname、__filename、require.resolve 等全局变量和方法,同样的,在 CommonJS 中我们也没办法使用 ESM 专有的 import.meta对象,那么如果要提供两种产物格式,这些模块规范相关的语法怎么处理呢?
在传统的编译构建工具中,我们很难逃开这个问题,但新一代的基础库打包器tsup给了我们解决方案。
3.新一代基础库打包器
tsup : 一个基于 Esbuild 的基础库打包器,主打无配置(no config)打包。借助它我们可以轻易地打出 ESM 和 CommonJS 双格式的产物,并且可以任意使用与模块格式强相关的一些全局变量或者 API,比如某个库的源码如下:
export interface Options {
data: string;
}
export function init(options: Options) {
console.log(options);
console.log(import.meta.url);
}
由于代码中使用了 import.meta 对象,这是仅在 ESM 下存在的变量,而经过 tsup 打包后的 CommonJS 版本却被转换成了下面这样:
var getImportMetaUrl = () =>
typeof document === "undefined"
? new URL("file:" + __filename).href
: (document.currentScript && document.currentScript.src) ||
new URL("main.js", document.baseURI).href;
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
// src/index.ts
function init(options) {
console.log(options);
console.log(importMetaUrl);
}
可以看到,ESM 中的 API 被转换为 CommonJS 对应的格式,反之也是同理。最后,我们可以借助之前提到的条件导出,将 ESM、CommonJS 的产物分别进行导出,如下所示。
{
"scripts": {
"watch": "npm run build -- --watch src",
"build": "tsup ./src/index.ts --format cjs,esm --dts --clean"
},
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js",
// 导出类型
"types": "./dist/index.d.ts"
}
}
}
🤔tsup 在解决了双格式产物问题的同时,本身利用 Esbuild 进行打包,性能非常强悍,也能生成类型文件,同时也弥补了 Esbuild 没有类型系统的缺点,推荐使用~
4.当下构建工具的问题在哪?
缓慢的启动 -> 项目编译等待成本高
缓慢的热更新 -> 修改代码后不能实时更新
开发体验问题日渐显露!
🤔瓶颈在哪?
5.两大行业趋势
- 全球浏览器对原生 ESM(原生浏览器支持 import 和 export 等 ES6 特性)的普遍支持(目前占比92% 以上)
- 基于原生语言(Go、Rust)编写前端编译工具链的兴起
- 如 Go 语言编写的 Esbuild、Rust 编写的 SWC
6.浏览器原生 ESM 支持
两大要素:
7.基于原生 ESM 的开发服务优势
✅无需打包项目源代码
✅天然的按需加载
✅可以利用文件级的浏览器缓存
8.基于 Esbuild 的编译性能优化
Esbuild -- 基于 Golang 开发的前端工具,具备如下能力:
- 打包器 Bundler
- 编译器 Transformer
- 压缩器 Minifier
9.内置的 Web 构建能力
2.Vite 上手实战
- 项目初始化
- 创建 vite 项目
- 输入初始化参数(文件名等配置)
- 启动项目
- 使用 Sass / Scss & CSS Modules
Sass :是采用 Ruby 语言编写的一款 CSS 预处理语言,是最大的成熟的 CSS 预处理语言
SASS 和 SCSS 的区别
Sass 和 SCSS 其实是同一种东西,我们平时都称之为 Sass,两者之间不同之处有以下两点:
- 文件扩展名不同。
Sass 是以“.sass”后缀为扩展名,而 SCSS 是以“.scss”后缀为扩展名。
- 语法书写方式不同。
Sass 是以严格的缩进式语法规则来书写,不带大括号( { } )和分号( ; ),而 SCSS 的语法书写和我们的 CSS 语法书写方式非常类似。
🔗Sass转css在线网址
❗ 注意:使用scss.module,可以做到不同组件的样式隔离,避免样式污染,同时,scss 中还支持样式嵌套,能提高开发效率
- 使用静态资源
除了常见的图片格式,Vite也内置了对于 JSON、Worker、WASM 资源的加载支持
🔗Vite官方文档-静态资源处理
- 使用 HMR :无需额外配置,自动开启
- 生产环境 Tree Shaking :无需配置,自动开启
优化原理:
- 基于 ESM 的import / export 语句依赖关系(静态语法),与运行时状态无关
- 在构建阶段将未使用到的代码进行删除
❗ 注意:
Vite的直观特点:响应迅速、开箱即用😉
3.Vite 的整体架构
关键技术:依赖预打包
为什么要进行预打包?
1.避免 node _ modules 过多的文件请求
2.将 CommonJS 格式转换为 ESM 格式
实现原理:
1.服务启动前扫描代码中用到的依赖
2.用 Esbuild 对依赖代码进行预打包
3.改写 import 语句,指定依赖为预构建产物路径
//改写前
import React from "react";
//改写后
import React from '/node_modules/.vite/react.js';
关键技术:单文件编译
用 Esbuild 编译 TS/JSX
优势:编译速度提升 10-100x
局限性:
关键技术:代码压缩
Esbuild 作为默认压缩工具,替换传统的 Terser、Uglify.js 等压缩工具
关键技术:插件机制
开发阶段 -> 模拟 Rollup 插件机制
生产环境 -> 直接使用 Rollup
🔗插件兼容性
4.Vite 进阶路线
1.深入双引擎
- Esbuild
- Rollup
📘参考资料:
Esbuild官方文档
Rollup官方文档
2.Vite 插件开发
1.为什么需要插件机制?
- 抽离核心逻辑
- 易于拓展
推荐学习顺序:
- 先了解基本使用,动手尝试各项常用配置
- 然后学习插件开发(可以详细看一看Rollup插件开发的章节)
通过上述的 Hook(钩子函数),我们可以在不同的构建阶段插入自定义的逻辑
重点掌握:
config、resolveId、load、transform
2.插件开发
📘参考资料:
Vite插件开发文档
复杂度较低的插件:json加载插件
复杂度中等的插件:Esbuild接入插件
复杂度较高的插件:官方React插件
📋 建议:先看文档,过一遍钩子插件的功能,然后自动多动手去写,多学习其他插件的实现,掌握套路(模仿 + 总结规律)
3.代码分割(拆包)
拆包前:
问题:
- 无法进行并发请求
- 缓存复用率低
拆包后:
参考资料:
构建选项-Vite官方文档
构建选项-rollup官方文档
4.JS编译工具(Babel)
原理:
出现原因:
- JS 语法标准繁多,浏览器支持程度不一
- 开发者需要用到高级语法
📘参考资料:
babel官方站点
babel插件手册(学习插件开发推荐去看)
5.语法安全降级
如何在构建产物中避免这类问题?
上层解决方案:@vitejs/plugin-legacy
底层原理:
- 借助 Babel 进行语法自动降级
- 提前注入 Polyfill 实现,如 core-js、regenerator-runtime
📘参考资料:
@babel/preset-env 文档
Vite官方降级文档
6.服务端渲染(SSR)
一种常见的渲染模式,用于提升首屏性能和 SEO 优化
构建阶段:
代码执行阶段:
📘参考资料:
Vite-SSR文档
Vite官方ssr-demo项目
使用Vite搭建ssr工程
7.深入了解底层标准
重点规范:
- CJS 规范
- ESM 规范
- HTTP 2.0 特性
📘参考资料:
链接1
链接2
什么是PureESM?
8.Vite 社区生态
🔨官方提供插件:
@ vitejs / plugin - vue ,提供 Vue 3支持
@ vitejs / plugin - vue - jsx ,提供 Vue 3 JSX 支持
@ vitejs / plugin - react ,提供 React 支持
@ vitejs / plugin - legacy ,提供低版本浏览器降级支持
💥 海量社区插件:插件