vue2,vue3中在template中使用component组件is属性绑定jsx的vnode
vue2中在template中使用component组件is属性绑定jsx的vnode
方式一使用自定义指令
<template>
<el-form-item
v-for="(item, index) in attrsList"
:key="`attrs_list_${index}`"
:label="item.label"
>
<div :class="['normal-content', item?.className]">
<div
:class="['normal-content', item?.className]"
v-insert-vnode="item.value"
/>
</div>
</el-form-item>
</template>
<script>
// 引入 Vue
import Vue from "vue";
// 全局注册 v-focus 指令
Vue.directive("insert-vnode", {
// 当绑定元素插入到 DOM 中时
inserted: (el, binding) => {
const vnode = binding.value;
// 创建一个临时的 Vue 实例来渲染 VNode
const tempVm = new Vue({
render: (h) => vnode,
});
// 挂载临时实例并将生成的 DOM 插入到目标元素中
tempVm.$mount();
el.appendChild(tempVm.$el);
},
});
export default {
name: "DemoTest",
data() {
return {
formData: {
name: "",
mobile: "",
},
};
},
methods: {
// 包装 JSX 为 Vue 组件
wrapJsx(renderFn) {
return {
render: renderFn,
};
},
},
computed: {
attrsList() {
const { name, mobile } = this.formData;
return [
{
label: "用户姓名:",
value: () => <span>{name || "--"}</span>,
},
{
label: "手机号:",
value: () => <span>{mobile || "--"}</span>,
},
];
},
},
};
</script>
方式二利用函数自定义函数wrapJsx
<template>
<el-form-item
v-for="(item, index) in attrsList"
:key="`attrs_list_${index}`"
:label="item.label"
>
<div :class="['normal-content', item?.className]">
<component :is="wrapJsx(item.value)" />
</div>
</el-form-item>
</template>
<script>
export default {
name: "InspectDialog",
methods: {
// 包装 JSX 为 Vue 组件
wrapJsx(renderFn) {
return {
render: renderFn,
};
},
},
computed: {
attrsList() {
const { name, mobile, psName, isPsBlack, permitCode, creditCode } =
this.formData;
return [
{
label: "用户姓名:",
value: (h) => h("span", {}, [name || "--"]),
},
{
label: "手机号:",
value: (h) => h("span", {}, [mobile || "--"]),
},
{
label: "企业名称:",
value: (h) => h("span", {}, [psName || "--"]),
},
{
label: "是否黑名单企业:",
value: (h) => h("span", {}, [isPsBlack ? "是" : "否"]),
},
{
label: "排污许可编号:",
value: (h) => h("span", {}, [permitCode || "--"]),
},
{
label: "统一社会信用代码:",
value: (h) => h("span", {}, [creditCode || "--"]),
},
];
},
},
};
</script>
方式二使用自定义RenderNode组件(推荐实现方式)
<template>
<el-form-item
v-for="(item, index) in attrsList"
:key="`attrs_list_${index}`"
:label="item.label"
>
<div :class="['normal-content', item?.className]">
<render-node :render="item.value" />
</div>
</el-form-item>
</template>
<script>
// 创建一个简单的渲染组件
const RenderNode = {
functional: true,
props: {
render: {
type: Function,
required: true,
},
},
render(h, { props }) {
return props.render(h);
},
};
export default {
components: {
RenderNode,
},
data() {
return {
formData: {
name: "",
mobile: "",
},
};
},
computed: {
attrsList() {
const { name, mobile } = this.formData;
return [
{
label: "用户姓名:",
value: () => <span>{name || "--"}</span>,
},
{
label: "手机号:",
value: () => <span>{mobile || "--"}</span>,
},
// ... 其他项目
];
},
},
};
</script>
方式三自定义JsxRender组件
<!--
* @Author: yeminglong
* @Date: 2024-11-25 14:55:21
* @LastEditors: yeminglong
* @LastEditTime: 2025-02-25 23:07:44
* @Description:
-->
<template>
<div>
<h1>Hello {{ userName }}</h1>
<div>
<div v-for="(item, index) in list" :key="`item${index}`">
<JsxRender :node="item"> </JsxRender>
</div>
</div>
</div>
</template>
<script>
const JsxRender = {
render(h) {
return <span>{this.node}</span>;
},
props: {
node: {
type: Object,
default: null,
},
},
};
export default {
name: "JsxTest",
props: {},
components: {
JsxRender,
},
data() {
return {
userName: "jsx Test",
list: [
<el-button type="success">{123123}</el-button>,
<el-button type="success">{123123}</el-button>,
],
};
},
methods: {},
mounted() {},
created() {},
beforeDestroy() {},
};
</script>
<style scoped></style>
vue3中在template中使用component组件is属性绑定jsx的vnode
<template>
<h2>div list</h2>
<my-list :list="list" />
<div v-for="(item, index) in list" :key="index">
<my-single :value="item.value" />
</div>
</template>
<script setup lang="tsx">
defineOptions({
name: 'MyTest'
})
const MyButton = props => {
return (
<el-button
type='primary'
onClick={() => {
alert(props.value)
}}
>
MyButton 、{props.value}
</el-button>
)
}
const MySingle = defineComponent({
props: {
value: {
type: Object,
default: () => {}
}
},
render() {
return <div>{this.value}</div>
}
})
const MyList = defineComponent({
props: {
list: {
type: Array,
default: () => []
}
},
render() {
return (
<div>
{this.list.map((item, index) => {
return <div key={index}>{item.value}</div>
})}
</div>
)
}
})
const list = [
{
name: 1,
value: <MyButton value={1}></MyButton>
},
{
name: 2,
value: (
<el-button
value={2}
onClick={() => {
alert('第二个按钮')
}}
>
123123
</el-button>
)
},
{
name: 2,
value: (
<div>
{
<el-card type='card'>
{{
header: () => {
return <div>header</div>
},
default: () => {
return (
<el-input type='textarea' value={2}>
123123
</el-input>
)
}
}}
</el-card>
}
</div>
)
}
]
</script>
<style scoped></style>
总结封装组件RenderNode.js
function isVNode(node) {
return (
node !== null &&
typeof node === 'object' &&
Object.prototype.hasOwnProperty.call(node, 'tag') &&
Object.prototype.hasOwnProperty.call(node, 'componentOptions')
)
}
const RenderNode = {
props: {
value: {
type: Object,
default: () => null
}
},
render(h) {
if (isVNode(this.value)) {
return this.value
}
return <div class="render-node-value">{this.value || ''}</div>
}
}
export default RenderNode
调用示例
<template>
<div>
<div v-for="item in list">
<div>{{ item.label }}</div>
<div>
<renderNode :value="item.value" />
</div>
</div>
</div>
</template>
<script>
import RenderNode from './renderNode'
export default {
name: "TestRenderNode",
props: {},
components: {
RenderNode,
}
data() {
const name = "张三";
const mobile = "12345678901";
return {
list: [
{
label: "测试:",
value: "123123",
},
{
label: "用户姓名:",
value: <span>{name}</span>,
},
{
label: "手机号:",
value: <span>{mobile}</span>,
},
],
};
},
};
</script>
<style scoped></style>
参考资料:https://blog.csdn.net/qq_35459724/article/details/125235696