[构建 Vite 知识体系 | 青训营笔记14]

98 阅读4分钟

前言

这是我参与「第五届青训营」伴学笔记创作活动的第 14天,上章我们讲了webpack,这章我们来去讲vite,也是构建打包工具,vite构建和打包都比webpack要快的多,支持跨平台等优势,大有代替webpack之势,那我接下来就开始讲

内容大纲

  1. 浅谈构建工具
  2. Vite 概要介绍
  3. Vite上手实战
  4. 整体架构Vite :
  5. Vite 进阶路线

为什么需要前端工具?

前端工程的痛点

核心要素 —— 资源

  • JS
  • TS
  • JSX
  • CSS
  • SCSS
  • LESS
  • PNG
  • JPEG
  • WEBP
  • ...

模块化

  • ESM、CommJS、UMD

资源编译

  • 高级语法的编译

产物质量

  • 代码体积,代码性能

开发效率

  • 热更新

前端构建工具的意义

8c35e563-5456-4e95-aeda-3fcb9306e986.png

模块化:webpack runtime 就是对模块的导入导出的规范统一;语法转译:loader;产物质量: boulder,treeshaking,babel; 开发效率:HMR

Vite 是什么? Why Vite?

Vite 概览

下载.png

  • 定位: 新一代前端构建工具
  • 两大组成部分
      1. No-bundle 开发服务,源文件无需打包
      1. 生产环境基于 RollupBundler
  • 核心特征
      1. 高性能,dev 启动速度和热更新速度非常快!
      1. 简单易用,开发者体验好

业界案例

06a4eb63-e343-4a15-8c13-59f3aca7dc27.png Rollup -> Vite

  • 启动时间: 2分15秒 -> 1.7 秒
  • 更新时间: 23 秒-> 1秒以内 6ebd5bc8-e8a2-4e00-ad22-c946d15da7ef.png Webpack -> Vite
  • 启动时间: 2分36秒 -> 6 秒
  • 热更新从 13 秒-> 1 秒以内

当下问题

下载 (1).png

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

开发体验问题日渐显露

瓶颈在哪里? bundle 带来的性能开销 JavaScript 语言的性能瓶

两大行业趋势

8aeee22e-c7a5-487e-a414-3b1a2339053e.png

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

76851c3a-85f3-424a-a54f-dc770ad745f1.png 90ed05f8-2cf3-45d8-85e0-05cefe98d689.png

  • 基于原生语言(GoRust)编写前端编译工具链
  • Go 语言编写的EsbuildRust 编写的 SWC

浏览器原生 ESM 支持

// index.html
<!DOCTYPE htmm>
<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';

两大要素

  1. script 标签增加 type="module"属性
  2. 使用 ESM 模块导入导出语法

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

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

aa04db5d-2e7b-4703-b8de-8bd80a5141ce.png

  1. 去除了boulder之前的开销,不需要项目打包源代码,一个文件就是一个请求;我需要哪些文件才按需引入,如:就只请求src/main.tsx 的话,就只编译src/main.tsx,其他先不编译;浏览器达到了文件级,当文件变更,就不会导致整个boulder失效,只会导致当前请求失效,就可以达到更细腻的请求

基于 Esbuild 的编译性能优化

48a58d43-21ce-4b1f-8646-c08abd3c9f30.png Esbuild --基于 Golang 开发的前端工具,具备如下能力:

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

性能极高,在 Vite 中被深度使用

内置的 Web 构建能力

Vite 开箱即用的功能等价于

  • webpack
  • webpack-dev-server
  • css-loader
  • style-loader
  • less-loader
  • sass-loader
  • postcss-loader
  • file-loader
  • MiniCssExtractPlugin
  • HTMLWebpackPlugin
  • ...
// webpack.config.ts
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 } 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
    plugins: [react()]
})

那么,我们如何使用 Vite 呢?

Vite 上手使用

项目初始化

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

ed9dac25-ac4c-4df8-8d39-496c32715e36.png

  • 输入初始化参数 450f5bd0-b84c-482f-8f55-99f5d862628b.png
  • 启动后截图

启动完成后,打开浏览器访问对应地址即可

使用 Sass/Scss & CSS Modules

目录结构

src
|—— App.css
|—— App.tsx
|—— components
|    |_ Header
|        |_ index.module.scss
|        |_ index.tsx
|—— favicon.svg
|—— index.css
|—— logo.svg
|—— main.tsx
|_  vite-env.d.ts
# 安装 Sass
pnpm i sass -D
// index.html
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;
}

使用 Scss & CSS Modules

目录结构

src
|—— App.css
|—— App.tsx
|—— components
|    |_ Header
|        |_ index.module.scss
|        |_ index.tsx
|—— favicon.svg
|—— index.css
|—— logo.svg
|—— main.tsx
|_  vite-env.d.ts

引入 Header 组件

import { Header } from './components/Header';

function App() {
    return (
    <div>
        <Header />
    </div>
    )
}

export default App;

使用静态资源

目录结构

src
|—— App.css
|—— App.tsx
|—— components
|    |_ Header
|        |_ index.module.scss
|        |_ index.tsx
|—— favicon.svg
|—— index.css
|—— logo.svg
|—— main.tsx
|_  vite-env.d.ts

