• Vite插件开发纪实:vite-plugin-monitor(上)


    背景

    最近在webpack项目里接入了Vite(dev mode),为开发提效。效果是真的猛。

    项目启动速度提升70%-80%,HMR直接碾压webpack dev server

    为了更加精准的计算收益,就需要将Vite启动相关的指标进行上报(启动时间,HMR,页面加载等等时间)

    为此就要通过开发插件收集这些信息,然后通过埋点上报sdk上报到数据分析的平台

    遇到的问题

    通过查阅官方文档并未找到相关的钩子直接获取到这些指标

    但在开发的时候添加 --debug就能很详细的看到所有资源的处理时间,HMR,详细的启动时间等等

    {
        "scripts": {
            "dev": "vite --debug",
        }
    }
    
    npm run dev
    

    图片

    为此只能通过一些hack的手段获取这些指标了,下面将展开详细的介绍

    期望

    通过向目标工程引入插件,通过特定的回掉函数即可获取到debug模式下反馈的各种信息

    准备工作

    比较详细的介绍一下开发步骤

    初始化工程

    创建插件目录

    mkdir vite-plugin-monitor
    
    cd vite-plugin-monitor
    

    初始化pkg.json

    npm init -y
    

    安装必要依赖

    yarn add -D vite typescript @types/node rimraf
    

    添加必要的两个指令dev,build,配置入口文件dist/index.js

    {
        "main": "dist/index.js",
        "scripts": {
            "dev": "tsc -w -p .",
            "build": "rimraf dist && tsc -p ."
        }
    }
    

    其中dev环境下添加了-w(--watch)参数,当文件有变动时,以便实时的进行更新

    rimraf的作用是替代rm -rf指令,且是跨平台的,windows下同样生效

    插件使用typescript开发,更有助于插件后续的维护

    其中build直接使用typescript提供的默认tsc指令,对ts直接进行转换

    根目录创建 tsconfig.json 内容如下

    {
        "compilerOptions": {
          "target": "es2015",
          "moduleResolution": "node",
          "strict": false,
          "declaration": true,
          "noUnusedLocals": true,
          "esModuleInterop": true,
          "outDir": "dist",
          "module": "commonjs",
          "lib": ["ESNext","DOM"],
          "sourceMap": true,
        },
        "include": ["./src"]
      }
    

    src 目录下进行开发,里面存放我们的源码

    目录结构

    最终目录如下

    ├── package.json
    ├── src
    |  ├── index.ts     # 插件入口
    |  ├── types        
    |  |  └── index.ts  # 类型定义
    |  └── utils
    |     └── index.ts  # 工具方法
    ├── tsconfig.json
    

    简单插件示例

    根据插件开发文档,在src/index.ts文件下编写如下简单的代码;

    • name:标识插件的名称
    • apply:标识插件在哪个时期工作(serve|build),默认都会调用
    • config:这个钩子接收原始用户配置(命令行选项指定的会与配置文件合并)和一个描述配置环境的变量
    import type { Plugin } from 'vite';
    
    export default function Monitor(): Plugin {
      return {
        name: 'vite-plugin-monitor',
        apply: 'serve',
        config(userConfig, env) {
          console.log(userConfig);
          console.log(env)
          // 可以做进一步的修改,会自动合入当前的配置
          // return
        },
      };
    }
    

    一个打印Vite配置的插件就搞定了,下面就是测试我们开发的插件

    本地测试插件

    首先是转换我们的ts=> js ,执行前面配置的指令yarn dev,就会看见生成了一个dist目录,里面有转换后的代码

    接着执行npm link在全局生成一个软连接,指向当前项目

    npm link
    

    在一个vite项目里的执行npm link vite-plugin-monitor(monitor根据实际情况替换),向目标项目加入此依赖

    npm link vite-plugin-monitor
    

    接着就可以在Vite项目的vite.config.js配置文件中加入我们的插件了

    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
    import vitePluginMonitor from 'vite-plugin-monitor'
    
    // https://vitejs.dev/config/
    export default defineConfig({
      plugins: [
        vue(),
        vitePluginMonitor()
      ]
    })
    

    接着通过配置的指令启动vite,就能看到我们插件的打印的配置文件内容了

    图片

    由于是通过软连接的方式引入的插件,那么在插件工程里的任意更改都会实时生效,也就避免了频繁的执行yarn add file:localProjectDir

    功能开发

    有了前文的铺垫内容,下面就是功能开发

    获取启动耗时

    项目启动后会在终端中输出ready in xxms

    图片

    为此咱们使用Vs Code在源码中搜一下这个关键字

    图片

    可以看到此部分代码在源码中如下

    const info = server.config.logger.info
    
    // @ts-ignore
    if (global.__vite_start_time) {
      // @ts-ignore
      const startupDuration = performance.now() - global.__vite_start_time
      info(`
      ${chalk.cyan(`ready in ${Math.ceil(startupDuration)}ms.`)}
    `)
    }
    

    这个performance.now()等同于Date.now()即当前时间,通过global.__vite_start_time就能获取到服务启动时间

    我们就从这个info方法入手,给它重定义一下,通过configureServer钩子可以获取到server实例

    index.ts

    import type { Plugin } from 'vite';
    
    export default function Monitor(): Plugin {
      const startTime = global.__vite_start_time
    
      return {
        name: 'vite-plugin-monitor',
        apply: 'serve',
        configureServer(server) {
          const { info } = server.config.logger;
          // 拦截info方法的调用
          server.config.logger.info = function _info(str) {
            // 调用原info方法
            info.apply(this, arguments);
            // 通过字符串内容进行一个简单的判断
            if (str.includes('ready in')) {
              console.log('startupDuration', Date.now() - startTime)
            }
          };
        },
      };
    }
    

    启动一个项目看看效果,成了。
    图片

    HMR时间获取

    热更新时,终端中会出现下面的日志

    图片

    同理源码里搜一搜,能够定位出如下内容

    config.logger.info(
        updates
        .map(({ path }) => chalk.green(`hmr update `) + chalk.dim(path))
        .join('
    '),
      { clear: true, timestamp: true }
    )
    

    暂以打印这个日志的时间作为HMR开始的时间

    let startTime = null
    const { info } = server.config.logger;
    server.config.logger.info = function _info(str) {
      info.apply(this, arguments);
      if (str.indexOf('hmr update') >= 0) {
        startTime = Date.now()
      }
    };
    

    触发HMR时,客户端会发出一个获取资源的请求,请求携带了一个import参数,我们通过这个参数来标识这个特定的请求

    http://localhost:8080/src/pages/home/index.vue?import&t=1632924377207
    

    钩子中的server实例包含middlewares属性可以向上添加自定义的中间件处理方法

    • 通过URL实例解析search参数,然后判断是否包含import&
    • 重定义end方法,在资源传回到客户端后打印耗时
    server.middlewares.use(async (req, res, next) => {
      const { search } = new URL(req.url, `http://${req.headers.host}`);
      if (
        search.indexOf('import&') >= 0
      ) {
        const { end } = res;
        res.end = function _end() {
          // 在资源返回后打印耗时
          end.apply(this, arguments);
          console.log(Date.now() - startTime)
        };
      }
      next();
    });
    

    事实上通过--debug启动服务,能看到在HMR时会打印4个时间

    图片

    目前方法仅仅得到了vite:hmr部分的时间,与实际耗时还有一丝丝差异

    小结

    本篇主要介绍了monitor插件开发的背景,要解决的问题,目标以及开发插件所需的一些列准备工作

    功能开发介绍了启动时间与HMR时间的获取方式

    更加详细的信息目前看来只能通过--debug看到,下一步的计划就是通过某种手段拿到debug下打印的日志内容

    由于时间关系,这部分hack还没完成。准备假期抽时间实现一下。下一篇文章将详细的介绍最终实现。

    "你的指尖,拥有改变世界的力量! " 欢迎关注我的个人博客:https://sugarat.top
  • 相关阅读:
    关于接口和接口中多肽问题的实例
    java 简单的单例 实现
    关于动态数组的问题
    一百以内的杨辉三角
    将阿拉伯数字转为中文大写读法
    sql server 2005 优化方法
    Sql Server 2005 数据库 优秀辅助工具推荐
    SSIS高级内容 系列一
    锁定
    SQL Server 2005查询处理结构用户模式计划(UMS)
  • 原文地址:https://www.cnblogs.com/roseAT/p/15354889.html
Copyright © 2020-2023  润新知