手把手教你写一个 Vite 插件

194 阅读4分钟

今天想试着写一个vite 插件,在网上找了很多案例。

找到了这个文档,今天把他简化一下 Vite 实战:手把手教你写一个 Vite 插件 - 掘金 (juejin.cn)

创建一个测试文件

这里我们把替换俩个字换成 下面的 md 文件里面的内容

image.png

开始看代码

我们先在 vue.config.js 里面把 文件引入

image.png

import markdownPlugin from './cs.cjs'
plugins: [ 
      markdownPlugin()
    ],

cs.cjs

// 我们先简单介绍一下,插件的各个函数

/**
 *
 * @param {*} customOptions  穿进来的配置
 * @returns
 */
function vitePluginStyleVWLoader (customOptions) {

  return {
    // 插件名称
    name: "vite-plugin-style-vw-loasadasdder",

    /**
     * build: 在 Vite 构建时调用
     * serve: 在 Vite 加载模块时调用
     * 默认: 都会调用
     */
    apply: 'build',
    /** 强制插件排序
        * pre 在 Vite 核心插件之前调用该插件
        * post 在 Vite 核心插件之后调用该插件
        * 默认: post
        */
    enforce: 'post', //在 Vite 核心插件之前调用该插件(就是在vite没有编译化代码前调用,) || post在 Vite 构建插件之后调用该插件

    /**
     * closeBundle:
     * 在 `vite` 本地服务关闭前,`rollup` 输出文件到目录前调用
     * (白话文:就是 build 打包之后调用的钩子函数)
     */
    closeBundle: {
      sequential: true,
      async handler () {
        // console.log(zipName[0])
        console.log('');
      }
    },

    /**
     *handleHotUpdate 热更新 在 src 下面的文件中修改就会执行
     * @param {*} file  file 修改的文件路径
     * @param {*} server vite 服务实例 返回 的是个 map对象
     * @param {*} modules 修改的文件对应的模块
     */
    handleHotUpdate (ctx) {
      const { file, server, modules } = ctx;

    },
    /**
     *修改了引用文件的,=返回对应的参数
     * @param {*} code :文件的源码字符串
     * @param {*} id 文件的路径path
     * @param {*} opt
     * @returns {ssr:boolean} 我也不知道是啥,说是在编译和
     */
    transform (code, id, opt) {

      return { code };

    }
  }
}


module.exports = vitePluginStyleVWLoader;

代码实现

Vite 实战:手把手教你写一个 Vite 插件 - 掘金 (juejin.cn)

这个博主是实现了一个组件,把md 文件引入进去。我们这个把文字替换成 md 文件里面的内容

// 当文件变化的时候,会优先执行 transform 钩子函数
 
    transform (code, id, opt) {
      const template = /<template\b[^>]*>[\s\S]*<\/template>/g;
		
       // 这里犯个懒,这里应该是用正则筛选出 是不是 vue文件,简单的写死了路径
      if (id !== 'D:/ZHUO MIAN/V3/my+ts/src/views/CeShi/index.vue') return code;
      //这里拿到了 修改的 vue 文件里面的内容,筛选出 template 标签里面的字符串,进行替换,返回出去即可
      const mdList = code.match(template);
      const readdirSync = file.readFileSync('D:/ZHUO MIAN/V3/my+ts/src/views/CeShi/as.md', 'utf-8',);
      const res = mdList[0].replace(/替换/g, readdirSync);
      let transformCode = code.replace(template, res);

      return transformCode;

    }

但是,我们的vue 文件改变了,vite 会热更新,但是引用的 md 文件就不会热i更新了。

transform 只会触发被 引入在页面的文件

那么 handleHotUpdate 函数就出厂了。他会把 src 下面所有的修改对的文件都会触发

 handleHotUpdate (ctx) {
      const { file, server, modules } = ctx;
      // 过滤非 md 文件
      if (file !== 'D:/ZHUO MIAN/V3/my+ts/src/views/CeShi/as.md') return;

      // 找到引入该 md 文件对应的 vue 文件
      const relationId = 'D:/ZHUO MIAN/V3/my+ts/src/views/CeShi/index.vue';
     
      // 找到该 vue 文件的 moduleNode 返回的是个 map 对象需要拿到里面的参数
      const relationModule = [...server.moduleGraph.getModulesByFile(relationId)][0];

      /** 发送 websocket 消息,进行单文件热重载
       * type: 'update' 更新
       * updates: [
       *    {
       *      type: 'js-update' js更新
       *      path: relationModule.file 修改的文件路径
       *      acceptedPath: relationModule.file 修改的文件路径
       *      timestamp: new Date().getTime() 时间戳
       *    }
       *  ]
       */
      server.ws.send({
        type: 'update',
        updates: [
          {
            type: 'js-update',
            path: relationModule.file,
            acceptedPath: relationModule.file,
            timestamp: new Date().getTime()
          }
        ]
      });

      // 指定需要重新编译的模块
      return [...modules, relationModule]
    },

这样就完成了。

全部代码

/* eslint-disable */

const path = require('path');
const file = require('fs');
// const MarkdownIt = require('markdown-it');
// import { Plugin } from 'vite'





function markdownPlugin () {
  const template = /<template\b[^>]*>[\s\S]*<\/template>/g;
  return {
    // 插件名称
    name: 'vite:markdown',
    // 该插件在 plugin-vue 插件之前执行,这样就可以直接解析到原模板文件
    enforce: 'pre',

   /**
     *handleHotUpdate 热更新 在 src 下面的文件中修改就会执行
     * @param {*} file  file 修改的文件路径
     * @param {*} server vite 服务实例 返回 的是个 map对象
     * @param {*} modules 修改的文件对应的模块
     */
    handleHotUpdate (ctx) {
      const { file, server, modules } = ctx;
      // 过滤非 md 文件
      if (file !== 'D:/ZHUO MIAN/V3/my+ts/src/views/CeShi/as.md') return;

      // 找到引入该 md 文件的 vue 文件
      const relationId = 'D:/ZHUO MIAN/V3/my+ts/src/views/CeShi/index.vue';
      // 找到该 vue 文件的 moduleNode
      const relationModule = [...server.moduleGraph.getModulesByFile(relationId)][0];

      /** 发送 websocket 消息,进行单文件热重载
       * type: 'update' 更新
       * updates: [
       *    {
       *      type: 'js-update' js更新
       *      path: relationModule.file 修改的文件路径
       *      acceptedPath: relationModule.file 修改的文件路径
       *      timestamp: new Date().getTime() 时间戳
       *    }
       *  ]
       */
      server.ws.send({
        type: 'update',
        updates: [
          {
            type: 'js-update',
            path: relationModule.file,
            acceptedPath: relationModule.file,
            timestamp: new Date().getTime()
          }
        ]
      });

      // 指定需要重新编译的模块
      return [...modules, relationModule]
    },

    // 代码转译,这个函数的功能类似于 `webpack` 的 `loader`
    transform (code, id, opt) {
      if (id !== 'D:/ZHUO MIAN/V3/my+ts/src/views/CeShi/index.vue') return code;
      const mdList = code.match(template);
      const readdirSync = file.readFileSync('D:/ZHUO MIAN/V3/my+ts/src/views/CeShi/as.md', 'utf-8',);
      const res = mdList[0].replace(/替换/g, readdirSync);
      let transformCode = code.replace(template, res);

      return transformCode;
    }
  }
}

// overwrite for cjs require('...')() usage
module.exports = markdownPlugin
markdownPlugin['default'] = markdownPlugin