如何以编程方式添加 Vue 3 组件?

Posted

技术标签:

【中文标题】如何以编程方式添加 Vue 3 组件?【英文标题】:How to add Vue 3 components programmatically? 【发布时间】:2021-05-16 02:36:31 【问题描述】:

Vue 3 没有 Vue.extend() 方法,所以这里的例子不起作用: https://css-tricks.com/creating-vue-js-component-instances-programmatically/

我已经尝试过这个解决方案: https://jsfiddle.net/jamesbrndwgn/fsoe7cuy/

但它会在控制台中引起警告:

Vue 收到了一个组件,该组件被制成反应性对象。这可能会导致不必要的性能开销,应通过使用markRaw 标记组件或使用shallowRef 而不是ref 来避免。

enter image description here

那么,在 Vue 3 中动态(以编程方式)添加组件的正确方法是什么?

上传任务标签管理器.vue

<template>
    <button @click="addTag()" type="button">Add new tag</button>
    <br>
    <div>
        <div v-for="child in children" :key="child.name">
            <component :is="child"></component>
        </div>
    </div>
</template>

<script>
    import UploadTaskTag from "./UploadTaskTag";

    export default 
        name: "UploadTaskTagManager",
        components: UploadTaskTag,

        data() 
            return 
                children: []
            
        ,

        methods: 
            addTag() 
                this.children.push(UploadTaskTag);
            ,
        
    
</script>

UploadTaskTag.vue

<template>
    <div>
        <select @change="onChangeTag($event)" v-model="currentTag">
            <option v-for="tag in getAllTags()" :key="tag.tag_name" :value="tag">tag.tag_name</option>
        </select>
        <input maxlength="16" class="tag-input" ref="inputField"/>
        <button @click="removeTag($event)" type="button">-</button>
        <br>
    </div>
</template>

<script>
    export default 
        name: "UploadTaskTag",

        data() 
            return 
                tags: [],
                currentTag: 
            
        ,

        methods: 
            onChangeTag(event) 
                this.$refs.inputField.value = this.currentTag.tag_name;
            ,

            getAllTags() 
                return this.$store.state.allTags;
            ,

            removeTag() 
                this.$el.parentElement.removeChild(this.$el)
            ,

            getValue(fieldIndex) 
                let tag = this.tags[fieldIndex];
                return tag ? tag.tag_name : "";
            ,
        
    
</script>

【问题讨论】:

【参考方案1】:

只需使用createApp 而不是Vue.extend 这是一个以编程方式的简单 vue3 组件示例

import Button from 'Button.vue'
import  createApp  from "vue"
// Creating the Instance
// use createApp https://v3.vuejs.org/api/global-api.html#createapp
var ComponentApp = createApp(Button)
import Button from "./Button.vue"

// inserting to dom
const wrapper = document.createElement("div")
ComponentApp.mount(wrapper)
document.body.appendChild(wrapper)

使用h,https://v3.vuejs.org/api/global-api.html#h,设置道具或插槽略有不同

import Button from 'Button.vue'
import  createApp, h  from "vue"

var ComponentApp = createApp( 
  setup () 
    return () => h(Button, type: "primary", "default slot as children")
  
)

【讨论】:

不知何故,使用 createApp() 以编程方式添加一个小的 Vue 组件感觉不对。它让我想起了臃肿和头顶的景象。不过我必须把它交给你,你的回答就可以了。 我面临着类似的问题。我只想动态创建一个组件并将其附加到 Vue 3 中的 DOM 中,这似乎不必要地困难。为什么要创建一个新的 Vue 实例?我不明白。没有更简单的方法吗?【参考方案2】:

如果我理解正确,那么解决方案将是这样的

您可以在官网https://v3.vuejs.org/api/global-api.html#defineasynccomponent了解更多关于'defineAsyncComponent'的信息

<template>
    <button @click="addTag()" type="button">Add new tag</button>
    <br>
    <div>
        <div v-for="child in children" :key="child.name">
            <component :is="child"></component>
        </div>
    </div>
</template>

<script>

import  defineAsyncComponent, defineComponent, ref  from "vue"

export default defineComponent(
components:      
      UploadTaskTag: defineAsyncComponent(() => import("./UploadTaskTag"))
    ,
 setup() 
      const children = ref([])

      const addTag = () => 
        children.value.push('UploadTaskTag')
        console.log(children.value)
      
return 
        addTag,
        children        
      
    ,
  )
</script>

【讨论】:

以上是关于如何以编程方式添加 Vue 3 组件?的主要内容,如果未能解决你的问题,请参考以下文章

Vue.js:以编程方式实例化功能组件

如何以编程方式创建 Vue.js 插槽?

如何以编程方式更改 Vuetify 选项卡和 vue-router

如何以编程/动态方式将组件添加到 p:dataTable facet

Vue3在按钮单击时以编程方式创建组件实例

如何以编程方式绑定 Nuxt/Vue 中的视频播放器 URL?