以往,面试官:“你自己开发过 Webpack 的插件吗?”
现在,面试官:“你自己开发过 Vite 的插件吗?”
如果你还在纠结 Webpack 的插件该开发姿势的话,点我这篇文章# 手把手带你开发webpack5.x - plugin(保姆教程)。 写 Vue 的朋友肯定最爱 Vite,当然写 React 的朋友肯定也爱,反正我爱,毕竟谁不希望自己快一点呢>_<
关于 Vite 你肯定需要知道的两点就是, 1. Vite 的原理是什么,为什么它这么快? 2. Vite 的插件是如何开发出来的,原理下一篇来唠,本篇文章先聊后者, Vite 插件的开发,手摸手,带你又快又稳的get它。
Vite 作为一个单纯的构建工具,根 Webpack 一样,其提供了很多 生命周期 钩子,这些钩子主要是在 Vite 从开始构建,到结束构建的过程中 提供给程序员使用的。旨在让你可以在构建中途的某个时间点,定制化一些功能
Vite 是基于 Rollup 这个构建工具来封装的,所以 Vite 中的一部分钩子其实就是Rollup 中的钩子(不知道Rollup的朋友不要有心理负担,一样能看的明白)
Vite 的钩子主要分为两类:
- 通用钩子(也就是沿用了Rollup的钩子)
- 独有的钩子
!!!了解这些钩子函数的执行时间节点非常重要,强烈建议你跟着文章按顺序过一遍再看后面的代码实操
在介绍这些钩子函数执行效果之前,我们先用一张图先大体介绍一下 Vite 的基本工作流程
知道 Vite 是靠 Http 服务不断向浏览器发送整个项目需要的各个文件资源后,再来看它的钩子们
通用钩子:
options
这是构建阶段的第一个钩子,通常用于插件开发中的参数阅读选项
1
2myPlugin({
3 name: '蜗牛',
4 age: 18
5})
buildStart
这是构建阶段的第二个钩子,读取到入口文件后开始构建。
buildStart
钩子函数的主要作用包括但不限于以下几点:
- 自定义任务:你可以在构建开始前执行自定义任务,例如清理临时文件、生成一些构建配置、执行前置操作等。
- 日志记录:你可以在构建开始前添加一些日志记录,以记录构建过程的开始时间、项目信息等,以便后续分析和调试。
- 状态检查:在构建开始前,你可以执行一些状态检查,确保构建所需的条件满足,如果有问题,可以提前终止构建并给出错误提示。
- 设置环境变量:你可以在构建开始前设置一些环境变量,以影响构建过程中的行为,例如根据不同的环境配置不同的构建选项。
resolveId
主要用于自定义模块解析的行为。模块解析是指当你在代码中导入模块时,Vite 需要确定模块的位置和如何加载它。resolveId
钩子函数允许你在模块解析过程中介入,以满足特定的项目需求。
1// 举个例子
2import { createApp } from 'Vue'
3
4// 当Vite执行到需要解析这种模块加载的代码时,就会触发resolveId钩子
5
61. **自定义模块解析规则**:你可以使用 `resolveId` 钩子函数来添加自定义的模块解析规则。例如,你可以为特定的文件扩展名或文件夹路径设置自定义解析逻辑。
71. **模块别名**:通过 `resolveId` 钩子函数,你可以实现模块别名功能,将某个模块的导入路径重定向到另一个路径,以简化模块导入。
81. **动态加载模块**:你可以在 `resolveId` 中执行异步操作,例如从远程服务器加载模块或根据环境条件选择不同的模块实现。
91. **解析外部依赖项**:如果你的项目依赖于不同的包管理器(例如 npm、Yarn、pnpm),你可以使用 `resolveId` 钩子来处理这些不同包管理器的依赖项解析差异。
101. **增强性能**:通过自定义模块解析逻辑,你可以优化模块的加载方式,以提高项目的性能。例如,你可以将某些模块预构建,以减少加载时间。
load
执行时间点:在模块加载时。
使用场景:用于自定义模块加载逻辑,例如加载动态数据或从外部源加载模块
transform
执行时间点:在模块代码构建期间。
使用场景:用于修改模块的源代码,可以在构建期间对模块进行转换和处理,例如添加额外的代码、转换特定格式的文件等。
比如:Vite 在加载到 Vue 项目中的 main.js 后我们可以在 transform 钩子中对 main.js的代码做一些修改
buildEnd
作用:buildEnd
钩子函数在 Vite 构建结束后触发。
使用场景:你可以使用 buildEnd
钩子来执行一些与构建结束相关的操作,例如生成构建报告、自动化部署、通知团队构建已完成等。这个钩子通常用于处理构建后的事务。
closeBundle
作用:closeBundle
钩子函数在 Vite 打包生成 bundle 文件时触发。
使用场景:你可以使用 closeBundle
钩子来执行一些与打包后的 bundle 文件相关的操作,例如自动化地上传 bundle 文件到 CDN、生成版本号、进行代码压缩或加密等。这个钩子通常用于处理 bundle 文件的后续处理。
以上 7 个 钩子是 Vite 中的通用钩子
独有的钩子:
config
: 允许你在 Vite 配置对象被创建之前对其进行修改和扩展。这个钩子函数在 Vite 配置加载过程中的早期阶段被触发,允许你动态地修改 Vite 的配置,以满足项目的特定需求。
场景举例:
自定义配置:你可以在
config
钩子中添加、修改或删除 Vite 配置的属性和选项,以适应项目的需求。例如,你可以修改构建输出目录、设置自定义别名、更改开发服务器的选项等。
configResolved
: 用于在 Vite 配置对象被解析和应用后执行自定义操作。这个钩子函数在配置加载过程的较早阶段触发,允许你检查和修改已解析的 Vite 配置。
场景举例:
配置检查与修改:你可以在
configResolved
钩子函数中检查和修改 Vite 配置。这通常用于在配置加载后动态地调整配置选项,以适应不同的项目需求。
configureServer
: 用于配置开发服务器。这个钩子函数在 Vite 开发服务器启动之前执行,允许你自定义开发服务器的行为。
场景举例:
添加中间件:你可以在
configureServer
中添加自定义中间件到开发服务器中。这使得你可以处理请求、修改响应、添加身份验证等。
-
configurePreviewServer
: 与 configureServer 相同,但用于预览服务器。 -
transformIndexHtml
: 允许你在构建过程中修改生成的 HTML 文件。这个钩子函数在生成最终的index.html
文件之前执行,允许你自定义 HTML 内容或添加额外的标签、脚本等。 -
handleHotUpdate
: 用于在模块发生热更新(Hot Module Replacement,HMR)时执行自定义逻辑。HMR 是一种开发工具,允许你在不刷新整个页面的情况下替换、添加或删除模块,以加快开发过程。
场景举例: 动态加载模块:你可以在热更新时动态加载新的模块,以实现按需加载或懒加载的效果。
以上 6 个 就是 Vite 中独有的钩子函数,Vite 总共提供了 13 个 钩子函数给开发人员,我们可以在打造插件的之前考虑清楚你的插件的功效,从而决定用哪个时间节点的钩子来完成
先制定一个需求,偶然,我看到了掘金官方网站的控制台
这个打印有点意思哦,那我们就以这个打印效果为目标,实现一个 Vite 插件,在项目运行起来后我们也能在控制台看到一些奇思妙语
声明一下:控制台的打印明明只要在项目中直接 console.log(‘xxx’) 就可以了,我为啥非要写一个插件来实现?我们是为了学习 Vite 的插件的开发才刻意这么干的,不是吃饱了撑得!
思路: 在项目被 Vite 构建的过程中,当 Vite 读取到项目全局的 js 文件时,我们给它写入一个有趣的 console.log(),就这么简单
所以如果我们在问,你想用哪个钩子函数来实现效果,你有答案了吗?
接下来的步骤就变得简单了起来
- 用 Vite 创建一个 Vue3 的项目
- 在项目的根目录创建一个 plugins 文件夹,用来写插件
- 再在其中创建 randomLetterPlugin.js 文件
这里我们干了什么:
- 抛出一个函数 randomLetterPlugin (这是 Vite 的语法,插件必须是一个函数)
- 里面 return 一个对象 (也是 Vite 固定语法)
- 对象中,设置 name 属性 (也是 Vite 固定语法)
- 紧接着使用了一个钩子 configureServer (查询第一点我们对所有钩子的介绍,该钩子函数执行时间为 Vite 启动 HTTP 服务时)
这么干能实现浏览器控制台的打印吗?
当然不行
我只是在这里给大家测试一下效果,既然是在启动 HTTP 服务时触发的,那就意味着,我能在终端执行 yarn dev 的时候看到这个函数的执行结果
但是现在依然测试不了,我们要让这个插件生效
- 在 vite.config.js 中调用我们的插件
此时再运行项目测试一下:
没问题!果然不出所料
那么要实现浏览器控制台的打印,按照我们的思路,我得读取到 main.js 的代码,再偷偷修改这份代码,所以我们要用到的 钩子是 transform
增加部分:
- 参数 code 是被构建出来之后的完整代码
- 找到 main.js 文件,在其中拼接如上的代码
注意:
- 这部分代码并不会真正的直接出现在开发中的 main.js 中,只是在编译后被加入,我们看本地的main.js是没有变化的
- 相同的 printRandomLetter 函数再次写一遍是因为写在 插件文件中的代码是不能直接作用于项目逻辑中的,只在编译过程生效
这么简单??
没错
看效果
每次刷新项目,默认会在控制台打印一句随机的话术,这个插件真的很简单呀!
控制台中内容的样式怎么写??? 检查掘金的源码,你会看到
我们翻译一下这份代码:
%c
是一个控制台输出的格式占位符,它表示后面的文本应用特定的样式。function fixHydrogen() { ... }
这个函数包含了一些条件判断和DOM操作,用于修改网页的样式。
fixHydrogen 函数我们用不上,所以直接 %c
就够啦,学到了学到了…
那就只需要修改这一行:
最后的效果:
完美!
最后你已经发现了,熟悉 Vite 的钩子函数是开发插件的基础,当然我们不需要去背这些钩子,学习应该是,掌握一个知识点的结构和边界,绝不是死记硬背,不记得的细枝末节,搜索引擎会帮助你