如何在Vue模板中使用VNode并解决循环结构转JSON报错问题
错误原因
- 你在
qf-dialog的created钩子中执行了JSON.parse(JSON.stringify(this.content)),传入的content是Vue组件对象,本身包含_renderProxy这类存在循环引用的内部属性,JSON序列化时会直接抛出你遇到的循环结构转换错误,这行代码完全是多余操作。 - 模板的双大括号
{{}}属于文本插值,只能输出文本内容,无法渲染VNode,就算你生成了合法的VNode,用双大括号也只会输出它的字符串形式,无法渲染成真实DOM。 - 你没有在
qf-dialog的props中声明content属性,传入的content会被归到$attrs中,无法通过this.content稳定获取。
修复方案
方案1:使用动态组件(最简实现)
直接用Vue内置的<component>动态组件渲染传入的组件,不需要手动创建VNode,修改qf-dialog代码如下:
<template> <el-dialog :visible.sync="visible" v-bind="$attrs" v-on="$listeners"> <!-- 动态渲染传入的组件,同时支持绑定自定义props --> <component :is="content" v-bind="contentProps" v-on="contentListeners" /> </el-dialog> </template> <script> export default { name: 'qf-dialog', props: { visible: { type: Boolean, default: false }, // 声明接收传入的组件 content: { type: [Object, Function], required: true }, // 可选:给传入的组件传递props contentProps: { type: Object, default: () => ({}) }, // 可选:监听传入组件的自定义事件 contentListeners: { type: Object, default: () => ({}) } } }; </script>
调用时如果需要给demo组件传参,可以按如下方式写:
import demo from './demo.vue'; methods: { open() { const dialog = this.$dialog({ content: demo, contentProps: { id: 1, title: '测试内容' }, contentListeners: { 'submit': (val) => { console.log('接收demo组件抛出的submit事件', val) } } }); } }
方案2:手动创建VNode(自定义渲染逻辑时使用)
如果你需要自己控制VNode的生成逻辑,可以用render函数替代模板实现,修改qf-dialog代码如下:
<script> export default { name: 'qf-dialog', props: { visible: { type: Boolean, default: false }, content: { type: [Object, Function], required: true }, contentProps: { type: Object, default: () => ({}) }, contentListeners: { type: Object, default: () => ({}) } }, render(h) { return h('el-dialog', { props: { visible: this.visible }, attrs: this.$attrs, on: { ...this.$listeners, 'update:visible': (val) => this.$emit('update:visible', val) } }, [ // 手动创建传入组件的VNode h(this.content, { props: this.contentProps, on: this.contentListeners }) ]) } }; </script>
两种方案都可以实现你的需求,优先推荐方案1,写法更简洁易维护。
内容的提问来源于stack exchange,提问作者CQ P
相关产品推荐
相关产品推荐

