方案一:Vue.extend(Component) 获取组件构造函数
Vue.extend
是 Vue 全局 API,官方文档 对其描述如下:
使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。
从官方文档的描述中,我们可以知道,extend
创建的是 Vue 构造器,而不是我们平时常写的组件实例,所以不可以通过 new Vue({ components: testExtend })
来直接使用,需要通过 new Profile().$mount('#mount-point')
来挂载到指定的元素上。
为什么使用 extend
在 vue 项目中,我们有了初始化的根实例后,所有页面基本上都是通过 router 来管理,组件也是通过 import
来进行局部注册,所以组件的创建我们不需要去关注,相比 extend
要更省心一点点。但是这样做会有几个缺点:
- 组件模板都是事先定义好的,如果我要从接口动态渲染组件怎么办?
- 所有内容都是在
#app
下渲染,注册组件都是在当前位置渲染。如果我要实现一个类似于 window.alert()
提示组件要求像调用 JS 函数一样调用它,该怎么办?
这时候,Vue.extend + vm.$mount
组合就派上用场了。
接下来,让我们用 Vue.extend() 来实现一个方法,该方法用来动态创建一个组件实例。
步骤一:获取组件构造函数
1 2
| const ConponentConstructor = Vue.extend(Component);
|
步骤二:构建组件实例并挂载到 DOM 节点上
1 2 3 4 5 6 7 8 9
| const vm = new ConponentConstructor({ render(createElement) { return createElement(Component, { props }); } }); vm.$mount();
|
步骤三:把当前 Vue 实例关联的 DOM 元素挂载到 body 上
1 2 3 4 5 6 7 8 9 10
| document.body.appendChild(vm.$el);
const component = vm.$children[0]; component.remove = () => { document.body.removeChild(vm.$el); vm.$destroy(); };
|
以下是完整代码:
方案一的第一种写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import Vue from 'vue';
const create = (Component, props) => { const ComponentConstructor = Vue.extend(Component);
const vm = new ComponentConstructor({ render(createElement) { return createElement(Component, { props }); } });
vm.$mount();
document.body.appendChild(vm.$el);
const component = vm.$children[0]; component.remove = () => { document.body.removeChild(vm.$el); vm.$destroy(); };
return component; };
export default create;
|
方案一的第二种写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import Vue from 'vue';
const create = (Component, props) => { const Ctor = Vue.extend(Component); const comp = new Ctor({ propsData: props }); comp.$mount(); document.body.appendChild(comp.$el); comp.remove = () => { document.body.removeChild(comp.$el); comp.$destroy(); };
return comp; };
export default create;
|
方案二:通过 new Vue() 的方式实例化一个组件实例
new Vue()
new Vue() 用于创建一个 Vue 实例
步骤一:构建 Component 实例
1 2 3 4 5 6 7
| const vm = new Vue({ render(createElement) { return createElement(Component, { props }); } }).$mount();
|
步骤二:把 Vue 实例关联的 DOM 元素挂载到 body 上
1
| document.body.appendChild(vm.$el);
|
步骤三:获取组件实例并返回
1 2 3 4 5 6 7
| const component = vm.$children[0]; component.remove = () => { document.body.removeChild(vm.$el); vm.$destroy(); };
|
以下是完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import Vue from 'vue';
function create(Component, props) { const vm = new Vue({ render(h) { return h(Component, { props }); } }).$mount(); document.body.appendChild(vm.$el);
const comp = vm.$children[0]; comp.remove = () => { document.body.removeChild(vm.$el); vm.$destroy(); };
return comp; } export default create;
|
我们已经实现了用于创建组件实例的 create 方法,那么我们该怎么使用它呢?
方法调用
方式一:import 导入
在你需要调用该方法的文件中通过 import 的方式导入 create 方法,然后直接调用:
1 2 3 4
| create(Notice, { title: '老杨喊你来搬砖', message: isValid ? '请求登录。。。' : '校验失败了' }).show();
|
create 方法的第一个参数是一个已经定义好的组件,第二个参数是传递给传入 create 方法的组件的 props,props 中的属性必须是传入的组件中已经定义好的。
上面代码的含义是通过 create 方法创建 Notice 组件的实例,调用 Notice 组件中定义的 show() 方法显示出来。
方式二:定义成插件,在全局调用
1 2 3 4 5 6 7 8 9 10 11 12
| import Notice from '@/components/Notice.vue'; import Vue from 'vue'
export default { install(Vue) { Vue.prototype.$notice = function (options) { return create(Notice, options) } } }
|
定义成插件后,便可以在全局作用下调用 create 方法了:
1 2 3 4 5
| this.$notice({ title: '我是弹窗标题', message: '我是弹窗提示信息', duration: 1000 }).show();
|
附上 Notice 组件的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| <template> <div class="box" v-if="isShow"> <h3>{{ title }}</h3> <p class="box-content">{{ message }}</p> </div> </template>
<script> export default { props: { title: { type: String, default: '' }, message: { type: String, default: '' }, duration: { type: Number, default: 1000 } }, data() { return { isShow: false }; }, methods: { show() { this.isShow = true; setTimeout(this.hide, this.duration); }, hide() { this.isShow = false; this.remove(); } } }; </script>
<style> .box { position: fixed; width: 100%; top: 16px; left: 0; text-align: center; pointer-events: none; background-color: #fff; border: grey 3px solid; box-sizing: border-box; } .box-content { width: 200px; margin: 10px auto; font-size: 14px; padding: 8px 16px; background: #fff; border-radius: 3px; margin-bottom: 8px; } </style>
|
来源:https://www.yuque.com/moozi/umgbyh/mps35y