当前位置:首页 > 科技  > 软件

浅析Vite插件机制,你学会了吗?

来源: 责编: 时间:2024-07-19 08:03:22 661观看
导读前言对于Vite来说,它是基于esbuild与rollup双引擎设计的,在开发阶段使用esbuild进行依赖预构建,然后基于浏览器原生支持的ESM完成开发预览,而在生产环境打包时,直接使用的rollup构建。那么在这种背景下,Vite的插件机制应该

前言

对于Vite来说,它是基于esbuild与rollup双引擎设计的,在开发阶段使用esbuild进行依赖预构建,然后基于浏览器原生支持的ESM完成开发预览,而在生产环境打包时,直接使用的rollup构建。那么在这种背景下,Vite的插件机制应该如何设计?3Lz28资讯网——每日最新资讯28at.com

在源码中,我们能够经常看到PluginContainer的身影,Vite正是通过它来模拟rollup的行为3Lz28资讯网——每日最新资讯28at.com

图片3Lz28资讯网——每日最新资讯28at.com

pluginContainer

PluginContainer 的 实现 基于借鉴于 WMR 中的 rollup-plugin-container.js ,主要功能有两个:3Lz28资讯网——每日最新资讯28at.com

  • 管理插件的生命周期
  • 实现插件钩子内部的 Context 上下文对象

插件生命周期

在开发阶段,vite会模拟rollup的行为,所以插件的执行机制也与rollup相同3Lz28资讯网——每日最新资讯28at.com

图片图片3Lz28资讯网——每日最新资讯28at.com

  1. 调用 options 钩子进行配置的转换,得到处理后的配置对象。
  2. 调用buildStart钩子,正式开始构建流程。
  3. 调用 resolveId 钩子中解析文件路径。(从 input 配置指定的入口文件开始)。
  4. 调用load钩子加载模块内容。
  5. 紧接着 Rollup 执行所有的 transform 钩子来对模块内容进行进行自定义的转换(比如 babel 转译)
  6. Rollup 拿到最后的模块内容,进行 AST 分析,得到所有的 import 内容,调用 moduleParsed 钩子
  7. 直到所有的 import 都解析完毕,Rollup 执行buildEnd钩子,Build 阶段结束。

3Lz28资讯网——每日最新资讯28at.com

这里需要注意的是:在 vite 中由于 AST 分析是通过 esbuild 进行的,所有没有模拟 moduleParsed 钩子3Lz28资讯网——每日最新资讯28at.com

传递上下文对象

上下文对象通过 Context 实现 PluginContext 接口定义,PluginContext 实际上是 Rollup 内部定义的类型,可以在源码中看到 vite 实现了 Rollup 上下文对象3Lz28资讯网——每日最新资讯28at.com

class Context implements PluginContext {  //... 具体实现}type PluginContext = Omit<  RollupPluginContext, // Rollup 定义插件上下文接口  // not documented  | 'cache'  // deprecated  | 'moduleIds'>

Context 上下文对象一共有 14 个核心方法,其中有 3 个方法是比较核心的方法3Lz28资讯网——每日最新资讯28at.com

  • parse:使用 Rollup 的内部基于 SWC 的解析器将代码解析为 AST
  • resolve:将相对路径解析为绝对路径,从而正确地处理模块之间的引用
  • load:加载并解析与给定 ID 对应的模块,并在提供的情况下附加附加的元信息到模块

更多内容可以查看rollup文档3Lz28资讯网——每日最新资讯28at.com

rollup插件

rollup构建流程主要分为两大类:build和output,build 阶段主要负责创建模块依赖图,初始化各个模块的 AST 以及模块之间的依赖关系。output阶段才是真正的打包构建过程3Lz28资讯网——每日最新资讯28at.com

插件hook类型(构建阶段)

通过构建流程rollup的hook类型可以分为:build hook和output hook两大类3Lz28资讯网——每日最新资讯28at.com

