渲染时,自定义标签
my-component
就会被替换为组件的内容,在哪写的组件标签,就会在哪里被替换。
换句话说,常规的组件使用方式,只能在规定的地方渲染组件
。但是,在下面的两种场景中就比较局限了:
场景一
:实现类似原生
window.alert()
的提示框组件,它的位置是在
<body>
下,而非
<div id="app">
,并且不会通过常规的组件自定义标签的形式使用,而是像 JS 调用函数一样使用;
场景二
:组件的模板是通过调用接口从服务端获取的,需要动态渲染组件;
一般来说,在我们访问页面时,组件就已经渲染就位了,对于场景一,其实并不陌生,在
jQuery
时代,通过操作 DOM,很容易就能实现,当然你也可以这么做,但是这不是
Vue
推荐的做法,既然使用
Vue
了,就应该用
Vue
的思路来解决问题。对于场景二,组件的渲染是异步的,预先不知道模板是什么。
因此,对于这两种场景,
Vue.extend
和
vm.$mount
语法就派上用场了。
Vue.extend
和
vm.$mount
的用法
Vue.extend
的作用,就是基于
Vue
构造器,创建一个
子类构造器
VueComponent
,我们看下它的源码:
function Vue() {
this._init(options)
Vue.extend = function (extendOptions) {
const Super = Vue
const Sub = function VueComponent (options) {
this._init(options)
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
return Sub
从源码中,可以非常看清楚子类的构造函数VueComponent
继承于父类Vue
。
在Vue
中,组件并不是Vue
实例,组件的构造是VueComponent
,两者并不相同,而是一个继承关系,切记切记。
我们首先看一下 Vue
实例的挂载过程:首先通过new Vue
实例化一个Vue
实例,实例上有个$mount
方法,调用$mount
方法,传入要挂载的dom节点,进行挂载。
var app = new Vue({
render: h => h(App)
}).$mount('#app')
同样地,组件的创建过程也是一样的。它的参数跟 new Vue
的基本一样,但 data
要跟组件一样,是个函数,再配合 $mount
,就可以让组件渲染,并且挂载到任意指定的节点上,比如 body
。
针对上面的场景,就可以这样写:
import Vue from 'vue'
const ConfirmComponent = Vue.extend({
template: '<div>{{ message }}</div>',
data () {
return {
message: 'Hello, vue'
这一步创建了一个构造器,这个过程也可以解决异步获取 template
模板的问题,下面要手动渲染组件,并把它挂载到 body
下:
const component = new ConfirmComponent().$mount();
这一步,我们调用了 $mount
方法对组件进行了手动渲染,但它仅仅是被渲染好了,也就是把组件内容转化为了真实的DOM
节点,但并没有挂载到节点上,也就显示不了组件。此时的 component
已经是一个标准的 Vue 组件实例,因此它的 $el
属性也可以被访问,也就是组件对应的真实DOM赋值给了$el
属性。
最后,通过appendChild
方法挂载到body
上。当然,除了 body
,你还可以挂载到其它节点上。
document.body.appendChild(component.$el);
$mount
也有一些快捷的挂载方式,以下两种都是可以的:
new ConfirmComponent().$mount('#app');
new ConfirmComponent({ el: '#app' });
实现同样的效果,除了用 Vue.extend
外,也可以直接创建 Vue
实例,并且用一个 Render
函数来渲染一个 .vue
文件:
import Vue from 'vue';
import Notification from './notification.vue';
const props = {};
const Instance = new Vue({
render (h) {
return h(Notification, {
props: props
const component = Instance.$mount();
document.body.appendChild(component.$el);
这样既可以使用 .vue
来写复杂的组件(毕竟在 template
里堆字符串很痛苦),还可以根据需要传入适当的 props
。
渲染后,如果想操作 Render
的 Notification
实例,也是很简单的,只需要找到Notification
实例即可。
因为我们是通过Vue
来实例化的,内部再通过VueComponent
来实例化了一个组件,两者就构成了父子关系,所以可以这么获取子组件的实例:
const notification = Instance.$children[0];
需要注意的是,我们是用 $mount
手动渲染的组件,如果要销毁,也要用 $destroy
来手动销毁实例,必要时,也可以用 removeChild
把节点从 DOM 中移除。
这两个 API
如果你不对 Vue
源码有一定的了解,你可能并不能真正意义上的弄懂上面的写法。所以,我个人还是非常适当的阅读下Vue
源码。
在多数情况下,我们只关注在业务层,并使用现成的组件库,所以使用这两个API
的情况较少。