Vite插件

插件能够了解为一种遵循某种标准,实现某种性能的程序。Vite插件就是赋予Vite更弱小的性能,与Webpack不同的是,Webpack有辨别Loader和Plugin,而在Vite中Loader也是作为Plugin去实现,这也是源于Vite是借助Rollup实现编译。

之前讲过,在生产环境下,Vite是通过Rollup来打包源代码。然而在开发环境下,Vite只会模仿Rollup的行为,之所以在开发环境没有间接调用Rollup的API是为了让性能失去更大的晋升,但同时在保障同一套配置文件体现统一的前提下,可能像 Rollup 一样,可能解析插件对象,并对插件的钩子进行正确的执行和解决。

开发插件最次要的就是要理解钩子,以及它们调用的机会。Vite反对Rollup的大部分钩子,再基于一些优化的思考,减少了一些本身特有的钩子,并且曾经预设了多个Rollup插件。

Vite插件的应用

插件的应用非常简单,只须要三步:

  1. 找到具备所需性能的Vite插件。在找相干技术栈的周边工具的时候,我个别是在github上搜寻awesome xxx
  2. 下载。增加到我的项目的 devDependencies。

     npm i vite-plugin-banner -D 
  3. 在 vite.config.js 配置文件中的 plugins 数组中引入它,能够依据该插件的API来配置相干参数。

    // vite.config.jsimport { defineConfig } from "vite";import banner from 'vite-plugin-banner';export default defineConfig({    plugins: [banner('This is the banner content.')]})

实现一个插件

需要剖析

指标:想要直观的看线上代码是哪个版本
计划:

  • 工夫戳。
  • 版本号。联合ONES的版本布局、Git的tag或者release
  • Git的分支治理。
  • Git的commit hash

表现形式:

  • 通过控制台打印
  • 通过最终输入文件名体现
  • 文件正文

这里咱们抉择,Git的commit hash联合控制台打印的计划(当然并不是这样就不须要Git的版本治理)。

实现需求

Vite插件的构造非常简单,就是一个对象,这个对象有一些必填属性(name)和选填属性(钩子函数),例如:

// vite.config.jsimport { defineConfig } from "vite";export default defineConfig({  plugins: [    {        name: "my-plugin", // 命名要遵循约定        transform(code, id) { // 钩子函数            return code + "var a = 1; console.log(a)"        }    },  ],});

实现需求:

// vite.config.jsimport { defineConfig } from "vite";import { execSync } from "child_process";export default defineConfig({  plugins: [    {      name: "my-plugin",      apply: "build",      transformIndexHtml(html) {        const hash = runGit("git rev-parse HEAD");        const branch = runGit("git rev-parse --abbrev-ref HEAD");        const date = runGit("git log -1 --format=%ci");        function runGit(str) {          return execSync(str).toString().replace("\n", "");        }                return [          {            tag: "script",            children: `                console.info("branch: ${branch}")                console.info("hash: ${hash}")                console.info("date: ${date}")            `,            injectTo: "head",          },        ];      },    },  ],});

封装公布

后面的插件内容都是间接写在了vite.config.js中,当插件多的时候或者想要向社区奉献插件的时候显然这是很不不便保护的。接下来将以最简洁的模式将插件公布到Coding公有镜像库:

  1. 新创建一个我的项目,命名为vite-plugin-git-log

    • 插件应用Vite 特有的钩子,则以vite-plugin-xxx的格局命名,如果用于特定的框架(例如Vue)则以vite-plugin-vue-xxx的格局命名。
    • 插件是能够兼容Rollup的,则以rollup-plugin-xxx的格局命名
  2. 增加package.json:

    { "name": "vite-git-log", "version": "1.0.0", "description": "", "main": "index.js", "scripts": {     "test": "echo \"Error: no test specified\" && exit 1" }, "peerDependencies": {     "vite": "^3.1.0" }, "engines": {     "node": "^14.18.0 || >=16.0.0" }, "author": "xxx", "license": "MIT" }
  3. 新建index.js:

    import { execSync } from "child_process"; export default function (pluginOptions) {     const hash = runGit("git rev-parse HEAD");     const branch = runGit("git rev-parse --abbrev-ref HEAD");     const date = runGit("git log -1 --format=%ci");     function runGit(str) {         return execSync(str).toString().replace("\n", "");     }     return {         name: "vite-plugin-git-log",         apply: "build",         transformIndexHtml(html) {         return [             {             tag: "script",             children: `                     console.info("branch: ${branch}")                     console.info("hash: ${hash}")                     console.info("date: ${date}")                 `,             injectTo: "head",             },         ];         },     }; }
  4. 找管理员开明推送权限。而后依据操作指引配置好了,就能够推送了。
  5. 在我的项目中就能够通过npm install vite-plugin-git-log -D之类的命令装置应用了。