  • Build Hook即在Build阶段执行的钩子函数,在这个阶段主要进行模块代码的转换、AST 解析以及模块依赖的解析,那么这个阶段的 Hook 对于代码的操作粒度一般为模块级别,也就是单文件级别。
  • Ouput Hook(官方称为Output Generation Hook),则主要进行代码的打包,对于代码而言,操作粒度一般为 chunk级别(一个 chunk 通常指很多文件打包到一起的产物)。

插件hook类型(执行方式)

除了上面这种分类,rollup插件还可以根据各自的执行方式来进行分类:3Lz28资讯网——每日最新资讯28at.com

  • Build Hook即在Build阶段执行的钩子函数,在这个阶段主要进行模块代码的转换、AST 解析以及模块依赖的解析,那么这个阶段的 Hook 对于代码的操作粒度一般为模块级别,也就是单文件级别。
  • Ouput Hook(官方称为Output Generation Hook),则主要进行代码的打包,对于代码而言,操作粒度一般为 chunk级别(一个 chunk 通常指很多文件打包到一起的产物)。

除了根据构建阶段可以将 Rollup 插件进行分类,根据不同的 Hook 执行方式也会有不同的分类,主要包括Async、Sync、Parallel、Squential、First这五种。在后文中我们将接触各种各样的插件 Hook,但无论哪个 Hook 都离不开这五种执行方式。3Lz28资讯网——每日最新资讯28at.com

1. Async & Sync

首先是Async和Sync钩子函数,两者其实是相对的,分别代表异步和同步的钩子函数,这个很好理解。3Lz28资讯网——每日最新资讯28at.com

2. Parallel

这里指并行的钩子函数。如果有多个插件实现了这个钩子的逻辑,一旦有钩子函数是异步逻辑,则并发执行钩子函数,不会等待当前钩子完成(底层使用 Promise.all)。3Lz28资讯网——每日最新资讯28at.com

比如对于Build阶段的buildStart钩子,它的执行时机其实是在构建刚开始的时候,各个插件可以在这个钩子当中做一些状态的初始化操作,但其实插件之间的操作并不是相互依赖的,也就是可以并发执行,从而提升构建性能。反之,对于需要依赖其他插件处理结果的情况就不适合用 Parallel 钩子了,比如 transform。3Lz28资讯网——每日最新资讯28at.com

3. Sequential

Sequential 指串行的钩子函数。这种 Hook 往往适用于插件间处理结果相互依赖的情况,前一个插件 Hook 的返回值作为后续插件的入参,这种情况就需要等待前一个插件执行完 Hook,获得其执行结果,然后才能进行下一个插件相应 Hook 的调用,如transform。3Lz28资讯网——每日最新资讯28at.com

4. First

如果有多个插件实现了这个 Hook,那么 Hook 将依次运行,直到返回一个非 null 或非 undefined 的值为止。比较典型的 Hook 是 resolveId,一旦有插件的 resolveId 返回了一个路径,将停止执行后续插件的 resolveId 逻辑。3Lz28资讯网——每日最新资讯28at.com

通用hook

以下钩子在服务器启动时被调用:3Lz28资讯网——每日最新资讯28at.com

  • options
  • buildStart

以下钩子会在每个传入模块请求时被调用:3Lz28资讯网——每日最新资讯28at.com

  • resolveId
  • load
  • transform

它们还有一个扩展的 options 参数,包含其他特定于 Vite 的属性。3Lz28资讯网——每日最新资讯28at.com

以下钩子在服务器关闭时被调用:3Lz28资讯网——每日最新资讯28at.com

