以编程方式创建Vue.js组件实例

发布于3/9/2020 来自:「前端知否」微信公众号

最近参与了一个Vue.js项目,项目中需要能够以编程方式创建组件。通过编程,意思是使用JavaScript创建和插入组件,而无需在模板中编写任何内容。本文接下来将介绍在模板中使用组件的各个方面,例如实例化,传递Props,插槽,挂载,转换为JavaScript代码。

通常,会推荐使用"单个文件组件"。比如一个Button组件,如下所示:

<template>
<button :class="type"><slot /></button>
</template>

<script>
export default {
name: 'Button',
props: [ 'type' ],
}
</script>

要在另一个组件中使用它,您要做的就是导入Button组件并将其标签插入模板中:

<template>
<Button type="primary">Click me!</Button>
</template>

<script>
import Button from 'Button.vue'

export default {
name: 'App',
components: { Button }
}
</script>

就项目需求而言,我不知道在模板中插入哪个组件以及在哪里插入。因此,我需要一种能在运行时为任何组件动态创建组件实例并将其插入DOM的方法。

创建实例

最初想法是使用new。但是,它将导出一个简单的对象,而不是类(构造函数)。如果我这样做:

import Button from 'Button.vue'

const instance = new Button()

上面的代码将失败。Button是一个对象,不是构造函数,不能new。我们需要的是一个Class,构造函数。我将组件对象传递给Vue.extend以创建Vue构造函数的子类。现在,我们可以使用new关键字从中创建一个实例:

import Button from 'Button.vue'
import Vue from 'vue'

const ComponentClass = Vue.extend(Button)
const instance = new ComponentClass()

完美!现在我们需要将其插入DOM中。

插入DOM

每个Vue实例都有一个名为$mount的方法,该方法将组件实例安装到传递给它的元素上(即,它将传递的元素替换为组件实例)。这不是我想要的效果。我想将组件实例插入某些DOM元素中。有一种方法可以做到这一点。从官方文档上看到:

如果未提供elementOrSelector参数,则该模板将呈现为文档外元素,并且必须使用浏览器DOM API自己将其插入文档中。

代码如下:

import Button from 'Button.vue'
import Vue from 'vue'

const ComponentClass = Vue.extend(Button)
const instance = new ComponentClass()

instance.$mount()
this.$refs.container.appendChild(instance.$el)

上面的代码中有两点需要注意。

首先,推荐使用$refs来引用Vue.js中的DOM元素。在要引用的DOM元素上指定一个属性(在本例中为<div ref =“ container”> </ div>),然后该元素在组件的$refs属性上就可以访问到。

其次,要从Vue组件实例获取文档上DOM元素引用,可以使用$el属性。

将Props传递给实例

接下来,我可以将一些Props传递给Button实例。比如,type属性。 Vue构造函数接受一个options对象,我们可以使用该对象来传递和初始化相关内容。对于传递Props,有一个名为propsData的键,我们可以使用它,如下所示:

import Button from 'Button.vue'
import Vue from 'vue'

const ComponentClass = Vue.extend(Button)
const instance = new ComponentClass({
propsData: { type: 'primary' }
})

instance.$mount()
this.$refs.container.appendChild(instance.$el)

我们几乎完成了,剩下最后一点。通过普通的模板方法,我们使用了如下按钮:<Button> Click me!</ Button>。标签之间的文本需要能够自定义,我们可以使用slot插槽来灵活设置将其渲染在最终按钮标签中。

设置插槽

如果您在Vue.js中使用了插槽,则可能知道在任何实例上都可以通过$slots属性访问这些插槽。而且,如果未使用命名插槽,则$slots.default中的插槽可以作为数组使用。这就是我们将在实例上修改的确切键,以设置按钮的内部文本。请记住,这需要在安装实例之前完成。

另外,在我们的例子中,我们只是在插槽中放入了一个简单的字符串。但是您还可以使用createElement函数以虚拟节点或VNode的形式将更复杂的DOM传递给它。您可以在Vue.js文档中阅读有关创建虚拟节点的信息。

import Button from 'Button.vue'
import Vue from 'vue'

const ComponentClass = Vue.extend(Button)
const instance = new ComponentClass({
propsData: { type: 'primary' }
})

instance.$slots.default = [ 'Click me!' ]

instance.$mount()
this.$refs.container.appendChild(instance.$el)