Vite 知识体系 | 青训营笔记

113 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 8 天

Vite

浅谈构建工具

前端工程存在很多需要解决的问题,例如:

  • 模块化:ESM、CommonJS、UMD
  • 资源编译:高级语法的编译
  • 产物的质量:代码体积、代码性能
  • 开发效率:热更新

前端构建工具的意义:

  • 模块化方案

    1. 提供模块加载方案
    2. 兼容不同模块规范
  • 语法转译

    1. 高级语法转译,如 Sass、Typescript
    2. 资源加载,如图片、字体、worker
  • 产物质量

    1. 产物压缩、无用代码删除、语法降级
  • 开发效率

    1. 热更新

Vite 概要介绍

概览

定位:新一代前端构建工具

两大组成部分:

  1. No-bundle 开发服务,源文件无需打包
  2. 生产环境基于 Rollup 的 Bundle

核心特征:

  1. 高性能,dev 启动速度和热更新速度非常快
  2. 简单易用,开发者体验好

传统构建工具存在的问题

  • 缓慢的启动 -> 项目编译成本高
  • 缓慢的热更新 -> 修改代码后不能实时更新

瓶颈:

  • bundle 带来的性能开销
  • JavaScript 语言的性能瓶颈

行业趋势

  • 全球浏览器普遍支持原生 ESM(目前占比 92% 以上)

    在浏览器中使用原生 ESM 实例:

    <!-- index.html -->
    <!DOCTYPE html>
    <html lang="zh-cn"><head>
        <meta charset="utf-8">
        <title>test esm</title>
    </head><body>
        <script type="module">
            import { foo } from './foo.js'
            console.log(foo)
        </script>
    </body></html>
    
    // foo.js
    export const foo = 'foo';
    
  • 基于原生语言(Go、Rust)编写前端编译工具链

  • 如 Go 语言编写的 ESbuild、Rust 编写的 SWC

基于原生 ESM 的开发服务优势

esm-dev.png

  • 无需打包项目源代码
  • 天然的按需加载
  • 可以利用文件级的浏览器缓存

基于 ESbuild 的编译性能优化

ESbuild 是基于 Golang 开发的前端工具,性能极高,在 Vite 中被深度使用。ESbuild 具备如下能力:

  1. 打包器 Bundler
  2. 编译器 Transformer
  3. 压缩器 Minifier

内置的 Web 构建能力

Vite 具有强大的构建能力,它拥有 Webpack 的众多功能,例如 dev-server、loader 和 plugin 等等。 以下是明显的配置对比:

webpack.config.js

webpack-config.png

等价于:

vite.config.js

vite-config.png

Vite 上手实战

项目初始化

# 安装 pnpm
npm i -g pnpm
# 初试化命令
pnpm create vite
# 安装依赖
pnpm install
# 启动项目
npm run dev

使用 Sass / Scss & CSS Module

安装 Sass

pnpm i sass -D

使用 Sass

// components/Header/index.tsx
import styles from './index.module.scss'
// 使用 CSS Modules 模块化方案,防止 className 命名冲突
export function Header () {
  return <p className={styles.header}>This is Header</p>
}
/* components/Header/index.module.scss */
.header{
    color: red;
}
// App.tsx
import { Header } from './components/Header'function App () {
  return (
    <div>
      <Header />
    </div>
  )
}
export default App;

使用静态资源

使用 svg 图片:

// App.tsx
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 资源的加载支持

使用 HMR

vite 默认自动开启 HMR,无需额外配置。

生产环境 Tree Shaking

优化原理:

  1. 基于 ESM 的 import/export 语句依赖关系,与运行时状态无关
  2. 在构建阶段将未使用到的代码进行删除

Tree Shaking 在 Vite 中无需配置,默认开启。

注意:CommonJS 格式不能做到 Tree Shaking,require 的部分可能依赖运行时计算的结果

Vite 整体架构

vite-architecture.png

依赖预打包

pre-bundle.png

预打包的用意:

  • 避免 node_modules 过多的文件请求
  • 将 CommonJS 格式转换为 ESM 格式

实现原理:

  1. 服务启动前扫描代码中用到的依赖

  2. 用 ESbuild 对依赖代码进行预打包

  3. 改写 import 语句,指定依赖为预构建产物路径

    // 改写前
    import React from 'react';
    // 改写后
    import React from '/node_modoule/.vite/react.js';
    

单文件编译

用 ESbuild 编译 TS/JSX

esbuild-transform.png

优势:

  • 编译速度提升 10 - 100 倍

缺点:

  • 不支持类型检查
  • 不支持语法降级到 ES5

代码压缩

esbuild-minify.png

ESbuild 作为默认压缩工具,在速度上有巨大的优势,替换了传统的 Terser、Uglify.js 等压缩工具。

插件机制

vite-plugin-pipeline.png

开发阶段 -> 模拟 Rollup 插件机制

生产阶段 -> 直接使用 Rollup

Vite 进阶路线

深入学习双引擎

继续深入学习 ESbuild 和 Rollup,学习顺序:

  1. 先了解基本使用,动手尝试各项常用配置;
  2. 然后学习其插件开发。

Vite 插件开发

插件机制的优势:

  • 抽离核心逻辑
  • 易于扩展

插件开发的各个阶段:

plugin-dev.png

通过上述 hook 可以在不同的构建阶段插入自定义的逻辑。

转换自定义文件类型示例:

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()]
})

开发过程:

  1. 开发 Vite 插件
  2. 配置文件引入插件

最好先看 Vite 官方文档,过一遍钩子的功能,然后多学习其它插件的实现,掌握套路。复杂度比较低的插件有 json 加载插件,复杂度中等的插件有 ESbuild 接入插件,复杂度较高的插件有官方 React 插件。

代码分割(拆包)

拆包前,引用资源会存在无法进行并发请求和缓存服用率低的情况。拆包后,能达到更好的缓存复用,提升页面加载速度。

JS 编译工具(Babel)

babel.png

Babel 出现的原因:

  • JavaScript 语法标准繁多,浏览器支持程度不一
  • 开发者需要用到高级语法

语法安全降级

在构建产物中避免浏览器语法不兼容问题的解决方案:

  • 上层解决方案:@vitesjs/plugin-legacy

  • 底层原理

    • 借助 Babel 进行语法自动降级
    • 提前注入 Polyfill 实现,如 core-js、regenerator-runtime

服务端渲染(SSR)

服务端渲染,一种常见的渲染模式,用于提升首屏性能和 SEO 优化。

构建阶段:

ssr-build.png

代码执行阶段:

ssr-run.png

深入了解底层标准

Vite 社区生态