这是我参与「第五届青训营 」伴学笔记创作活动的第 14 天
一、本堂课重点内容:
本堂课的知识要点有哪些?
- 浅谈构建工具
- Vite概要介绍
- Vite上手实战
- Vite整体架构
- Vite进阶路线
二、详细知识点介绍:
浅谈构建工具
随着前端发展,涉及到的资源是越来越多,如何高效快速的进行资源管理问题无可避免,正是由于传统的资源管理方式所带来的一系列问题,推动了构建工具的诞生。
前端工程的痛点
- 模块化:ESM、CommonJS、UMD
- 资源编译:高级语法的编译
- 产物质量:代码体积、代码性能
- 开发效率:热更新
前端构建工具的意义
模块化方案
- 提供模块加载方案
- 兼容不同模块规范
语法转译
- 高级语法转译
- 资源加载
产物质量
- 产物压缩
- 无用代码删除
- 语法降级
开发效率
- 热更新
Vite概要介绍
定位:新一代前端构建工具
Vite两大组成部分:
1.No-bundle开发服务,源文件无需打包
2.生产环境基于Rollup的Bundler
核心特征:
- 高性能,dev启动速度和热更新速度非常快
- 简单易用,开发者体验好
传统构架工具(wepack、rollup)遇到的问题
- 缓慢的启动=>项目编译等待成本告
- 缓慢的热更新=>修改代码后不能实时更新
瓶颈
- bundle带来的性能开销
- JS语言的性能瓶颈
行业趋势:
1.浏览器原生ESM支持:
- script标签增加type=“module”属性
- 使用ESM模块导入导出语法
基于原生ESM的开发服务优势:
- 无需打包项目源代码
- 天然的按需加载
- 可以利用文件级的浏览器缓存
2.基于esbuild的编译性能优化:
- esbuild基于golang开发的前端工具,具备打包器+编译器+压缩器
- 性能极高,在vite中被深度使用
Vite 开箱即用的功能等价于:
- webpack
- webpack-dev-server
- css-loader
- style-loader
- less-loader
- sass-loader
- postcss-loader
- file-loader
- MiniCssExtractPlugin
- HTMLWebpackPlugin
- HMR 无需额外配置,自动开启
- Tree Shaking 无需配置,默认开启
- ...
例如,以下 webpack.config.js 配置文件:
export default {
entry: './src/index.ts',
module: {
rules: [
{
test: /.[tj]sx?$/,
use: [{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-typescript'],
}
}]
},
{
test: /.s[ac]ss$/,
use: [
process.env.NODE_ENV === 'production' ? MiniCssExtractPlugin
.loader :
'style-loader',
{
loader: 'css-loader',
options: {
//开启CSS Modules
modules: true
}
},
'postcss-loader',
'sass-loader'
]
},
{
test: /.css$/,
use: {
process.env.NODE_ENV === 'production' ? MiniCssExtractPlugin
.loader : 'style-loader',
{
loader: 'css-loader',
options: {
//开启CSS Modules
modules: true
}
},
'postcss-loader',
},
},
]
},
plugins: [
new MiniCssExtractPlugin(),
new HTMLWebpackPlugin()
]
};
等价于以下 vite.config.js 配置:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()]
});
Vite上手实战
- 项目初始化
npm i -g pnpm//安装pnpm
pnpm create vite//初始化命令
pnpm install//安装依赖
npm run dev//启动项目
输入初始化参数
使用Sass/Scss &CSS Modules
- 安装 Sass,
pnpm i sass -D。 - 新建文件夹 components/Header,文件 index.tsx、index.module.scss。
目录结构
├── src
│ ├── App.css
│ ├── App.tsx
│ ├── components
│ ├── Header
│ ├── index.module.scss
│ ├── index.tsx
│ ├── index.css
│ ├── main.tsx
│ └── vite-env.d.ts
- index.module.scss
.header {
color: red;
}
- index.tsx
import styles from './index.module.scss';
//使用CSS Modules模块化方案,防止className命名冲突
export function Header() {
return <p className={styles.header}>This is Header</p>
};
使用静态资源
- 在 Vite 的配置文件中配置一下别名,方便后续的图片引入。
import path from 'path';
// ...
{
resolve: {
// 别名配置
alias: {
'@assets': path.join(__dirname, 'src/assets')
}
}
}
这样 Vite 在遇到 @assets 路径的时候,会自动帮我们定位至根目录下的 src/assets 目录。值得注意的是,alias 别名配置不仅在 JavaScript 的 import 语句中生效,在 CSS 代码的 @import 和 url 导入语句中也同样生效。
- 以 svg 图片为例,在 App.tsx 文件中导入一个静态资源会返回解析后的 URL,然后可以通过单花括号包裹,将其赋值给
img元素的src属性。
import { Header } from "./components/Header";
import logoUrl from './assets/react.svg';
import './App.css'
function App() {
return (
<div>
<img src={logoUrl} className="logo react" alt="React logo" />
<Header />
</div>
)
}
export default App;
- 除了常见的图片格式,Vite 也内置了对于 JSON、Worker、WASM 资源的加载支持。例如,对于 JSON 文件的解析,底层使用
@rollup/pluginutils的dataToEsm方法将 JSON 对象转换为一个包含各种具名导出的 ES 模块:
import { version } from '../../../package.json';
也可以在配置文件禁用按名导入的方式:
// vite.config.ts
// ...
{
json: {
stringify: true
}
}
这样会将 JSON 的内容解析为 export default JSON.parse("xxx"),这样会失去按名导出的能力,不过在 JSON 数据量比较大的时候,可以优化解析性能。
- 如果项目中还存在其它格式的静态资源,还可以通过
assetsInclude配置让 Vite 来支持加载:
// vite.config.ts
// ...
{
assetsInclude: ['.gltf']
}
- Vite 中引入静态资源时,也支持在路径最后加上一些特殊的 query 后缀,包括:
-
?url:表示获取资源的路径,这在只想获取文件路径而不是内容的场景将会很有用。 -
?raw:表示获取资源的字符串内容,如果你只想拿到资源的原始内容,可以使用这个后缀。 -
?inline:表示资源强制内联,而不是打包成单独的文件。 -
?worker:加载为 Web Worker。 -
?worker&inline:在构建时 Web Worker 内联为 base64 字符串。
使用HMR:
无需额外配置,自动开启
生产环境Tree Shaking
优化原理:
1.基于ESM的import/export语句依赖关系,与运行时状态无关
2.在构建阶段将未使用到的代码进行删除
3.Tree Shaking在Vite中无需配置,默认开启
Vite整体架构
关键技术:依赖预打包
进行预打包的原因
- 避免node_moudles过多的文件请求
- 将CommonJs格式转换为ESM格式
实现原理:
- 服务器启动前扫描代码中用到的依赖
- 用Esbuild对依赖代码进行预打包
- 改写import语句,指定依赖为预构建产物路径
//改写前
import React from "react";
//改写后
import React from '/node_nodules/.vite/react.js'
关键技术:单文件编译
用Esbuild编译TS/JSX
优势: 编译速度提升10-100x
局限性
- 不支持类型检查
- 不支持语法降级为ES5
关键技术:代码压缩
Esbuild作为默认压缩工具,替换传统的Terser、Uglify.js等压缩工具
压缩性能对比
关键技术:插件机制
无论是开发阶段还是生产环境,Vite 都根植于 Rollup 的插件机制和生态。
- 开发阶段使用
Plugin Container,用来模拟 Rollup 调度各个 Vite 插件的执行逻辑 - 生产环境直接使用 Rollup
但不是 Rollup 的插件都可以使用到 Vite 中,插件兼容性具体可查阅 Vite Rollup Plugins (patak.dev)
Vite进阶路线
深入双引擎
插件机制
- 抽离核心逻辑
- 易于拓展
Vite插件
插件示例
1、开发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
}
}
}
}
}
2、配置文件引入插件
// vite.config.js
import plugin from './myPlugin'
export default defineConfig ({
plugins:[plugin()]
})
这个插件所做的事情 把自定义的文件内容进行一个编译,把编译的结果返回给Vite
代码分割|拆包
- 传统的项目一般是打包成一个
Bundle
主要问题是:
- 无法进行并发请求
- 缓存复用率低
构建选项 | Vite 官方中文文档 (vitejs.dev)
Configuration Options | Rollup (rollupjs.org)
JS编译工具(Babel)
为什么出现Babel
- JavaScript语法标准繁多,浏览器支撑程度不统一
- 开发者需要用到高级语法
Babel原理:
- What is Babel? · Babel (babeljs.io)
- babel-handbook/plugin-handbook.md at master · jamiebuilds/babel-handbook (github.com)
语法安全降级
以Promise语法为例,IE11没有支持
如何在构建产物时避免这类问题
1.上层解决方案: @vitejs/plugin-legacy
2.底层原理
-
借助Babel进行语法降级
-
提前注入Polifill实现,如core-js、regenerator-runtime
@babel/preset-env · Babel (babeljs.io)
vite/packages/plugin-legacy at main · vitejs/vite (github.com)
SSR服务端渲染
- 和原来的模式最大的不同是,除了构建出原来前端的产物,也会构建出一份SSR的产物,这份产物是跑在Node.js (服务端),在代码执行的阶段,我们在服务端会拿到SSR的产物,进行数据的预取,进行组件的渲染,然后在服务端就把HTML的内容给产出出来,这样前端能够拿到的是完整的HTML内容,而不是之前的仅仅一个
<div id="root"></div>,因此可以更快地渲染到页面上(提高首屏时间)
服务端渲染 | Vite 官方中文文档 (vitejs.dev)
服务端渲染 | Vite 官方中文文档 (vitejs.dev)
深入浅出 Vite - 神三元 - 掘金小册 (juejin.cn)
深入了解底层标准
重点特性:
- CJS规范
- ESM规范
- HTTP 2.0特性
不光是浏览器支持了原生地ESM模块加载,包括Node也支持了原生的Node加载
为了保证稳定性,目前阶段,还是需要去发同时具有ESM和CJS的包,未来可能可以逐渐过渡到纯ESM的模式(社区也称之为PureESM)
- ES modules: A cartoon deep-dive - Mozilla Hacks - the Web developer blog
- Ship ESM & CJS in one Package (antfu.me)
- Pure ESM package (github.com)
Vite的社区生态