以 svg 图片为例

import { Header } from './components/Header';
import logoUrl from './logo.svg';

function App() {
    return (
    <div>
        <Header />
    </div>
    )
}

export default App;

除了常见的图片格式,Vite 也内置了对于JSONWorkerWASM 资源的加载支持
cn.vitejs.dev/guide/featu…

使用 HMR

无需额外配置,自动开启 详细请看Vite官方介绍

生产环境 Tree Shaking

21663ff9-0547-411e-ac3d-487423edf589.png 优化原理:

  1. 基于 ESM 的 import/export 语句依赖关系,.与运行时状态无关
  2. 在构建阶段将未使用到的代码进行删除
// CommonJS 格式不能做到 Tree Shaking
// require 的部分可能依赖运行时计算的结果
require(someVariable)

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

Tree Shaking 只能用于 ESM;因为nodejs是runtime 计算的结果,所以你就没办法将它们静态分析,如果你强制把它删掉的话,会删掉不应该删掉的东西;这是有风险的,因此Tree Shaking只能由于ESM

Vite 给你最直观的印象如何?

  • 响应迅速
  • 开箱即用

Vite 整体架构

下载 (2).png

关键技术:依赖预打包

为什么要进行预打包?

  1. 避免 node_modules 过多的文件请求
  2. CommonJS 格式转换为 ESM 格式 下载 (3).png 实现原理:
  3. 服务启动前扫描代码中用到的依赖
  4. Esbuild 对依赖代码进行预打包
  5. 改写 import 语句,指定依赖为预构建产物路径
// 改写前
import React from 'react';
// 改写后
import React from '/node_modules/.vite/react.js'

关键技术:单文件编译

Esbuild 编译 TS/JSX

2eea267f-83d6-4615-b850-106f95df8266.png 00a25d6e-e4df-470e-9f16-8cca15a74e98.png 优势:

  • 编译速度提升 10-100 x

局限性:

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

关键技术:代码压缩

7753e532-a25d-4aea-9d76-2ca63cee7557.png 压缩性能对比 f5a3d052-e52d-4ad5-b396-0156189b7499.png

Esbuild 作为默认压缩工具,替换传统的 Terser、Uglify.js 等压缩工具

Vite 进阶路线

深入双引擎

76851c3a-85f3-424a-a54f-dc770ad745f1.png

d0eb266c-b6bf-4ba7-a525-676f69674c4c.png 参考资料

推荐学习顺序:

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

Vite 插件开发

为什么需要插件机制?

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

e60c5948-cd86-4a59-9c6a-c6a20c1f3241.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: nul1
          // 如果可行将提供 source map

        }
      }
    }
  }
}
// vite.config.js
import plugin from './myPlugin'

export default defineConfig({
    plugins:[plugin()]
})
  1. 开发 Vite 插件
  2. 配置文件引入插件

参考资料:

先看文档,过一遍插件钩子的功能,然后多学习其它插件的实现,掌握套路

代码分割(拆包)

拆包前 49d6ee24-0a44-4439-a562-a1aca438d936.png 问题:

  • 无法进行并发请求
  • 缓存复用率低

拆包后 8a467de9-69eb-4605-8d98-3ca4c0f60b8c.png

参考资料:
cn.vitejs.dev/config/buil… rollupjs.org/guide/en/#o…

JS 编译工具(Babel)

Babel 原理 下载 (4).png

出现的原因:

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

参考资料:

语法安全降级

以 Promise 语法为例,IE11 没有支持

4a7965ea-82fc-492e-a32f-a62894c6a911.png

83b39c68-0e83-4814-949a-1f912c16a297.png

  • 如何在构建产物中避免这类问题?
    • 上层解决方案: @viteis/plugin-legacy
    • 底层原理
      • 借助 Babel 进行语法自动降级
      • 提前注入 Polyfill 实现,如 core-jsregenerator-runtime

参考资料:

服务端渲染(SSR)

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

下载 (5).png 构建阶段 450a6a5a-a17c-4453-9d57-f8a1de5dc688.png 代码执行阶段

参考资料:

深入了解底层标准

重点特性:

  • CJS 规范
  • ESM 规范
  • HTTP/1.1
  • HTTP 2.0 特性

参考资料:

Vite 社区生态

Github 40k+ star(可参考 webpack 61.3 K, rollup 21.8 k),并且目前还在持续维护

官方提供插件:

  • @vitejs/plugin-vue,提供 Vue 3 支持
  • @vitejs/plugin-vue-jsx,提供 Vue 3 JSX 支持
  • @vitejs/plugin-react,提供 React 支持
  • @vitejs/plugin-legacy,提供低版本浏览器降级支持

海量社区插件 qithub.com/vitejs/awes…

众多框架内置

  • Nuxt

2a3379f5-24f4-46ba-a3a6-8db41c2ea39d.png

  • SvelteKit 6df86706-9ee8-436f-bef1-937a2204d801.png
  • Astro d50e5d2e-7af6-404a-9234-df37d4948574.png
  • Vitepress 445bbd72-cc40-4eb3-b8e7-a02d33ad6418.png