已注册Vue组件,但仍会警告Vue未知的自定义元素

时间:2019-08-01 13:33:39

标签: vue.js vue-component single-page-application vue-dynamic-components


我有一个小型的Vue应用,可以动态加载组件
首先,我编写了一个称为SPA的功能,该功能负责创建主应用并处理加载组件

ref_no

然后,我编写一个将从服务器获取并按需运行的全局组件

    /*
     * param {object} params: {
     *     {CSS selector/HTMLElement} el: required, an existing DOM element to Vue mount on
     *     {CSS selector} componentEl: required, placeholder element in "el" for Vue component
     *     {CSS selector/HTML string} initComponentTemplate: optional, default Vue component template when initialize app
     *     {object} data: optional, data for Vue instance
     *     {object} methods: optional, methods for Vue instance
     * }
     * returns {SPA}
     * example create new SPA object
            window.exampleSPA = new SPA({
                el: "#app-view",
                componentEl: "#app-view-component"
            });
     */
    var SPA = function(params) {
        var app = this;
        /*
         * Construct params for Vue instance
         */
        var vmConstruct = {
            el: null,
            data: function() {
                var fullData = {
                    /*
                     * Current component
                     * currentView presents for current child component
                       In this, props technical used to pass data around components,
                       https://vuejs.org/v2/guide/components.html#Props
                       currentItem will contain data pass form parent to child component and vice-versa
                       Vue will send data in currentItem to child component whenever having a component rendered into this.
                       In components, we get data sent from parent via props "passingMessage".
                       Vue will also listen "pass" event to get data from child component then set to currentItem,
                       the "pass" event is emitted by child component aims to pass data back to parent.
                     */
                    currentView: "init",
                    // current data passed to component
                    currentItem: {}
                };
                // add properties from SPA initialization
                if (!params.hasOwnProperty("data")) {
                    return fullData;
                }
                for (var prop in params.data) {
                    if (fullData.hasOwnProperty(prop)) {
                        continue;
                    }
                    fullData[prop] = params.data[prop];
                }
                return fullData;
            },
            computed: {},
            created: function() {},
            mounted: function () {
                this.$nextTick(function () {
                  // Code that will run only after the entire view has been rendered
                });
            },
            methods: {
                /*
                 * Set data for currentItem,
                 * usually, data get from child component will be bind to currentItem
                 * param {object} params
                 */
                setCurrentItem: function(params) {
                    this.currentItem = params;
                }
            }
        };
        if (params.el === 'undefined') {
            throw new Error("Missing param el");
        }
        vmConstruct.el = params.el; //assume params.el is HTMLElement
        if (params.el.charAt(0) === "#") { //params.el is CSS selector
            vmConstruct.el = document.querySelector(params.el);
        }
        //check for existing of component element,
        //replace it with a fixed template
        if (params.componentEl === 'undefined') {
            throw new Error("Missing param componentEl");
        }
        var componentTemplate = '<section><div id="spa-template-container" style="display: none;"></div><article><div v-bind:is="currentView" v-bind:passing-message="currentItem" v-on:pass="setCurrentItem"></div></article></section>';
        if (params.componentEl.charAt(0) !== "#") {
            throw new Error("Param componentEl invalid, must be CSS selector");
        }
        var componentEl = vmConstruct.el.querySelector(params.componentEl);
        if (componentEl === null) {
            throw new Error('Placeholder element in "el" for Vue component not found');
        }
        componentEl.innerHTML = componentTemplate;
        //Create the 'default' component
        var initComponentTemplate = '<div></div>';
        if (typeof params.initComponentTemplate === "string") {
            initComponentTemplate = params.initComponentTemplate;
        }
        Vue.component("init", {
            template: initComponentTemplate
        });
        if (params.hasOwnProperty("methods")) {
            for (var prop in params.methods) {
                if (vmConstruct.hasOwnProperty(prop)) {
                    continue;
                }
                vmConstruct.methods[prop] = params.methods[prop];
            }
        }
        /*
         * Create view model
         */
        this.vm = new Vue(vmConstruct);
        //require lib public/libs/base/js/script.js
        this.assets = new Admin.assets();
    };
    /*
     * Change the page to target component
     * param {object} params: {
     *     {string} tagName
     * }
     */
    SPA.prototype.setCurrentView = function(params) {
        //And then change the page to that component
        this.vm.currentView = params.tagName;
    };
    /*
     * Dynamically components loading
     * param {object} params: {
     *     {anchor DOM object} anchor
     *     {string} action
     * }
     */
    SPA.prototype.getComponent = function(params) {
        var app = this;
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.onreadystatechange = function () {
            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                //Load new template and script in it
                var templateContainer = app.getTemplateContainer();
                templateContainer.innerHTML = xmlhttp.responseText;
                var responsePage = templateContainer.querySelector('title');
                if (responsePage) {
                    var redirectAnchor = document.createElement("a");
                    redirectAnchor.href = xmlhttp.responseURL;
                    window.location.href = redirectAnchor.origin + redirectAnchor.pathname;
                }
                var links = templateContainer.querySelectorAll('link');
                app.assets.load({links: links});
                var scripts = templateContainer.querySelectorAll('script');
                app.assets.load({scripts: scripts}, function(error, result) {
                    app.setCurrentView({tagName: params.action});
                });
            }
        };
        xmlhttp.open("get", params.anchor.href);
        xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xmlhttp.send();
    };
    /*
     * Set SPA component
     * We use DOMElement of HTML a tag as "params" object
     * The property "pathname" in attribute "href" will be used to parse component ID
     * Example pathname "/example/path" will be parsed into component ID "example-path",
     * so in Vue component setup, we need to create component with:
     * Vue.component("example-path", {...});
     * param {anchor DOM object} params: {
     *     ...
     *     {string} pathname: "/example/path"
     *     ...
     * }
     */
    SPA.prototype.setComponent = function(params) {
        var action = params.pathname.replace(/\//g, "-").substr(1);
        if (!(action in Vue.options.components)) {
            this.getComponent({anchor: params, action: action});
        } else {
            this.setCurrentView({tagName: action});
        }
    };
    /*
     * param {object} params
     */
    SPA.prototype.getTemplateContainer = function(params) {
        var app = this;
        var templateSelector = 'spa-template-container';
        var templateContainer = document.querySelector('#'+templateSelector);
        if (templateContainer === null) {
            templateContainer = document.createElement("div");
            templateContainer.setAttribute("id", templateSelector);
            templateContainer.style.display = "none";
            app.vm.$el.appendChild(templateContainer);
        }
        return templateContainer;
    };

我监听click事件以加载组件,每次需要加载组件时,我都会调用 spa.setComponent
如您在SPA func中看到的,我如何加载组件
我已经测试过加载脚本序列,但可以,可以在设置currentView之前创建组件。
现在被卡住了
我的消息来源可能不好,但请专注于问题,我需要了解发生了什么并解决该问题。
非常感谢您的帮助!

0 个答案:

没有答案