Vite(五)插件API(钩子hook)、HMR API、JavaScript API
- 网址:https://vitejs.cn/guide/why.html
1. 插件 API
Vite 插件扩展了设计出色的 Rollup 接口,带有一些 vite 独有的配置项。因此,你只需要编写一个 Vite 插件,就可以同时为开发环境和生产环境工作。
推荐在阅读下面的章节之前,首先阅读下 Rollup 插件文档
约定
如果插件不使用 Vite 特有的钩子,可以实现为 兼容的 Rollup 插件,推荐使用 Rollup 插件名称约定。
- Rollup 插件应该有一个带
rollup-plugin-
前缀、语义清晰的名称。 - 在 package.json 中包含
rollup-plugin
和vite-plugin
关键字。
这样,插件也可以用于纯 Rollup 或基于 WMR 的项目。
对于 Vite 专属的插件:
- Vite 插件应该有一个带
vite-plugin-
前缀、语义清晰的名称。 - 在 package.json 中包含
vite-plugin
关键字。 - 在插件文档增加一部分关于为什么本插件是一个 Vite 专属插件的详细说明(如,本插件使用了 Vite 特有的插件钩子)。
如果你的插件只适用于特定的框架,它的名字应该遵循以下前缀格式:
vite-plugin-vue-
前缀作为 Vue 插件vite-plugin-react-
前缀作为 React 插件vite-plugin-svelte-
前缀作为 Svelte 插件
简单示例
TIP
通常的惯例是创建一个 Vite/Rollup 插件作为一个返回实际插件对象的工厂函数。该函数可以接受允许用户自定义插件行为的选项。
引入一个虚拟文件
- 在 ES6 模块系统中,使用 export default 可以导出一个默认值,使用方可以用 import foo from ‘foo’ 而不是 import { foo } from ‘foo’ 来导入这个默认值。
- 在类型声明文件中,export default 用来导出默认值的类型
- 注意,只有
function
、class
和interface
可以直接默认导出,其他的变量需要先定义出来,再默认导出
export default function myPlugin() {
const virtualFileId = '@my-virtual-file'
return {
name: 'my-plugin', // 必须的,将会显示在 warning 和 error 中
resolveId(id) {
if (id === virtualFileId) {
return virtualFileId
}
},
load(id) {
if (id === virtualFileId) {
return `export const msg = "from virtual file"`
}
}
}
}
这使得可以在 JavaScript 中引入这些文件:
import {
msg } from '@my-virtual-file'
console.log(msg)
转换自定义文件类型
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 // provide source map if available
}
}
}
}
}
通用钩子
简单的说,钩子就是程序。这些程序可以在特定的时间被调用。 你既然用CI框架,那么应该知道,在框架初始化的过程中,有一些特殊的时间点,比如 (1)、框架初始化之前的时间点。 (2)、控制器初始化之前的时间点 (3)、控制器运行之后的时间点 当然还有其他的时间点。 在每一个时间点,你都可以埋下一些钩子(可以是一段程序,或者一个函数)。框架中都会有一个专门处理钩子的类库(比如CI中的hooks.php),这个Hook类就会在程序运行的特定点检查是不是有特定的钩子,如果有钩子,就执行这个钩子。 说了这么多,可能还是有点抽象。打个比方: 你下班回家的整个流程看做是框架的执行流程,正常情况下,你下班直接走回家就可以。如果你老妈给你打电话说,如果你路过邮局,刚好有咱家的快递,带回家,这个过程,就比较类似钩子(检查钩子,如果有则执行之)。 如果你做过一些前端的工作,这个钩子就恰似“事件驱动”的模式。
所谓Hook机制,是从Windows编程中流行开的一种技术。其主要思想是提前在可能增加功能的地方埋好(预设)一个钩子,这个钩子并没有实际的意义,当我们需要重新修改或者增加这个地方的逻辑的时候,把扩展的类或者方法挂载到这个点即可。
讲到“钩子”,一定要提前说明的是一种设计模式,那就是行为型设计模式中的模板方法模式,明白了它的话,会让我们更容易理解钩子。
- 模板方法模式是一种基于继承的代码复用,它是一种类行为型模式,在其结构中只存在父类与子类之间的继承关系。通过使用模板方法模式,可以将一些复杂流程的实现步骤封装在一系列基本方法中,在抽象父类中提供一个称之为模板方法的方法来定义这些基本方法的执行次序,而通过其子类来实现某些步骤,从而使得相同的算法框架可以有不同的执行结果。
- 要注意的是,在处理的过程中,要遵循反向控制接口(“好莱坞原则”),这个原则是指父类调用子类的操作,而子类不调用父类的操作。好莱坞原则于模版方法设计模式紧密相关,因为它在父类中实现,除了templateMethod方法外,父类的其他方法都是抽象和受保护的方法。所以,尽管客户实例化一个具体类,但是它调用了父类中实现的方法。
- 有的时候,模板方法函数中可能有一个不想要的步骤,例如,在我们购买商品的时候要计算最终价格,商品价格+运费+服务产生费用,不过在有些活动中,顾客商品价格满100元就可以免运费。这里就要使用到模板方法的钩子。
- 也就是说,在模板方法设计模式中,利用钩子可以将一个方法作为模板方法的一部分,不过不一定会用到这个方法。换句话说,它是方法的一部分,不过它包含一个钩子,可以处理例外的情况,子类可以为算法增加一个可选元素,这样以来,尽管仍然按照父类模板方法建立的顺序执行,但是有可能并不完全按照模板方法期望的那样动作。
优点
1.提高代码复用性 ,将相同部分的代码放在抽象的父类中
2.提高了拓展性 ,将不同的代码放入不同的子类中,通过对子类的扩展增加新的行为
3.实现了反向控制,通过一个父类调用其子类的操作,通过对子类的扩展增加新的行为,实现了反向控制 & 符合“开闭原则”缺点
引入了抽象类,每一个不同的实现都需要一个子类来实现,导致类的个数增加,从而增加了系统实现的复杂度。
在开发中,Vite 开发服务器会创建一个插件容器来调用 Rollup 构建钩子,与 Rollup 如出一辙。
以下钩子在服务器启动时被调用:
以下钩子会在每个传入模块请求时被调用:
以下钩子在服务器关闭时被调用:
请注意 moduleParsed
钩子 不是 在开发中被调用的,因为 Vite 为了性能会避免完整的 AST 解析。
Output Generation Hooks(除了 closeBundle
) 不是 在开发中被调用的。你可以认为 Vite 的开发服务器只调用了 rollup.rollup()
而没有调用 bundle.generate()
.
Vite 独有钩子
Vite 插件也可以提供钩子来服务于特定的 Vite 目标。这些钩子会被 Rollup 忽略。
config
-
类型:
(config: UserConfig, env: { mode: string, command: string }) => UserConfig | null | void
-
种类:
sync
,sequential
在被解析之前修改 Vite 配置。钩子接收原始用户配置(命令行选项指定的会与配置文件合并)和一个描述配置环境的变量,包含正在使用的
mode
和command
。它可以返回一个将被深度合并到现有配置中的部分配置对象,或者直接改变配置(如果默认的合并不能达到预期的结果)。示例
// 返回部分配置(推荐) const partialConfigPlugin = () => ({ name: 'return-partial', config: () =>