需求
要做一个中台的详情页,和其他中台详情页不同的是,这个详情页的组件是希望通过配置 json 文件去动态的生成的。
例子
[{
key: 'search',
label: '搜索内容',
template: {
type: 'a-input',
config: {
placeholder: '请输入',
style: { width: '100%' }
allowClear: true
}
}
},
{
key: 'status',
label: '活动状态',
template: {
type: 'a-select',
options: [a, b, c],
config: {
allowClear: true,
placeholder: '请选择'
}
}
}]
例如上面的 json 内容,需要生成下面的 vue 组件。(部分变量做了简写处理)
<a-form-item label="搜索内容">
<a-input
placeholder="请输入"
v-model:value="search"
:style="{ width: '100%' }"
:allowClear="true"
/>
</a-form-item>
<a-form-item label="活动状态">
<a-select
placeholder="请选择"
v-model:value="state"
:allowClear="true"
>
<a-select-option v-for="item in options" :key="item" :value="item">{{ item }}</a-select-option>
</a-select>
</a-form-item>
问题
如何动态的将 config 字段的指令和方法动态的解析到组件的中去?
一开始打算使用全部写一遍然后一个个判断的笨方法,但是因为使用了 antdv 组件库,且每个组件都有大量的 API 和事件,全部写一遍不太现实,所以才想着怎么样去动态的生成他。
实践过的方案
h 函数
第一个想到的就是使用 h 函数去实现,但是转念一想肯定不行,因为 h 函数生成的是 vNode 而并不是组件。h('a-input', {}, 'test') 这样的代码倒是可以由上述 json 文件动态生成,但它渲染出来的直接就是 <a-input>test</a-input> 这样的 DOM 元素,没有任何组件上的功能和样式。这样并不能达成我的需求,所以不行。
jsx
jsx 可以直接返回 <a-input>test</a-input>,且页面上也能按照组件的样子渲染出来,但是动态的添加 config 却依然是一个问题。
h 函数和 resolveComponent
- resolvecomponent
在官网找到了
resolveComponent这个api,他可以加载已有的组件,经过实践,该方法确实能正确的识别加载对应的组件。接下来就要解决加载指令的问题。
resolveDirective
- resolveDirective
resolveDirective这个方法可以解析指令,然后通过withDirectives就可以把解析出来的指令挂载到前面使用resolveComponent解析出来的组件上。
然而理想是丰满的,现实却是骨感的。在挂载指令的时候却又碰到了问题,我完全照搬了官网的教程去引入 resolveDirective 去解析指令,但是反馈的却是一直报错。。。
<template>
<testTemp />
</template>
<script setup>
import { h, resolveComponent, withDirectives, resolveDirective } from "vue";
const testTemp = () => {
// 这一步确定没有问题
const aInput = resolveComponent('a-input');
// 这里的 foo,打印出来的却是 undefined
const foo = resolveDirective('bind');
console.log(`foo 打印内容为:`, foo);
if (foo) {
return withDirectives(h(aInput), [[foo, 'test']])
}
}
</script>
他却始终报 [Vue warn]: Failed to resolve directive: bind,无法解析 bind 指令。页面上没有显示任何东西,连前面确定可以用的 resolveComponent 都没有了,我起初以为是 a-input 上确实没有 bind 指令,但是后来发现并不是,我无论挂载什么指令他都是报这个 warning,且 resolveDirective 返回的结果一直都是 undefined。
检查了我的 vue 版本,是 3.2.19,没有问题的。也注意到文档上写了上面的三个 API 只能在 setup 或 render 函数中执行,我也分别尝试了 <script setup> 语法糖和传统的 export default 导出 setup 或 render 函数也都是一样的结果。
未解决
目前并没有一个完美的解决方案,且看起来完美的 resolveDirective 方案也没有正常的工作。不知道各位有没有碰到过这个报错?或者能解决这个需求的方法都可以,感谢各位大佬!