Skip to content

动态Vue组件

动态Vue组件允许您通过字符串形式的Vue单文件组件(SFC)内容来动态创建和渲染Vue组件。该组件支持完整的Vue SFC语法,包括template、script和style部分,并提供了样式隔离和错误处理机制。

基础使用

基本渲染

通过传入Vue单文件组件字符串来动态渲染组件:

js
const vueContent = `
<template>
  <div class="dynamic-component">
    <h3>{{ title }}</h3>
    <p>{{ message }}</p>
    <button @click="handleClick">点击我</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      title: '动态组件',
      message: '这是一个动态渲染的Vue组件'
    }
  },
  methods: {
    handleClick() {
      this.message = '按钮被点击了!'
    }
  }
}
</script>

<style scoped>
.dynamic-component {
  padding: 20px;
  border: 1px solid #ddd;
  border-radius: 8px;
}
</style>
`

在FormCreate中使用

js
const rule = [
  {
    type: 'dynamic-render',
    field: 'dynamicComponent',
    props: {
      vueContent: vueContent
    }
  }
]

配置项

动态Vue组件提供了以下配置选项:

属性名类型默认值必需说明
vueContentString-Vue单文件组件字符串内容

事件

动态Vue组件提供了丰富的事件,方便您监听组件状态变化:

事件名参数说明
mountedelement组件挂载完成事件,当动态组件渲染完成时触发
errorerror组件解析错误事件,当Vue内容解析失败时触发
update:modelValuevalue双向绑定值更新事件,当组件值变化时触发

事件使用示例

监听组件挂载

js
function handleMounted(element) {
    console.log('动态组件已挂载:', element);
    // 可以在这里进行DOM操作或初始化
}

监听解析错误

js
function handleError(error) {
    console.error('组件解析失败:', error);
    // 显示错误提示给用户
    showErrorMessage('组件内容解析失败,请检查语法');
}

监听值变化

js
function handleValueChange(value) {
    console.log('组件值变化:', value);
    // 可以实时保存数据或触发其他操作
}

方法

动态Vue组件提供了多种方法,方便您进行程序化控制:

方法名参数说明返回值
reRender-重新渲染组件-
removeStyleid移除指定样式Boolean
clearAllStyles-清除所有动态添加的样式Number

方法使用示例

重新渲染组件

js
function refreshComponent($inject) {
    const dynamicRender = $inject.api.el('ref_dynamic_render');
    dynamicRender.reRender();
}

清除样式

js
function clearStyles($inject) {
    const dynamicRender = $inject.api.el('ref_dynamic_render');
    const removedCount = dynamicRender.clearAllStyles();
    console.log(`清除了 ${removedCount} 个样式`);
}

支持的Vue语法

Template语法

支持完整的Vue模板语法:

vue
<template>
  <!-- 条件渲染 -->
  <div v-if="showContent">内容显示</div>

  <!-- 列表渲染 -->
  <ul>
    <li v-for="item in list" :key="item.id">
      {{ item.name }}
    </li>
  </ul>

  <!-- 事件处理 -->
  <button @click="handleClick">点击</button>

  <!-- 属性绑定 -->
  <input v-model="inputValue" :placeholder="placeholder" />
</template>

Script语法

支持两种script语法:

Vue 2 Options API

vue
<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  computed: {
    doubleCount() {
      return this.count * 2
    }
  },
  methods: {
    increment() {
      this.count++
    }
  },
  watch: {
    count(newVal) {
      console.log('count changed:', newVal)
    }
  }
}
</script>

Vue 3 Composition API (setup)

vue
<script setup>
import { ref, computed, watch } from 'vue'

const count = ref(0)
const doubleCount = computed(() => count.value * 2)

const increment = () => {
  count.value++
}

watch(count, (newVal) => {
  console.log('count changed:', newVal)
})
</script>

Style语法

支持CSS样式:

vue
<style>
.component-wrapper {
  padding: 20px;
  background: #f5f5f5;
}

.component-wrapper h3 {
  color: #333;
  margin-bottom: 10px;
}
</style>

高级用法

与FormCreate集成

动态组件可以完美集成到FormCreate表单中:

js
const rule = [
  {
    type: 'dynamic-render',
    field: 'customComponent',
    props: {
      vueContent: `
        <template>
          <div>
            <el-input v-model="inputValue" placeholder="请输入内容" />
            <el-button @click="submit">提交</el-button>
          </div>
        </template>
        <script>
        export default {
          data() {
            return {
              inputValue: ''
            }
          },
          methods: {
            submit() {
              console.log('提交值:', this.inputValue)
            }
          }
        }
        </script>
      `
    },
    on: {
      mounted: (element) => {
        console.log('自定义组件已挂载')
      }
    }
  }
]

动态更新组件内容

js
function updateComponentContent($inject, newContent) {
    const dynamicRender = $inject.api.el('ref_dynamic_render');
    // 更新vueContent属性会触发重新渲染
    dynamicRender.vueContent = newContent;
}

错误处理

组件内置了完善的错误处理机制:

js
const rule = [
  {
    type: 'dynamic-render',
    field: 'errorHandling',
    props: {
      vueContent: invalidVueContent
    },
    on: {
      error: (error) => {
        console.error('组件解析错误:', error);
        // 可以显示友好的错误提示
        showErrorToast('组件内容有误,请检查语法');
      }
    }
  }
]

注意事项

注意

该组件需要导入完整的 Vue 版本才能正常工作,请确保在构建配置中正确设置 Vue 别名。

1. Vue 版本配置

动态组件需要完整的 Vue 版本支持,请在构建配置中添加以下别名设置:

Vue 3 配置

js
// vite.config.js
export default {
  resolve: {
    alias: {
      vue: 'vue/dist/vue.esm-bundler.js'
    }
  }
}
js
// webpack.config.js
module.exports = {
  resolve: {
    alias: {
      vue: 'vue/dist/vue.esm-bundler.js'
    }
  }
}

Vue 2 配置

js
// vite.config.js
export default {
  resolve: {
    alias: {
      vue: 'vue/dist/vue.esm.js'
    }
  }
}
js
// webpack.config.js
module.exports = {
  resolve: {
    alias: {
      vue: 'vue/dist/vue.esm.js'
    }
  }
}

2. 外部资源限制

限制

动态组件内部不能通过 importrequire 导入外部资源,包括:

  • 外部 JavaScript 模块
  • CSS 文件
  • 图片资源
  • 字体文件

不支持的用法

vue
<script>
// ❌ 不支持外部模块导入
import { someFunction } from './utils'
import axios from 'axios'

// ❌ 不支持外部样式导入
import './styles.css'

export default {
  data() {
    return {}
  }
}
</script>

<style>
/* ❌ 不支持外部资源引用 */
@import url('https://fonts.googleapis.com/css2?family=Roboto');
</style>

推荐的替代方案

vue
<script>
export default {
  data() {
    return {}
  },
  methods: {
    // ✅ 使用内联代码
    someFunction() {
      // 函数实现
    },

    // ✅ 使用全局变量
    getApi() {
      return window.axios || fetch
    }
  }
}
</script>

<style>
/* ✅ 使用内联样式 */
.component {
  font-family: 'Arial', sans-serif;
  color: #333;
}
</style>