插件钩子

下面的内容次要是先写一个插件体验下,没有讲Vite插件的API,例如下面写过的两个插件,为什么配置的钩子函数不同,不同的钩子又有什么不同?

对于插件开发者来说,最重要的就是要晓得抉择哪个(些)钩子,并在对应的钩子里实现不同的业务,进而达成目标。上面就将钩子做一个总结:

调用机会

  • Build Hooks
    const chunks = rollup.rollup() 执行期间的构建钩子函数
  • Output Generation Hooks
    chunks.generator(write)执行期间的输入钩子函数
  • WatchChange Hooks

    rollup.watch() 执行期间监听文件变动并从新执行构建的钩子函数

调用模式

  • async: 该钩子还可能返回一个解析为雷同类型值的 Promise;否则,钩子被标记为sync
  • first:  如果多个插件实现了雷同的钩子函数,那么会串式执行,从头到尾,然而,如果其中某个的返回值不是 null 也不是 undefined 的话,会间接终止掉后续插件。
  • sequential:  如果多个插件实现了雷同的钩子函数,那么会串式执行,依照应用插件的程序从头到尾执行,如果是异步的,会期待之前处理完毕,在执行下一个插件。
  • parallel:  同上,不过如果某个插件是异步的,其后的插件不会期待,而是并行执行,这个也就是咱们在 rollup.rollup() 阶段看到的解决形式。

插件常见钩子及程序

先来理论运行看一下执行成果:

pnpm dev:

pnpm build:

开发的钩子

钩子类型形容是否Vite独享
configasync, sequential在解析 Vite 配置前调用。能够返回一个将被深度合并到现有配置中的局部配置对象,或者间接扭转配置(如果默认的合并不能达到预期的后果)
configResolvedasync, parallel在解析 Vite 配置后调用。应用这个钩子读取和存储最终解析的配置。当插件须要依据运行的命令做一些不同的事件时,它也很有用。
optionsasync, sequential替换或操作传递给 rollup.rollup 的选项对象
configureServerasync, sequential是用于配置开发服务器的钩子。最常见的用例是在外部 connect 应用程序中增加自定义中间件
buildStartasync, parallel在每个 rollup.rollup 构建中调用。当您须要拜访传递给 rollup.rollup() 的选项时。
transformIndexHtmlasync, sequential转换 index.html 的专用钩子。
resolveIdasync, first定义自定义解析器。解析器可用于例如定位第三方依赖。
loadasync, first定义一个自定义加载器。
transformasync, sequential可用于转换单个模块。
buildEndasync, parallel服务器敞开时被调用。在 rollup 实现捆绑,但在调用 generate 或 write 之前调用

构建输入的钩子

钩子类型形容
outputOptionssync, sequential替换或操作传递给 bundle.generate() 或 bundle.write() 的输入选项对象。
renderStartasync, parallel每次调用 bundle.generate() 或 bundle.write() 时最后调用。
augmentChunkHashsync, sequential用于减少单个chunk的hash。
renderChunkasync, sequential用于转换单个chunk。
generateBundleasync, sequential在 bundle.generate() 完结时或在将文件写入 bundle.write() 之前立刻调用。
transformIndexHtmlasync, sequential转换 index.html 的专用钩子。
writeBundleasync, parallel写入所有文件后,仅在 bundle.write() 开端调用。
closeBundleasync, parallel整个构建输入实现时调用

插件程序

一个 Vite 插件能够额定指定一个 enforce 属性(相似于 webpack 加载器)来调整它的利用程序。enforce 的值能够是prepost。解析后的插件将依照以下顺序排列:

  • Alias
  • 带有 enforce: 'pre' 的用户插件
  • Vite 外围插件
  • 没有 enforce 值的用户插件
  • Vite 构建用的插件
  • 带有 enforce: 'post' 的用户插件
  • Vite 后置构建插件(最小化,manifest,报告)