学习Vite | 青训营笔记

120 阅读4分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 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中被深度使用

image.png

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//启动项目

输入初始化参数

image.png

使用Sass/Scss &CSS Modules

  1. 安装 Sass,pnpm i sass -D
  2. 新建文件夹 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>
};

使用静态资源

  1. 在 Vite 的配置文件中配置一下别名,方便后续的图片引入。
import path from 'path';
// ...
{
  resolve: {
    // 别名配置
    alias: {
      '@assets': path.join(__dirname, 'src/assets')
    }
  }
}

这样 Vite 在遇到 @assets 路径的时候,会自动帮我们定位至根目录下的 src/assets 目录。值得注意的是,alias 别名配置不仅在 JavaScript 的 import 语句中生效,在 CSS 代码的 @import 和 url 导入语句中也同样生效。

  1. 以 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;
  1. 除了常见的图片格式,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 数据量比较大的时候,可以优化解析性能。

  1. 如果项目中还存在其它格式的静态资源,还可以通过 assetsInclude 配置让 Vite 来支持加载:
// vite.config.ts
// ...
{
  assetsInclude: ['.gltf']
}
  1. Vite 中引入静态资源时,也支持在路径最后加上一些特殊的 query 后缀,包括:
  • ?url:表示获取资源的路径,这在只想获取文件路径而不是内容的场景将会很有用。

  • ?raw:表示获取资源的字符串内容,如果你只想拿到资源的原始内容,可以使用这个后缀。

  • ?inline:表示资源强制内联,而不是打包成单独的文件。

  • ?worker:加载为 Web Worker。

  • ?worker&inline:在构建时 Web Worker 内联为 base64 字符串。

使用HMR:

无需额外配置,自动开启

生产环境Tree Shaking

image.png

优化原理:

1.基于ESM的import/export语句依赖关系,与运行时状态无关

2.在构建阶段将未使用到的代码进行删除

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

Vite整体架构

image.png

关键技术:依赖预打包

进行预打包的原因

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

image.png

实现原理:

  • 服务器启动前扫描代码中用到的依赖
  • 用Esbuild对依赖代码进行预打包
  • 改写import语句,指定依赖为预构建产物路径
//改写前
import React from "react";
//改写后
import React from '/node_nodules/.vite/react.js' 

关键技术:单文件编译

用Esbuild编译TS/JSX

image.png

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

image.png

局限性

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

关键技术:代码压缩

image.png

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

压缩性能对比

image.png

关键技术:插件机制

image.png

无论是开发阶段还是生产环境,Vite 都根植于 Rollup 的插件机制和生态。

  • 开发阶段使用 Plugin Container,用来模拟 Rollup 调度各个 Vite 插件的执行逻辑
  • 生产环境直接使用 Rollup

但不是 Rollup 的插件都可以使用到 Vite 中,插件兼容性具体可查阅 Vite Rollup Plugins (patak.dev)

Vite进阶路线

深入双引擎

插件机制

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

Vite插件

image.png

插件示例

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原理:

image.png

语法安全降级

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

如何在构建产物时避免这类问题

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

2.底层原理

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加载

为了保证稳定性,目前阶段,还是需要去发同时具有ESMCJS的包,未来可能可以逐渐过渡到纯ESM的模式(社区也称之为PureESM)

Vite的社区生态

三、引用参考: