vite4不支持vueCustomBlockTransforms???

242 阅读2分钟

场景:在编写UI库的过程中需要实现代码渲染器

一个简单的栗子:介绍Button组件的用法

我们已经编写好了Button组件,需要创建一个ButtonDemo.vue,在里面添加自定义块demo

<script setup lang='ts'>
import Button from '../../lib/Button.vue'
</script>

<!-- 自定义块 demo -->
<demo>
  支持 shape
</demo>

<template>
  <Button>
    default
  </Button>
  <Button shape="round">
    round
  </Button>
</template>

创建index.vue将Shape作为props传入Demo.vue

<script setup lang='ts'>
import Shape from './Shape.vue'
import Demo from '@/components/Demo.vue'
</script>

<template>
  <div>
    <h1>Button 按钮</h1>
    <p>常用的操作按钮。</p>
    <Demo :component="Shape" />
  </div>
</template>

在Demo.vue中展示自定义块里面的内容以及渲染ButtonDemo.vue的代码

<script lang="ts" setup>
import { computed, ref } from 'vue'
import Prism from 'prismjs'

defineProps({
  component: { type: Object, required: true },
})

</script>

<template>
	<div class="code-renderer">
	<h2>{{ component?.sourceTitle }}</h2>
	<pre><code>{{ component?.sourceCode }}</code></pre>
	</div><
/template>

需要实现的功能点

  1. 让*.vue允许自定义块的存在
  2. 如何解析获取Shape.vue文件中的自定义块和代码,并且给组件添加两个额外的属性:__sourceTitle和__sourceCode。供Demo.vue渲染展示?

我了解到vite提供了vueCustomBlockTransforms

vueCustomBlockTransforms是一个vite选项,它可以让你在vue文件中使用自定义块。自定义块可以用来存放一些特定的代码或内容,比如demo等。你可以在vueCustomBlockTransforms对象中定义一个函数,来返回一个虚拟的js文件,这样就可以在自定义块中使用js语法,像这样:

export default function (Component) {
  Component.__sourceCode = 'Code'
  Component.__sourceTitle = 'Title'
}

可以把这个虚拟的js文件理解为一个高阶函数,它接收一个组件作为参数,然后返回一个增强后的组件。

这恰好符合需求,但是在vite4已经不支持vueCustomBlockTransforms :)

目前只能自己尝试写写插件了

注册插件

// vite.config.ts
import { defineConfig } from 'vite'
import { custom } from './plugins/custom'
export default defineConfig({
  plugins: [
    custom(),
  ],
})

自定义中间件

import { Plugin } from 'vite'
import fs from 'fs'
import { baseParse } from '@vue/compiler-core'

const fileRegex = /lang.demo$/

export function custom():Plugin {
  return {
        name: 'vite-plugin-customBlockTransforms',
        transform: (code,id)=> {
          if (fileRegex.test(id)) {
            const originalUrl = id.split('?')[0]
            const file = fs.readFileSync(originalUrl).toString()
            // ButtonDemo.vue的代码
            const main = baseParse(file).loc.source.trim()
            // code 为自定义块内容
            const body = `export default function (Component) {
                  Component.__sourceCode = ${
                  JSON.stringify(main)
                  }
                  Component.__sourceTitle = ${JSON.stringify(code)}
                }`
            return body
          }
        },
  }
}

transform选项是一个函数,它可以对vite处理的文件进行自定义的转换。可以在这个函数中使用正则表达式、路径解析来修改文件的内容。这也恰好满足了现有的需求。