轮子项目中写了一个Vite的插件,实现了处理自定义命名标签的功能

大纲链接 §

[toc]


插件需求

  • 自定义块转换来转换 SFC 中的 <demo>常规用法</demo>
  • vite 在遇到 vue 文件的时候如何处理自定义块
  • 封装插件方法,使用时传入需要转换的标签名 addSrcFromCustomBlock('demo')

创建 plugins/addSrcFromCustomBlock.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import {baseParse, ElementNode} from '@vue/compiler-core';
import fs from 'fs';

// 自定义块转换
// 可以告诉 vite 在遇到 vue 文件的时候如何处理自定义块 <example>
// 获取组件源码 Component.__sourceCode
// 获取组件标题 Component.__sourceCodeTitle 等价于 code
export function addSrcFromCustomBlock(customBlockName: string) {
  const regexp = new RegExp(`vue&type=${customBlockName}`);
  return {
    name: 'vueCustomBlockTransforms',
    transform: (code: string, id: string) => {
      if (!regexp.test(id)) {
        return;
      }
      const path = id
        .replace(`?vue&type=${customBlockName}&index=0&lang.${customBlockName}`,
          '');
      const fileString = fs.readFileSync(path).toString();
      const parsed = baseParse(fileString).children.find((n) => (n as ElementNode).tag === 'demo');
      const scriptTag = baseParse(fileString).children.find(n => (n as ElementNode).tag === 'script');
      // demo 标题
      const title = code; // @ts-ignore // const title = parsed.children[0].content;
      // 去掉demo标签和纯script后需要显示的代码主体
      const main = fileString.split(parsed.loc.source).join('').split(scriptTag.loc.source).join('').trim();

      return `export default function (Component) {
        Component.__sourceCode = ${JSON.stringify(main)}
        Component.__sourceCodeTitle = ${JSON.stringify(title)}
      }`.trim();
    },
  };
}

  • 核心方法是 transform(code: string, id: string) {}

引入到 vite.config.ts

1
2
3
4
5
6
7
8
9
// vueCustomBlockTransforms
import {addSrcFromCustomBlock} from './plugins/addSrcFromCustomBlock';

export const basicConfig = defineConfig({
  plugins: [
    addSrcFromCustomBlock('demo')
  ],
});


vite 插件 rollup 文档的 参数命名误导 id

  • 处理自定义标签<demo></demo>
  • vite 1.x 迁移 自定义块转换
  • transform(code, id)中的id其实所解析到的文件的 url
  • 使用正则匹配/vue&type=demo/.test(id)
  • 去除多余的查询参数得到该文件路径
    • const path = id.replace(`?vue&type=demo&index=0&lang.demo`,'');
  • 按路径读取文件内容const fileString = fs.readFileSync(path).toString();
  • 找出<demo></demo>标签节点,并且得到文本
    • const parsed = baseParse(fileString).children.find(n => n.tag === 'demo');
    • const title = parsed.children[0].content;
  • 得到代码主体const main = fileString.split(parsed.loc.source).join('').trim();
  • 给组件附加属性
    • Component.__sourceCode = ${JSON.stringify(main)}
    • Component.__sourceCodeTitle = ${JSON.stringify(title)}
  • 在模板字符串中返回 方法
    • return `export default function (Component) { Component.__sourceCode = ${JSON.stringify(main)} Component.__sourceCodeTitle = ${JSON.stringify(title)} }`.trim();

如何解决

  • TLDR 如果不想通读文档的话,只能在开发过程中使用console.log来调试


参考文章

相关文章


  • 作者: Joel
  • 文章链接:
  • 版权声明
  • 非自由转载-非商用-非衍生-保持署名