  • buildEnd
  • closeBundle

请注意 moduleParsed 钩子在开发中是 不会 被调用的,因为 Vite 为了性能会避免完整的 AST 解析。3Lz28资讯网——每日最新资讯28at.com

output阶段的hook(除了 closeBundle) 在开发中是 不会 被调用的。你可以认为 Vite 的开发服务器只调用了 rollup.rollup() 而没有调用 bundle.generate()。3Lz28资讯网——每日最新资讯28at.com

Vite 独有hook

Vite 插件也可以提供钩子来服务于特定的 Vite 目标。当然这些钩子会被 Rollup 忽略。3Lz28资讯网——每日最新资讯28at.com

config

在解析 Vite 配置前调用。钩子接收原始用户配置(命令行选项指定的会与配置文件合并)和一个描述配置环境的变量,包含正在使用的 mode 和 command。它可以返回一个将被深度合并到现有配置中的部分配置对象,或者直接改变配置(如果默认的合并不能达到预期的结果)。3Lz28资讯网——每日最新资讯28at.com

// 返回部分配置(推荐)const partialConfigPlugin = () => ({  name: 'nanjiu-plugin',  config(config, { command }) {    console.log('config', config, command)  }})

图片图片3Lz28资讯网——每日最新资讯28at.com

需要注意的是:用户插件在运行这个钩子之前会被解析,因此在 config 钩子中注入其他插件不会有任何效果。3Lz28资讯网——每日最新资讯28at.com

configResolved

在解析 Vite 配置后调用。使用这个钩子读取和存储最终解析的配置。3Lz28资讯网——每日最新资讯28at.com

const examplePlugin = () => {  let config  return {    name: 'read-config',    configResolved(resolvedConfig) {      // 存储最终解析的配置      config = resolvedConfig    },    // 在其他钩子中使用存储的配置    transform(code, id) {      if (config.command === 'serve') {        // dev: 由开发服务器调用的插件      } else {        // build: 由 Rollup 调用的插件      }    },  }}

configureServer

是用于配置开发服务器的钩子。最常见的用例是在内部 connect 应用程序中添加自定义中间件3Lz28资讯网——每日最新资讯28at.com

const myPlugin = () => ({  name: 'configure-server',  configureServer(server) {    server.middlewares.use((req, res, next) => {      // 自定义请求处理...    })  },})

configurePreviewServer

与 configureServer 相同,但用于预览服务器。configurePreviewServer 这个钩子与 configureServer 类似,也是在其他中间件安装前被调用。如果你想要在其他中间件 之后 安装一个插件,你可以从 configurePreviewServer 返回一个函数,它将会在内部中间件被安装之后再调用3Lz28资讯网——每日最新资讯28at.com

const myPlugin = () => ({  name: 'configure-preview-server',  configurePreviewServer(server) {    // 返回一个钩子,会在其他中间件安装完成后调用    return () => {      server.middlewares.use((req, res, next) => {        // 自定义处理请求 ...      })    }  },})

transformIndexHtml

转换 index.html 的专用钩子。钩子接收当前的 HTML 字符串和转换上下文。上下文在开发期间暴露ViteDevServer实例,在构建期间暴露 Rollup 输出的包。3Lz28资讯网——每日最新资讯28at.com

这个钩子可以是异步的,并且可以返回以下其中之一:3Lz28资讯网——每日最新资讯28at.com

  • 经过转换的 HTML 字符串
  • 注入到现有 HTML 中的标签描述符对象数组({ tag, attrs, children })。每个标签也可以指定它应该被注入到哪里(默认是在 <head> 之前)
  • 一个包含 { html, tags } 的对象

默认情况下 order 是 undefined,这个钩子会在 HTML 被转换后应用。为了注入一个应该通过 Vite 插件管道的脚本, order: 'pre' 指将在处理 HTML 之前应用。order: 'post' 是在所有未定义的 order 的钩子函数被应用后才应用。3Lz28资讯网——每日最新资讯28at.com

const htmlPlugin = () => {  return {    name: 'nanjiu-plugin',    transformIndexHtml(html) {      return html.replace(/<title>(.*?)<//title>/,                          `<title> nanjiu plugin </title>`)    },  }}

handleHotUpdate

执行自定义 HMR 更新处理。钩子接收一个带有以下签名的上下文对象3Lz28资讯网——每日最新资讯28at.com

interface HmrContext {  file: string  timestamp: number  modules: Array<ModuleNode>  read: () => string | Promise<string>  server: ViteDevServer}
  • modules 是受更改文件影响的模块数组。它是一个数组,因为单个文件可能映射到多个服务模块(例如 Vue 单文件组件)。
  • read 这是一个异步读函数,它返回文件的内容。之所以这样做,是因为在某些系统上,文件更改的回调函数可能会在编辑器完成文件更新之前过快地触发,并 fs.readFile 直接会返回空内容。传入的 read 函数规范了这种行为。
const hotPlugin = () => {  return {    name: 'nanjiu-plugin',    handleHotUpdate({ server, modules, timestamp}) {      console.log('handleHotUpdate', modules)    },  }}

当我修改App.vue文件时,modules可以获取到如下信息:3Lz28资讯网——每日最新资讯28at.com

图片图片3Lz28资讯网——每日最新资讯28at.com

插件顺序

一个 Vite 插件可以额外指定一个 enforce 属性(类似于 webpack 加载器)来调整它的应用顺序。enforce 的值可以是pre 或 post。解析后的插件将按照以下顺序排列:3Lz28资讯网——每日最新资讯28at.com

  • Alias
  • 带有 enforce: 'pre' 的用户插件
  • Vite 核心插件
  • 没有 enforce 值的用户插件
  • Vite 构建用的插件
  • 带有 enforce: 'post' 的用户插件
  • Vite 后置构建插件

请注意,这与钩子的排序是分开的,钩子的顺序仍然会受到它们的 order 属性的影响,这一点 和 Rollup 钩子的表现一样3Lz28资讯网——每日最新资讯28at.com

总结

vite 在 开发环境中,会使用 createPluginContainer 方法创建插件容器,插件容器有两个核心功能:管理插件生命周期、传递插件上下文3Lz28资讯网——每日最新资讯28at.com

图片图片3Lz28资讯网——每日最新资讯28at.com

3Lz28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-101721-0.html浅析Vite插件机制,你学会了吗?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 软件版本号为什么那么奇怪?

下一篇: 基于Netty的自研流系统缓存实现挑战:内存碎片与OOM困境

标签:
  • 热门焦点
  • 5月iOS设备好评榜:iPhone 14仅排第43?

    来到新的一月,安兔兔的各个榜单又重新汇总了数据,像安卓阵营的榜单都有着比较大的变动,不过iOS由于设备的更新换代并没有那么快,所以相对来说变化并不大,特别是iOS好评榜,老款设
  • K6:面向开发人员的现代负载测试工具

    K6 是一个开源负载测试工具,可以轻松编写、运行和分析性能测试。它建立在 Go 和 JavaScript 之上,它被设计为功能强大、可扩展且易于使用。k6 可用于测试各种应用程序,包括 Web
  • K8S | Service服务发现

    一、背景在微服务架构中,这里以开发环境「Dev」为基础来描述,在K8S集群中通常会开放:路由网关、注册中心、配置中心等相关服务,可以被集群外部访问;图片对于测试「Tes」环境或者
  • 在线图片编辑器,支持PSD解析、AI抠图等

    自从我上次分享一个人开发仿造稿定设计的图片编辑器到现在,不知不觉已过去一年时间了,期间我经历了裁员失业、面试找工作碰壁,寒冬下一直没有很好地履行计划.....这些就放在日
  • 使用LLM插件从命令行访问Llama 2

    最近的一个大新闻是Meta AI推出了新的开源授权的大型语言模型Llama 2。这是一项非常重要的进展:Llama 2可免费用于研究和商业用途。(几小时前,swyy发现它已从LLaMA 2更名为Lla
  • 为什么你不应该使用Div作为可点击元素

    按钮是为任何网络应用程序提供交互性的最常见方式。但我们经常倾向于使用其他HTML元素,如 div span 等作为 clickable 元素。但通过这样做,我们错过了许多内置浏览器的功能。
  • 微博大门常打开,迎接海外画师漂洋东渡

    作者:互联网那些事&ldquo;起猛了,我能看得懂日语了&rdquo;。&ldquo;为什么日本人说话我能听懂?&rdquo;&ldquo;中文不像中文,日语不像日语,但是我竟然看懂了&rdquo;&hellip;&hell
  • 机构称Q2全球智能手机出货量同比下滑11% 苹果份额依旧第2

    7月20日消息,据外媒报道,研究机构的报告显示,由于需求下滑,今年二季度全球智能手机的出货量,同比下滑了11%,三星、苹果等主要厂商的销量,较去年同期均有下
  • 2022爆款:ROG魔霸6 冰川散热系统持续护航

    喜逢开学季,各大商家开始推出自己的新产品,进行打折促销活动。对于忠实的端游爱好者来说,能够拥有一款梦寐以求的笔记本电脑是一件十分开心的事。但是现在的
Top