这是我参与「第四届青训营」笔记创作活动的第12天
01 浅谈构建工具
- 前端工程的痛点
- 模块化
- 资源编译
- 浏览器的标准赶不上前端开发者的速度
- 产物质量
- 实际开发中需要代码压缩
- 需要兼容到Android 4.4,兼容低端浏览器
- 开发效率
- 前端构建工具的意义
- 模块化方案
- 提供模块加载方案
- 兼容不同模块规范
- 语法转译
- 高级语法转译,如Sass、TypeScript
- 资源转译,如图片、字体、worker
- 产物质量
- 产物压缩
- 删除无用代码
- 语法降级
- 开发效率
- 实现热更新
- 模块化方案
- 传统构建工具
- 存在的问题
- 启动缓慢,项目编译等待成本高
- 热更新缓慢,修改代码后不能实时更新
- 瓶颈
- 打包带来的性能开销
- JavaScript 语言的性能瓶颈
- 存在的问题
- 两大行业趋势
- 浏览器普遍支持原生ESM
- script标签增加
type="module"属性 - 使用ESM模块导入导出语法
- script标签增加
- 基于原生语言(
GoRust等)编写的前端编译工具链兴起- Go语言编写的
Esbuild,性能极高,在Vite中频繁使用- 打包器Bundler
- 编译器Transformer
- 压缩器Minifier
- Rust编写的
SWC,对标babel,速度可以快几十倍
- Go语言编写的
- 浏览器普遍支持原生ESM
02 Vite 概要介绍
- 新一代前端构建工具
- 组成部分
- No-bundle 开发服务,源文件无需打包
- 生产环境基于Rollup的Bundler
- 核心特征
- 高性能,dev启动速度和热更新速度快
- 简单易用,开发者体验好
- 基于原生ESM的开发服务优势
- 无需打包项目源代码
- 天然按需加载
- 可以利用文件级的浏览器缓存
- 内置的Web构建能力
- webpack webpack-dev-server
- webpack的各种loader
- webpack的各种plugin
03 Vite 上手实战
- 项目初始化
- 安装pnpm
npm i -g pnpm - 初始化命令
pnpm create vite - 安装依赖
pnpm install - 启动项目
npm run dev - 输入初始化参数:项目名称、框架等
- 安装pnpm
// package.json
{
"scripts": {
// 开发环境下运行的命令
"dev": "vite",
// 生产环境下运行的命令
// vite使用的esbuild默认不支持TypeScript的类型,需要调用tsc进行类型检查
"build": "tsc && vite build",
// 预览生产环境产物内容
"preview": "vite preview"
}
}
- 使用Sass/Scss & CSS Modules示例
- 安装Sass
pnpm i sass -D - 编写index.tsx
- 编写index.module.scss
- 安装Sass
// index.tsx
import styles from './index.module.scss'
// 使用CSS Modules模块化方案{styles.header} 防止类名冲突和样式污染
export function Header() {
return <p className={styles.header}>I'm not header </p>
}
// index.module.scss
// 与css只能平铺相比,scss 可以嵌套编写子元素样式
.header {
color: blue;
}
- 使用静态资源
import reactLogo from './assets/react.svg'
function App(){
//...
return {
<img src={reactLogo}/>
}
}
- 使用HMR
- 模块热替换
- vite默认自动配置
- HMR可以保存组件的局部状态
- 使用生产环境Tree-shaking
- 在构建阶段删除无用代码
- vite默认自动配置
- 依赖rollup实现
- 基于ESM的
import/export语句依赖关系,与运行时状态无关 - CommonJS格式不能做到Tree-shaking,只能用于ESM
- 如果
require的部分依赖运行时计算的结果,使用Tree-shaking会有一定风险
04 Vite 整体架构
- 依赖预打包
- 为什么要进行依赖预打包?
- 避免node_modules过多的文件请求
- 将CommonJS格式转换为ESM格式
- 实现原理
- 服务启动前扫描代码中用到的依赖
- 用esbuild对依赖代码进行预打包
- 改写import语句,指定依赖为预构建产物路径
- 为什么要进行依赖预打包?
- 单文件编译
- 用esbuild编译ts/jsx
- 优势
- 快
- 局限性
- 不支持类型检查
- 不支持语法降级到ES5
- 代码压缩
- 默认压缩工具是esbuild,替换传统的
terseruglify.js等
- 默认压缩工具是esbuild,替换传统的
- 插件机制
- 开发阶段: 模拟Rollup插件机制
- 生产环境: 直接使用Rollup
- vite和rollup间的插件兼容性文档
05 Vite 进阶路线
- 深入vite依赖的两个引擎
- esbuild
- rollup.js
- 推荐学习顺序
- 了解基本使用
- 动手尝试各项常用配置
- 学习插件开发
- Vite插件开发
- Why plugin?
- 抽离核心逻辑
- 易于拓展
- Vite各阶段钩子
- 服务启动阶段
- 请求响应阶段
- 热更新阶段
- 服务关闭阶段
- 常见钩子
- config
- resolveId
- load
- transform
- Why plugin?
- 代码分割(拆包)
- 拆包前
- 只有一个bundle文件
- 无法进行并发请求
- 缓存复用率低
- vite的拆包依赖rollup的打包功能
- 拆包前
- 语法安全降级
- 如IE11不支持
Promise语法 - 上层解决方案:
@vitejs/plugin-legacy - 底层原理
- 借助Babel进行语法自动降级
- JavaScript编译工具
- 出现原因
- JavaScript语法标准繁多,浏览器支持程度不一
- 使用高级语法的需求
- 提前注入polyfill实现
- 如
core-jsregenerator-runtime
- 如
- 借助Babel进行语法自动降级
- 如IE11不支持
- 服务端渲染(SSR)
- 提升首屏性能和进行SEO优化
- 深入了解底层标准
- CJS规范
- ESM规范
- HTTP 2.0特性