Vue 动态生成组件

目的:为了可以在方法中无需引用组件即可在页面生成dom结构

新建一个base文件夹,放置

1.baseComponent.js
2.index.js

baseComponent.js

import Vue from 'vue';
// 定义base组件
function BaseComponent () {

}

/**
 * 生成一个Vue 实例
 * @param Component
 * @param properties
 * @param dom
 * @param args
 * @returns {{[p: number]: *, some(callbackfn: (value: *, index: number, array: *[]) => unknown, thisArg?: any): boolean, keys(): IterableIterator, values(): IterableIterator<*>, shift(): (* | undefined), pop(): (* | undefined), slice(start?: number, end?: number): *[], find: {(predicate: (this:void, value: *, index: number, obj: *[]) => value is S, thisArg?: any): (S | undefined), (predicate: (value: *, index: number, obj: *[]) => unknown, thisArg?: any): (* | undefined)}, flat: {(this:U[][][][][][][][], depth: 7): U[], (this:U[][][][][][][], depth: 6): U[], (this:U[][][][][][], depth: 5): U[], (this:U[][][][][], depth: 4): U[], (this:U[][][][], depth: 3): U[], (this:U[][][], depth: 2): U[], (this:U[][], depth?: 1): U[], (this:U[], depth: 0): U[], (depth?: number): any[], (this:A, depth?: D): FlatArray[]}, join(separator?: string): string, reduceRight: {(callbackfn: (previousValue: *, currentValue: *, currentIndex: number, array: *[]) => *): *, (callbackfn: (previousValue: *, currentValue: *, currentIndex: number, array: *[]) => *, initialValue: *): *, (callbackfn: (previousValue: U, currentValue: *, currentIndex: number, array: *[]) => U, initialValue: U): U}, copyWithin(target: number, start: number, end?: number): this, indexOf(searchElement: *, fromIndex?: number): number, every(callbackfn: (value: *, index: number, array: *[]) => unknown, thisArg?: any): boolean, map(callbackfn: (value: *, index: number, array: *[]) => U, thisArg?: any): U[], reduce: {(callbackfn: (previousValue: *, currentValue: *, currentIndex: number, array: *[]) => *): *, (callbackfn: (previousValue: *, currentValue: *, currentIndex: number, array: *[]) => *, initialValue: *): *, (callbackfn: (previousValue: U, currentValue: *, currentIndex: number, array: *[]) => U, initialValue: U): U}, [Symbol.iterator](): IterableIterator<*>, splice: {(start: number, deleteCount?: number): *[], (start: number, deleteCount: number, ...items: *[]): *[]}, forEach(callbackfn: (value: *, index: number, array: *[]) => void, thisArg?: any): void, length: number, destroy(*=): void, includes(searchElement: *, fromIndex?: number): boolean, concat: {(...items: ConcatArray<*>): *[], (...items: ConcatArray<*> | *[]): *[]}, sort(compareFn?: (a: *, b: *) => number): this, fill(value: *, start?: number, end?: number): this, reverse(): *[], push(...items: *[]): number, [Symbol.unscopables](): {copyWithin: boolean, entries: boolean, fill: boolean, find: boolean, findIndex: boolean, keys: boolean, values: boolean}, props: ({option}|*), findIndex(predicate: (value: *, index: number, obj: *[]) => unknown, thisArg?: any): number, flatMap: {(callback: (this:This, value: *, index: number, array: *[]) => (ReadonlyArray | U), thisArg?: This): U[], (callback: (this:This, value: *, index: number, array: *[]) => (ReadonlyArray | U), thisArg?: This): U[]}, filter: {(callbackfn: (value: *, index: number, array: *[]) => value is S, thisArg?: any): S[], (callbackfn: (value: *, index: number, array: *[]) => unknown, thisArg?: any): *[]}, lastIndexOf(searchElement: *, fromIndex?: number): number, entries(): IterableIterator<[number, *]>, component, name: string, toString(): string, unshift(...items: *[]): number, toLocaleString(): string}|VNode}
 */
BaseComponent.prototype.newInstance = (Component, properties, dom, ...args) => {
    const props = properties || {};
    delete props.option.template;
    const Instance = new Vue({
        render (h) {
            return h(Component, {
                props: props
            });
        }
    });
    const component = Instance.$mount();
    if (dom) {
        dom.appendChild(component.$el);
    } else {
        document.body.appendChild(component.$el);
    }
    let name = null;
    if (properties.option) {
        name = properties.option.name || 'undefined';
    }
    // 动态生成的组件
    const notification = Instance.$children[0];
    return {
        ...args,
        name: name || 'undefined',
        component: notification,
        props: properties,
        destroy (callback) {
            setTimeout(() => {
                // 清除dom元素
                dom ? dom.removeChild(component.$el) : document.body.removeChild(component.$el);
                // 销毁实例
                notification.$destroy();
                if (callback && callback instanceof Function) {
                    callback(notification);
                }
            }, 300)
        }
    };
};

export default BaseComponent;

2.index.js

import BaseComponent from './baseComponent'
// 创建store来存储组件
let store = [];
let noticeInstance;
let createInstance = function (option) {
    // 创建实例对象
    let instance = new BaseComponent().newInstance(option.template, {
        option: option,
        destroy: function () {
            if (noticeInstance !== null) {
                // 数组中删除component
                store = store.filter(function (item) {
                    return item.name !== noticeInstance.name;
                })
                // 删除实例对象
                noticeInstance.destroy(() => {
                    noticeInstance = null;
                });
            }
        }
    }, option.dom);
    if (option.name) {
        let flag = true;
        for (let i = 0; i < store.length; i++) {
            if (store[i].name === option.name) {
                flag = false;
                break;
            }
        }
        if (flag) {
            store.push({
                name: option.name,
                component: instance
            });
        }
    }
    return instance;
};

/**
 * 动态新建一个组件
 * @param option
 * @returns {*}
 */
function loadComponentInstance (option) {
    // 组件存在
    if (noticeInstance) {
        if (noticeInstance.name !== 'undefined') {
            if (option.name !== noticeInstance.name) {
                let flag = false;
                // 在数组中寻找组件是否存在
                store.forEach((item) => {
                    if (item.name === option.name) {
                        flag = true;
                        noticeInstance = item.component;
                        return noticeInstance;
                    }
                })
                // 不存在
                if (!flag) {
                    noticeInstance = createInstance(option);
                }
            }
        } else {
            noticeInstance.component.destroy();
            noticeInstance = createInstance(option);
        }
    } else {
        // 组件不存在
        noticeInstance = createInstance(option);
    }
    return noticeInstance;
}
/**
 * 销毁组件
 * @param option
 * @returns {*}
 */
function destroyInstance (name) {
    if (name === null) {
        console.error('destroyInstance function needs name...');
    }
    store.forEach((item) => {
        if (item.name === name) {
            let instance = item.component;
            instance.destroy();
        }
    })
}
export default {
    loadComponentInstance,
    destroyInstance
};

使用

在main.js 文件引入组件

import loadView from '_c/base/index'
Vue.prototype.$loadView = loadView;

在***.vue文件中使用

 switchDomain () {
                const obj = this.$loadView.loadComponentInstance({
                    name: 'switch-domain-modal',
                    template: switchDomainModal
                })
                //拿到组件实例
                let modal = obj.component;
                //组件方法 我这是modal框 执行打开
                modal.open();
            }

你可能感兴趣的:(Vue 动态生成组件)