Lesson 11 Vite 知识体系 学习笔记 | 青训营

139 阅读10分钟

Lesson 11 Vite 知识体系

浅谈构建工具

  • 前端核心要素:资源

    • JS、 TS、JSX
    • CSS、SCSS、LESS
    • PNG、JPEG、WEBP
  • 模块化:ESM、CommonJS、UMD

    • 提供模块加载方案
    • 兼容不同模块规范
  • 资源编译:高级语法的编译

    • 高级语法转译,如 Sass, TypeScript
    • 资源加载,如图片、字体、worker
  • 产物质量:代码体积、代码性能

    • 产物压缩、无用代码删除、语法降级
  • 开发效率:热更新

Vite 概要介绍

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

  • 两大组成部分

    • No-bundle 开发服务,源文件无需打包
    • 生产环境基于 Rollup 的 Bundler
  • 核心特征

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

当下问题

  • 缓慢的启动 -> 项目编译等待成本高
  • 缓慢的热更新 -> 修改代码后不能实时更新
  • Bundle 带来的性能开销
  • Javascript 语言的性能瓶颈

ESM

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

    • Script 标签增加 type="module" 属性
    • 使用 ESM 模块导入导出语法
  • 基于原生语言(Go、Rust)编写前端编译工具链

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

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

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

ESM 是 ECMAScript 模块的缩写,是一种在 JavaScript 中用于组织、导入和导出代码的模块化系统。它在 ECMAScript 2015(ES6)规范中引入,旨在改进传统的脚本加载方式,并提供更好的代码组织和重用机制。

ESM 具有以下特点:

  1. 模块化导入和导出: 使用 import 关键字从一个模块中导入函数、变量或对象,使用 export 关键字将这些内容暴露给其他模块。
  2. 静态解析: ESM 的导入和导出是在代码解析阶段处理的,这意味着导入的模块路径必须是静态的,不能依赖于运行时的条件。
  3. 延迟加载: 模块在第一次被导入时才会加载,这有助于提高性能,因为只有在需要时才会加载所需的模块。
  4. 作用域隔离: 每个模块都有自己的作用域,模块中的变量不会污染全局作用域或其他模块的作用域。
  5. 循环依赖处理: ESM 支持解决模块之间的循环依赖问题,确保模块加载的顺序不会导致死循环。
  6. 默认导出和命名导出: 可以使用 export default 来指定默认导出内容,以及使用命名导出来暴露多个内容。

基于 ES build 的编译性能优化

ES build —— 基于 Golang 开发的前端工具,具备如下能力

  • 打包器 Bundler
  • 编译器 Transformer
  • 压缩器 Minifier
  • 性能极高,在 Vite 中被深度采用

内置的 Web 构建能力

Vite 开箱即用的功能等价于

  • webpack
  • webpack-dev-server
  • css-loader
  • style-loader
  • less-loader
  • sass-loader
  • postcss-loader
  • file-loader
  • MiniCssExtractPlugin
  • HTMLWebpackPlugin

Vite 上手实战

Vite 项目初始化

# Install pnpm
npm i -g pnpm
# Initialize Commands
pnpm create vite
# Install Dependences
pnpm install
# Start Project
npm run dev

使用 Sass / Scss & CSS Modules

使用静态资源

除了常见的图片格式,Vite 也内置了对于 JSON、Worker、WASM 资源的加载支持 Vite —— 静态资源处理

生产环境 Tree Shaking

在现代前端开发中,Tree Shaking 是一个重要的优化技术,它用于在生产环境中剔除未使用的代码,以减小最终打包后的文件大小。Tree Shaking 主要针对 ES 模块系统中的代码,并且在使用像 Webpack、Rollup 或 Vite 这样的构建工具时表现尤为出色。

Tree Shaking 的工作原理是基于静态代码分析的。它会检查模块之间的依赖关系,并从代码中识别出未被使用的部分。这些未使用的代码片段会在最终的构建过程中被移除,从而减小了输出文件的体积。

在 Vite 中,Tree Shaking 是默认启用的。但是,为确保 Tree Shaking 的有效性,请注意以下几点:

  1. 使用 ES 模块: 确保您的代码使用 ES 模块(即 importexport 语法)。CommonJS 模块(requiremodule.exports)不支持 Tree Shaking。
  2. 避免副作用: 在您的代码中尽量避免引入有副作用的代码(比如直接操作全局作用域或修改外部状态)。这有助于 Tree Shaking 的准确性。
  3. 使用纯函数: 使用纯函数可以增加 Tree Shaking 的效果。纯函数是指函数的输出仅由输入决定,不受外部状态的影响。
  4. 按需引入库: 在引入第三方库时,尽量只引入您实际使用的部分。一些库可能会将整个库都打包进来,但您可以通过按需引入来最小化打包文件大小。
  5. 避免动态引入: 动态 import() 语法会导致 Tree Shaking 失效,因为它在运行时决定加载哪个模块。尽量避免在需要 Tree Shaking 的代码中使用动态引入。

Vite 整体架构

依赖预打包

  • 进行预打包的原因

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

    • 服务启动前扫描代码中用到的依赖
    • 用 ES build 对依赖代码进行预打包
    • 改写 import 语句,指定依赖为预构建产物路径

单文件编译

用 ES build 编译 TS/JSX

优势:编译速度提升 10-100x

局限性:不支持类型检查、不支持语法降级到 ES5

代码压缩

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

插件机制

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

  • 生产环境 -> 直接使用 Rollup

Vite 进阶路线

深入双引擎

ES build:esbuild - An extremely fast bundler for the web

Rollup.js:Rollup

Vite 插件开发

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

参考资料:

Vite 插件开发文档

复杂度较低的插件:JSON 加载插件

复杂度中等的插件:ES build接入插件

复杂度较高的插件:官方React插件

代码分割(拆包)

代码分割(Code Splitting)是一种优化前端应用性能的技术,旨在将应用的代码分成多个小块(或模块),并在需要时按需加载。这样做的主要目的是减少初始加载时所需的代码量,从而加快页面的加载速度,提升用户体验。

传统上,前端应用程序通常将所有代码打包到一个单一的文件中,这意味着用户在访问页面时必须下载整个应用的代码,即使用户可能仅仅浏览了其中的一部分内容。代码分割允许开发者将应用拆分成多个较小的模块,每个模块对应一个特定的功能或路由,然后在需要时进行按需加载。

代码分割有几个主要优点:

  1. 减少初始加载时间: 通过将代码分割成小块,页面的初始加载时间可以大大减少,因为只有当前页面所需的代码块会被加载。
  2. 提升用户体验: 用户能够更快地访问页面内容,因为不需要等待整个应用的代码都加载完成。
  3. 节省带宽: 对于访问多个页面的应用,代码分割可以减少带宽的使用,因为用户只会下载当前页面所需的代码。
  4. 优化缓存: 当用户在不同页面之间导航时,只需要加载新页面的代码块,而之前加载过的代码块可以从缓存中重用。

拆包前:

问题:

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

拆包后:

参考资料:

Rollup - API Configuration Options

Vite - API Configuration Options

JS 编译工具(Babel)

Babel 是一个流行的 JavaScript 编译工具,用于将新版本的 JavaScript 代码转换为旧版本的代码,以便在不支持最新语法和功能的浏览器或环境中运行。它主要用于将 ES6+(ECMAScript 2015 及更高版本)代码转换为 ES5 代码,以及应用其他转换,如 TypeScript 转换、JSX 转换等。Babel 的主要目标是帮助开发者在不同的 JavaScript 环境中实现跨浏览器兼容性和语法支持。

Babel 提供了一套插件系统,允许开发者根据需要添加不同的插件来执行各种转换。例如,你可以使用 Babel 的插件来转换箭头函数、解构赋值、模板字符串等 ES6+ 特性为 ES5 代码。Babel 还支持预设(Presets),预设是一组预定义的插件集合,可以方便地启用一组常用的转换。

出现的原因:

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

参考资料:

babel 官方站点

babel 插件手册

语法安全降级

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

在构建产物中避免这些问题的方法:

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

  • 底层原理

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

参考资料:

@babel/preset-env Document

Vite 官方降级插件文档

服务端渲染 (SSR)

服务端渲染(Server-Side Rendering,简称 SSR)是一种用于构建前端应用的技术,它与传统的客户端渲染(Client-Side Rendering,简称 CSR)有所不同。在 SSR 中,部分或全部页面内容是在服务器端生成的,然后将已渲染的 HTML 内容发送到浏览器,而不是在浏览器中使用 JavaScript 动态生成和渲染页面内容。

在传统的 CSR 中,页面的初始加载会导致浏览器下载一个包含 JavaScript 代码的 HTML 文件,然后浏览器会执行该代码以生成页面内容。这可能会导致页面在初始加载时出现白屏或加载过慢的情况,因为浏览器需要先下载和执行 JavaScript,然后才能生成和呈现页面内容。

相比之下,SSR 的优点包括:

  1. 更快的初始加载: 在 SSR 中,页面的初始加载会包含已经生成好的 HTML 内容,因此用户能够更快地看到页面的内容,减少白屏时间。
  2. 更好的 SEO: 由于搜索引擎可以直接看到渲染好的 HTML 内容,SSR 通常对搜索引擎优化(SEO)更友好,有助于提高页面的搜索引擎排名。
  3. 更好的性能: 在一些情况下,SSR 可以减少客户端的资源下载和处理负担,从而提升性能,尤其是在网络环境较差或设备性能较弱的情况下。
  4. 更好的可访问性: 一些网络爬虫或辅助技术可能无法理解或处理动态生成的内容,而通过 SSR 可以提供更好的可访问性体验。

尽管 SSR 有很多优点,但也存在一些挑战,如服务器负载增加、开发和调试复杂度等。在选择是否采用 SSR 时,需要考虑项目的需求、目标受众和技术栈等因素。

参考资料:

Vite - SSR Document

Vite 官方SSR-Demo Project

使用 Vite 搭建 SSR Project

深入了解底层标准

参考资料:

ES modules: A cartoon deep-dive – Mozilla Hacks - the Web developer blog

Ship ESM & CJS in one Package

Pure ESM package

Vite 社区生态

⭐ Github 58k+ star,并且目前还在持续维护

🔨 官方提供插件

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

🔥 海量社区插件 Awesome Vite

🤩 众多框架内置