前言
最近遇到的一个需求,就是html页面需要组件化开发,然后找了一圈,发现很多组件化开发不好用
一般就是几种,比较用的多的一个是iframe嵌套页面,一个就是通过js生成页面写组件。感觉都挺麻烦的。就想着有没有能像vue哪样简单的组件引入方式。而且iframe加载也慢。没有组件快。
后来发现了这个httpVueLoader.js非常好用。
只需要引入这个文件,然后其他使用方法和vue差不多了。稍微有点变动可能就是传值方面。
传值下面也给出了方案。集中传值的方法应该大体是够用了的。
httpVueLoader.js代码
创建一个httpVueLoader.js文件,然后把这段代码复制进去。
(function umd(root,factory){if(typeof module==='object' && typeof exports === 'object' )module.exports=factory()else if(typeof define==='function' && define.amd)define([],factory)elseroot.httpVueLoader=factory()})(this,function factory() {'use strict';var scopeIndex = 0;StyleContext.prototype = {withBase: function(callback) {var tmpBaseElt;if ( ponent.baseURI ) {// firefox and chrome need the <base> to be set while inserting or modifying <style> in a document.tmpBaseElt = document.createElement('base');tmpBaseElt.href = ponent.baseURI;var headElt = ponent.getHead();headElt.insertBefore(tmpBaseElt, headElt.firstChild);}callback.call(this);if ( tmpBaseElt )ponent.getHead().removeChild(tmpBaseElt);},scopeStyles: function(styleElt, scopeName) {function process() {var sheet = styleElt.sheet;var rules = sheet.cssRules;for ( var i = 0; i < rules.length; ++i ) {var rule = rules[i];if ( rule.type !== 1 )continue;var scopedSelectors = [];rule.selectorText.split(/\s*,\s*/).forEach(function(sel) {scopedSelectors.push(scopeName+' '+sel);var segments = sel.match(/([^ :]+)(.+)?/);scopedSelectors.push(segments[1] + scopeName + (segments[2]||''));});var scopedRule = scopedSelectors.join(',') + rule.cssText.substr(rule.selectorText.length);sheet.deleteRule(i);sheet.insertRule(scopedRule, i);}}try {// firefox may fail sheet.cssRules with InvalidAccessErrorprocess();} catch (ex) {if ( ex instanceof DOMException && ex.code === DOMException.INVALID_ACCESS_ERR ) {styleElt.sheet.disabled = true;styleElt.addEventListener('load', function onStyleLoaded() {styleElt.removeEventListener('load', onStyleLoaded);// firefox need this timeout otherwise we have to use document.importNode(style, true)setTimeout(function() {process();styleElt.sheet.disabled = false;});});return;}throw ex;}},compile: function() {var hasTemplate = this.template !== null;var scoped = this.elt.hasAttribute('scoped');if ( scoped ) {// no template, no scopable style neededif ( !hasTemplate )return;// firefox does not tolerate this attributethis.elt.removeAttribute('scoped');}this.withBase(function() {ponent.getHead().appendChild(this.elt);});if ( scoped )this.scopeStyles(this.elt, '['+ponent.getScopeId()+']');return Promise.resolve();},getContent: function() {return this.elt.textContent;},setContent: function(content) {this.withBase(function() {this.elt.textContent = content;});}};function StyleContext(component, elt) {ponent = component;this.elt = elt;}ScriptContext.prototype = {getContent: function() {return this.elt.textContent;},setContent: function(content) {this.elt.textContent = content;},compile: function(module) {var childModuleRequire = function(childURL) {return httpVueLoader.require(resolveURL(ponent.baseURI, childURL));}.bind(this);var childLoader = function(childURL, childName) {return httpVueLoader(resolveURL(ponent.baseURI, childURL), childName);}.bind(this);try {Function('exports', 'require', 'httpVueLoader', 'module', this.getContent()).call(this.module.exports, this.module.exports, childModuleRequire, childLoader, this.module);} catch(ex) {if ( !('lineNumber' in ex) ) {return Promise.reject(ex);}var vueFileData = responseText.replace(/\r?\n/g, '\n');var lineNumber = vueFileData.substr(0, vueFileData.indexOf(script)).split('\n').length + ex.lineNumber - 1;throw new (ex.constructor)(ex.message, url, lineNumber);}return Promise.resolve(this.module.exports).then(httpVueLoader.scriptExportsHandler.bind(this)).then(function(exports) {this.module.exports = exports;}.bind(this));}};function ScriptContext(component, elt) {ponent = component;this.elt = elt;this.module = {exports:{} };}TemplateContext.prototype = {getContent: function() {return this.elt.innerHTML;},setContent: function(content) {this.elt.innerHTML = content;},getRootElt: function() {var tplElt = this.elt.content || this.elt;if ( 'firstElementChild' in tplElt )return tplElt.firstElementChild;for ( tplElt = tplElt.firstChild; tplElt !== null; tplElt = tplElt.nextSibling )if ( tplElt.nodeType === Node.ELEMENT_NODE )return tplElt;return null;},compile: function() {return Promise.resolve();}};function TemplateContext(component, elt) {ponent = component;this.elt = elt;}Component.prototype = {getHead: function() {return document.head || document.getElementsByTagName('head')[0];},getScopeId: function() {if ( this._scopeId === '' ) {this._scopeId = 'data-s-' + (scopeIndex++).toString(36);this.template.getRootElt().setAttribute(this._scopeId, '');}return this._scopeId;},load: function(componentURL) {return httpVueLoader.httpRequest(componentURL).then(function(responseText) {this.baseURI = componentURL.substr(0, componentURL.lastIndexOf('/')+1);var doc = document.implementation.createHTMLDocument('');// IE requires the <base> to come with <style>doc.body.innerHTML = (this.baseURI ? '<base href="'+this.baseURI+'">' : '') + responseText;for ( var it = doc.body.firstChild; it; it = it.nextSibling ) {switch ( it.nodeName ) {case 'TEMPLATE':this.template = new TemplateContext(this, it);break;case 'SCRIPT':this.script = new ScriptContext(this, it);break;case 'STYLE':this.styles.push(new StyleContext(this, it));break;}}return this;}.bind(this));},_normalizeSection: function(eltCx) {var p;if ( eltCx === null || !eltCx.elt.hasAttribute('src') ) {p = Promise.resolve(null);} else {p = httpVueLoader.httpRequest(eltCx.elt.getAttribute('src')).then(function(content) {eltCx.elt.removeAttribute('src');return content;});}return p.then(function(content) {if ( eltCx !== null && eltCx.elt.hasAttribute('lang') ) {var lang = eltCx.elt.getAttribute('lang');eltCx.elt.removeAttribute('lang');return httpVueLoader.langProcessor[lang.toLowerCase()].call(this, content === null ? eltCx.getContent() : content);}return content;}.bind(this)).then(function(content) {if ( content !== null )eltCx.setContent(content);});},normalize: function() {return Promise.all(Array.prototype.concat(this._normalizeSection(this.template),this._normalizeSection(this.script),this.styles.map(this._normalizeSection))).then(function() {return this;}.bind(this));},compile: function() {return Promise.all(Array.prototype.concat(this.template && pile(),this.script && pile(),this.styles.map(function(style) {return pile(); }))).then(function() {return this;}.bind(this));}};function Component(name) {this.name = name;this.template = null;this.script = null;this.styles = [];this._scopeId = '';}function identity(value) {return value;}function parseComponentURL(url) {var comp = url.match(/(.*?)([^/]+?)\/?(\.vue)?(\?.*|#.*|$)/);return {name: comp[2],url: comp[1] + comp[2] + (comp[3] === undefined ? '/index.vue' : comp[3]) + comp[4]};}function resolveURL(baseURL, url) {if (url.substr(0, 2) === './' || url.substr(0, 3) === '../') {return baseURL + url;}return url;}httpVueLoader.load = function(url, name) {return function() {return new Component(name).load(url).then(function(component) {return component.normalize();}).then(function(component) {return pile();}).then(function(component) {var exports = component.script !== null ? component.script.module.exports : {};if ( component.template !== null )exports.template = component.template.getContent();if ( exports.name === undefined )if ( component.name !== undefined )exports.name = component.name;exports._baseURI = component.baseURI;return exports;});};};httpVueLoader.register = function(Vue, url) {var comp = parseComponentURL(url);ponent(comp.name, httpVueLoader.load(comp.url));};httpVueLoader.install = function(Vue) {Vue.mixin({beforeCreate: function () {var components = this.$ponents;for ( var componentName in components ) {if ( typeof(components[componentName]) === 'string' && components[componentName].substr(0, 4) === 'url:' ) {var comp = parseComponentURL(components[componentName].substr(4));var componentURL = ('_baseURI' in this.$options) ? resolveURL(this.$options._baseURI, comp.url) : comp.url;if ( isNaN(componentName) )components[componentName] = httpVueLoader.load(componentURL, componentName);elsecomponents[componentName] = ponent(comp.name, httpVueLoader.load(componentURL, comp.name));}}}});};httpVueLoader.require = function(moduleName) {return window[moduleName];};httpVueLoader.httpRequest = function(url) {return new Promise(function(resolve, reject) {var xhr = new XMLHttpRequest();xhr.responseType = 'text';xhr.open('GET', url);xhr.onreadystatechange = function() {if ( xhr.readyState === 4 ) {if ( xhr.status >= 200 && xhr.status < 300 )resolve(xhr.responseText);elsereject(xhr.status);}};xhr.send(null);});};httpVueLoader.langProcessor = {html: identity,js: identity,css: identity};httpVueLoader.scriptExportsHandler = identity;function httpVueLoader(url, name) {var comp = parseComponentURL(url);return httpVueLoader.load(comp.url, name);}return httpVueLoader;});
使用方法(前提:html是引入vue框架的)
1,引入httpVueLoader.js文件。
2,components注册组件,前面的dr就是组件的名字,随便你改。后面的地址需要用…/这种形式的。
3,在html中把组件放上去,其实和vue的写法基本一样。上面的@closedialog就是自定义方法,你子组件可以通过$emit调用父组件的这个方法。
<!DOCTYPE html><html><head><meta charset='UTF-8'><!-- vue部分依赖 --><link rel="stylesheet" href="/statics/vue_element/element.css"><script src="/statics/vue_element/vue.js"></script><script src="/statics/vue_element/element.js"></script><script src="/statics/vue_element/axios.js"></script><!-- 引入vue类型组件 --><script src="/statics/vue_element/httpVueLoader.js"></script><title>导入导出功能模板</title></head><body><div id="app"><div><dr id="dr" ref="dr" @closedialog="closedl"></dr></div></div></body><script>new Vue({el: '#app',components: {'dr': httpVueLoader('../../../../components/mb/dr.vue')},}</script></html>
父子组件通信方式
由于是html的页面,虽然是引入了vue的框架语法,但是并不是真的变成了vue。
有些东西还是用起来不行的,我试过了不行。
所以这里我使用的方法是这样的。
把原生的一些组件通信方法结合vue的一些使用。
父级拿子级的变量
子级需要定义全局变量,也就是写在vue外面
document.getElementById('iframeId').contentWindow.变量名
父级调用子级的方法
子级需要定义全局方法,也就是写在vue外面
document.getElementById('iframeId').contentWindow.iframeMethod(param);
子级拿父级变量
这种父级需要定义全局变量,写在vue外面
window.parent.父级变量名;
子级调用父级的方法并传参
这种的父级方法要写在vue外面,function那种原始写法才能调用到
但是好的一点是这种传值方法可以拿到返回值。也就是说你方法执行完了可以return出一个结果,这里调用是可以接受到这个结果的
window.parent.父级方法名(item, index);
这种的可以直接调用到父级methods内的方法,是通过组件自定义事件触发的。
this.$emit("closedialog", "关闭弹框");