这是我参与「第四届青训营 」笔记创作活动的的第十七天。 今天学习了Vite知识体系,通过昨天对Webpack的学习,我逐渐建立起了前端工程化的概念,所谓前端工程化,就是使用软件工程的思想应用到前端开发中,将开发阶段的代码发布到生产环境,包含:构建,分支管理,自动化测试,部署。这些都需要降低开发者的维护成本,提高页面的响应速度,给用户更好的体验。
本堂课重点内容
- 为什么需要Vite
- 什么是Vite
- Vite使用
- Vite整体架构
- Vite进阶
详细知识点介绍
详细笔记分享在文章最后,记得点个赞再走!
课后个人总结
通过今天的学习,我觉得vite最大的特点就是弥补了webpack的速度慢、配置繁琐、并且打包后的文件占用内存小,尽管 Webpack有大量的插件和社区支持,但是从很多方面来说,如今它算不上是最好的选择。Vite仍在发展,而且社区支持也是很丰富的,单单是基于配置方便这个优点,我觉得Vite完全是一个更好的选择。
笔记
Vite知识体系
一、为什么需要构建工具
前端工程的痛点
核心要素 ==> 资源
对于资源的处理,往往是很麻烦的事情。比如JS、TS、JXS、CSS、SCSS等资源文件。
解决
- 模块化(ESM、CommonJS、UMD)
- 资源编译(高级语法的编译)
- 产物质量(代码体积、代码性能)
- 开发效率(热更新)
前端构建工具的意义
-
模块化方案
- 1.提供模块加载方案
- 2.兼容不同模块规范
-
语法转译
- 1.高级语法转译,如Sass、TypeScript
- 2.资源加载,如图片、字体、worker
-
产物质量
- 产物压缩、无用代码删除、语法降级
-
开发效率
- 热更新
二、什么是Vite
1、Vite介绍
Vite新一代前端构建工具。
两大部分组成:
- No-bundle开发服务,源文件无需打包
- 生产环境基于Rollup的 Bundler
核心特性:
- 高性能,dev启动速度和热更新速度非常快
- 简单易用,开发者体验好
当下问题
当下打包工具,如parcel、webpack、rollup等,还是存在一些问题。
缓慢的启动 ==> 项目编译等待成本高
缓慢的热更新 ==> 修改代码后不能实时更新
这些问题的产生的原因在哪?
- bundle 带来的性能开销
- JavaScript语言的性能瓶颈
2、Vite特性
行业趋势
- 全球浏览器对原生ESM的普遍支持(目前占比92%以上)
- 基于原生语言(Go、Rust)编写前端编译工具链
- 如Go语言编写的Esbuild、Rust 编写的Swc
浏览器原生ESM支持
两大要素:
1.script标签增加type="module”属性
<script type= "module">
import { foo } from './foo .js 'console.log ( foo)
</ script>
2.使用ESM模块导入导出语法
export const foo = 'foo';
(1)基于原生ESM的开发服务优势
- 无需打包项目源代码
- 天然的按需加载
- 可以利用文件级的浏览器缓存
(2)基于Esbuild的编译性能优化
Esbuild,基于Golang开发的前端工具。具备如下能力:
1.打包器 Bundler
2.编译器Transformer
3.压缩器Minifier
所以,由于性能极高的特性,在vite中被深度使用。
(3)内置的Web构建能力
Vite开箱即用的功能相当于 webpack、webpack-dev-server、class-loader、style-loader、less-loader....
可以将几十行的webpack.config.js代码简化成几行的vite.config.js
三、Vite使用
1、项目初始化
# 提前安装 pnpm
npm i -g pnpm
# 初始化命令
pnpm create vite
# 安装依赖
pnpm install
# 启动项目
npm run dev
初始化参数:
启动后截图:
启动完成后,打开浏览器访问对应地址即可。
2、使用Sass/Scss & CSS Modules
- src目录结构
- 安装Sass
pnpm i sass -D
- index.tsx
import styles from ' ./index.module.scss' ;
//使用CSS Modules模块化方案,防止 className命名冲突
export function Header() {
return <p className={styles.header}>This is Header</p>
};
- index.module.scss
.header {
color: red;
}
3、使用Scss & CSS Module
- src目录结构
- 引入Header组件
import { Header } from " ./components/Header " ;
function App() {
return (
<div>
<Header/>
</div>
);
}
export default App;
4、使用静态资源
- src目录结构
- 以svg图片为例
import { Header } from " ./components/Header ";
import logoUrl from ' ./logo.svg ';
function App() {
return (
<div>
<Header />
<img src={logoUrl} alt=""/>
</div>
);
}
export default App;
- 除了常见的图片格式, Vite也内置了对于JSON、Worker、WASM资源的加载支持
[Vite内置资源加载] cn.vitejs.dev/guide/featu…
5、生产环境 Tree Shaking
优化原理:
1.基于ESM的import/export语句依赖关系,与运行时状态无关
2.在构建阶段将未使用到的代码进行删除
Tree Shaking在Vite中无需配置,默认开启。
6、Vite直观印象
- 响应迅速
- 开箱即用
四、Vite整体架构
1、依赖预打包
为什么要进行预打包
- 避免node_modules过多的文件请求
- 将CommonJS格式转换为ESM格式
实现原理
1.服务启动前扫描代码中用到的依赖
2.用Esbuild对依赖代码进行预打包
3.改写import语句,指定依赖为预构建产物路径
//改写前
import React from "react";
//改写后
import React from ' /node_modules /.vite/react. js'
2、单文件编译
用Esbuild 编译TS/JSX
-
优势:编译速度提升10-1000
-
局限性:
- 不支持类型检查
- 不支持语法降级到ES5
3、代码压缩
Esbuild 作为默认压缩工具,替换传统的Terser.Uglify.js等压缩工具
4、插件机制
- 开发阶段:模拟Rollup插件机制
- 生产环境:直接使用Rollup
五、Vite进阶路线
1、深入双引擎
- esbuild
- rollup.js
推荐学习顺序:先了解基本使用,手动尝试各项常用配置,然后学习其插件开发。
2、Vite插件开发
为什么需要插件机制?
- 抽离核心逻辑
- 易于拓展
(1)服务启动阶段:
config -> configResolved -> options -> configureServer -> buildStart
(2)请求响应阶段:
-> transformIndexHtml
-> resolveId -> load -> transfrom
(3)热更新阶段
handleHotUpdate
(4)服务关闭阶段
buildEnd -> closeBundle
通过上述的Hook,我们可以在不同的构建阶段插入自定义的逻辑、
[Vite插件开发文档] cn.vitejs.dev/guide/api-p…
[json加载插件] github.com/vitejs/vite…
[Esbuild接入插件] github.com/vitejs/vite…
[官方React插件] github.com/vitejs/vite…
3、代码分割(拆包)
拆包前:
存在的问题:
- 无法进行并发请求
- 缓存复用率低
拆包后:
参考资料
[构建选项] cn.vitejs.dev/config/buil…
[rollup.js] rollupjs.org/guide/en/#o…
4、JS编译工具(Babel)
出现的原因:
- JavaScript语法标准繁多,浏览器支持程度不一
- 开发者需要用到高级语法
参考资料
[babel官方站点] babeljs.io/docs/en/
[babel插件手册] github.com/jamiebuilds…
5、安全语法降级
以Promise语法为例,IE11没有支持。
如何在构建产物中避免这类问题?
-
上层解决方案:@vitejs/plugin-legacy
-
底层原理
- 借助Babel进行语法自动降级
- 提前注入Polyfill实现,如core-js、regenerator-runtime
参考资料
[@babel/preset-env官方文档] babeljs.io/docs/en/bab…
[Vite官方降级插件文档] github.com/vitejs/vite…
6、服务端渲染(SSR)
一种常见的渲染模式,用于提升首屏性能和SEO优化
构建阶段:
代码执行阶段: