我有一个小型的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之前创建组件。
现在被卡住了
我的消息来源可能不好,但请专注于问题,我需要了解发生了什么并解决该问题。
非常感谢您的帮助!