场景:在编写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>
需要实现的功能点
- 让*.vue允许自定义块的存在
- 如何解析获取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处理的文件进行自定义的转换。可以在这个函数中使用正则表达式、路径解析来修改文件的内容。这也恰好满足了现有的需求。