1. 如何開發(fā)一個 Vite 插件?以 vite-plugin-monitor 為例

        共 7873字,需瀏覽 16分鐘

         ·

        2021-10-15 00:55

        背景

        最近在webpack項目里接入了Vite(dev mode),為開發(fā)提效。效果是真的猛。

        項目啟動速度提升70%-80%,HMR直接碾壓webpack dev server

        為了更加精準的計算收益,就需要將Vite啟動相關的指標進行上報(啟動時間,HMR,頁面加載等等時間)

        為此就要通過開發(fā)插件收集這些信息,然后通過埋點上報sdk上報到數(shù)據(jù)分析的平臺

        遇到的問題

        通過查閱官方文檔[1]并未找到相關的鉤子直接獲取到這些指標

        但在開發(fā)的時候添加 --debug就能很詳細的看到所有資源的處理時間,HMR,詳細的啟動時間等等

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

        為此只能通過一些hack的手段獲取這些指標了,下面將展開詳細的介紹

        期望

        通過向目標工程引入插件,通過特定的回調函數(shù)即可獲取到debug模式下反饋的各種信息

        準備工作

        比較詳細的介紹一下開發(fā)步驟

        初始化工程

        創(chuàng)建插件目錄

        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環(huán)境下添加了-w(--watch)參數(shù),當文件有變動時,以便實時的進行更新

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

        插件使用typescript開發(fā),更有助于插件后續(xù)的維護

        其中build直接使用typescript提供的默認tsc指令,對ts直接進行轉換

        根目錄創(chuàng)建 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 目錄下進行開發(fā),里面存放我們的源碼

        目錄結構

        最終目錄如下

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

        簡單插件示例

        根據(jù)插件開發(fā)文檔,在src/index.ts文件下編寫如下簡單的代碼;

        • name:標識插件的名稱
        • apply:標識插件在哪個時期工作(serve|build),默認都會調用
        • config:這個鉤子接收原始用戶配置(命令行選項指定的會與配置文件合并)和一個描述配置環(huán)境的變量
        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配置的插件就搞定了,下面就是測試我們開發(fā)的插件

        本地測試插件

        首先是轉換我們的ts=> js ,執(zhí)行前面配置的指令yarn dev,就會看見生成了一個dist目錄,里面有轉換后的代碼

        接著執(zhí)行npm link在全局生成一個軟連接,指向當前項目

        npm link

        在一個vite項目里的執(zhí)行npm link vite-plugin-monitor(monitor根據(jù)實際情況替換),向目標項目加入此依賴

        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,就能看到我們插件的打印的配置文件內容了

        圖片

        由于是通過軟連接的方式引入的插件,那么在插件工程里的任意更改都會實時生效,也就避免了頻繁的執(zhí)行yarn add file:localProjectDir

        功能開發(fā)

        有了前文的鋪墊內容,下面就是功能開發(fā)

        獲取啟動耗時

        項目啟動后會在終端中輸出ready in xxms

        圖片

        為此咱們使用Vs Code在源碼[2]中搜一下這個關鍵字

        圖片

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

        const info = server.config.logger.info

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

        這個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(thisarguments);
                // 通過字符串內容進行一個簡單的判斷
                if (str.includes('ready in')) {
                  console.log('startupDuration'Date.now() - startTime)
                }
              };
            },
          };
        }

        啟動一個項目看看效果,成了。

        HMR時間獲取

        熱更新時,終端中會出現(xiàn)下面的日志

        圖片

        同理源碼里搜一搜,能夠定位出如下內容

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

        暫以打印這個日志的時間作為HMR開始的時間

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

        觸發(fā)HMR時,客戶端會發(fā)出一個獲取資源的請求,請求攜帶了一個import參數(shù),我們通過這個參數(shù)來標識這個特定的請求

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

        鉤子中的server實例包含middlewares屬性可以向上添加自定義的中間件處理方法

        • 通過URL實例解析search參數(shù),然后判斷是否包含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(thisarguments);
              console.log(Date.now() - startTime)
            };
          }
          next();
        });

        事實上通過--debug啟動服務,能看到在HMR時會打印4個時間

        圖片

        目前方法僅僅得到了vite:hmr部分的時間,與實際耗時還有一絲絲差異

        小結

        本篇主要介紹了monitor插件開發(fā)的背景,要解決的問題,目標以及開發(fā)插件所需的一些列準備工作

        功能開發(fā)介紹了啟動時間與HMR時間的獲取方式

        更加詳細的信息目前看來只能通過--debug看到,下一步的計劃就是通過某種手段拿到debug下打印的日志內容

        由于時間關系,這部分hack還沒完成。準備假期抽時間實現(xiàn)一下。下一篇文章將詳細的介紹最終實現(xiàn)。

        • 倉庫源碼[3]

        參考資料

        [1]

        官方文檔: https://vitejs.dev/guide/api-plugin.html

        [2]

        源碼: https://github1s.com/vitejs/vite

        [3]

        倉庫源碼: https://github.com/ATQQ/vite-plugin-monitor


        瀏覽 260
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 国产21在线 | 超碰啪啪| 航空美女一级大毛片 | 人成在线网站 | 日韩人妻无码一区二区三区视频 |