2


我有一个动态加载组件的小型 Vue 应用程序
首先我编写了一个名为 SPA 的函数,该函数负责创建主应用程序并处理加载组件

    /*
     * 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;
    };

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

    <div id="component-name">
        Content
    </div>
    <script>
    Vue.component("component-name", {
        template: "#component-name",
        .....
        .....
    });
    </script>

我监听点击事件来加载组件,每次我需要加载一个组件时,我调用spa.setComponent
正如你在 SPA func 中看到的我如何加载组件
我测试加载脚本序列但是没关系,组件在我设置 currentView 之前创建.
现在我被卡住
了我的来源可能不好,但请关注问题,我需要了解发生了什么并解决它。
非常感谢您的帮助!

4

0 回答 0