UNPKG

vue-scooter-template

Version:
533 lines (492 loc) 18.6 kB
var VueScooter = (function () { 'use strict'; var c="comm";var n="rule";var t="decl";var i="@import";var k=Math.abs;var d=String.fromCharCode;function g(e){return e.trim()}function y(e,r,a){return e.replace(r,a)}function z(e,r){return e.charCodeAt(r)|0}function C(e,r,a){return e.slice(r,a)}function A(e){return e.length}function M(e){return e.length}function O(e,r){return r.push(e),e}var q=1;var B=1;var D=0;var E=0;var F=0;var G="";function H(e,r,a,c,n,t,s){return {value:e,root:r,parent:a,type:c,props:n,children:t,line:q,column:B,length:s,return:""}}function J(){return F}function K(){F=E<D?z(G,E++):0;if(B++,F===10)B=1,q++;return F}function L(){return z(G,E)}function N(){return E}function P(e,r){return C(G,e,r)}function Q(e){switch(e){case 0:case 9:case 10:case 13:case 32:return 5;case 33:case 43:case 44:case 47:case 62:case 64:case 126:case 59:case 123:case 125:return 4;case 58:return 3;case 34:case 39:case 40:case 91:return 2;case 41:case 93:return 1}return 0}function R(e){return q=B=1,D=A(G=e),E=0,[]}function T(e){return G="",e}function U(e){return g(P(E-1,Y(e===91?e+2:e===40?e+1:e)))}function W(e){while(F=L())if(F<33)K();else break;return Q(e)>2||Q(F)>3?"":" "}function Y(e){while(K())switch(F){case e:return E;case 34:case 39:return Y(e===34||e===39?e:F);case 40:if(e===41)Y(e);break;case 92:K();break}return E}function Z(e,r){while(K())if(e+F===47+10)break;else if(e+F===42+42&&L()===47)break;return "/*"+P(r,E-1)+"*"+d(e===47?e:K())}function _(e){while(!Q(L()))K();return P(e,E)}function ee(e){return T(re("",null,null,null,[""],e=R(e),0,[0],e))}function re(e,r,a,c,n,t,s,u,i){var f=0;var o=0;var l=s;var v=0;var h=0;var p=0;var w=1;var b=1;var $=1;var k=0;var m="";var g=n;var x=t;var j=c;var z=m;while(b)switch(p=k,k=K()){case 34:case 39:case 91:case 40:z+=U(k);break;case 9:case 10:case 13:case 32:z+=W(p);break;case 47:switch(L()){case 42:case 47:O(ce(Z(K(),N()),r,a),i);break;default:z+="/";}break;case 123*w:u[f++]=A(z)*$;case 125*w:case 59:case 0:switch(k){case 0:case 125:b=0;case 59+o:if(h>0)O(h>32?ne(z+";",c,a,l-1):ne(y(z," ","")+";",c,a,l-2),i);break;case 59:z+=";";default:O(j=ae(z,r,a,f,o,n,u,m,g=[],x=[],l),t);if(k===123)if(o===0)re(z,r,j,j,g,t,l,u,x);else switch(v){case 100:case 109:case 115:re(e,j,j,c&&O(ae(e,j,j,0,0,n,u,m,n,g=[],l),x),n,x,l,u,c?g:x);break;default:re(z,j,j,j,[""],x,l,u,x);}}f=o=h=0,w=$=1,m=z="",l=s;break;case 58:l=1+A(z),h=p;default:switch(z+=d(k),k*w){case 38:$=o>0?1:(z+="\f",-1);break;case 44:u[f++]=(A(z)-1)*$,$=1;break;case 64:if(L()===45)z+=U(K());v=L(),o=A(m=z+=_(N())),k++;break;case 45:if(p===45&&A(z)==2)w=0;}}return t}function ae(e,r,a,c,t,s,u,i,f,o,l){var v=t-1;var h=t===0?s:[""];var p=M(h);for(var w=0,b=0,$=0;w<c;++w)for(var d=0,m=C(e,v+1,v=k(b=u[w])),x=e;d<p;++d)if(x=g(b>0?h[d]+" "+m:y(m,/&\f/g,h[d])))f[$++]=x;return H(e,r,a,t===0?n:i,f,o,l)}function ce(e,r,a){return H(e,r,a,c,d(J()),C(e,2,-2),0)}function ne(e,r,a,c){return H(e,r,a,t,C(e,0,c),C(e,c+1,-1),c)}function se(e,r){var a="";var c=M(e);for(var n=0;n<c;n++)a+=r(e[n],n,e,r)||"";return a}function ue(e,r,a,s){switch(e.type){case i:case t:return e.return=e.return||e.value;case c:return "";case n:e.value=e.props.join(",");}return A(a=se(e.children,s))?e.return=e.value+"{"+a+"}":""}function ie(e){var r=M(e);return function(a,c,n,t){var s="";for(var u=0;u<r;u++)s+=e[u](a,c,n,t)||"";return s}} function unwrapExports (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var dist = createCommonjsModule(function (module, exports) { var Vue; // late bind var version; var map = Object.create(null); if (typeof window !== 'undefined') { window.__VUE_HOT_MAP__ = map; } var installed = false; var initHookName = 'beforeCreate'; exports.install = function (vue, browserify) { if (installed) { return } installed = true; Vue = vue.__esModule ? vue.default : vue; version = Vue.version.split('.').map(Number); // compat with < 2.0.0-alpha.7 if (Vue.config._lifecycleHooks.indexOf('init') > -1) { initHookName = 'init'; } exports.compatible = version[0] >= 2; if (!exports.compatible) { console.warn( '[HMR] You are using a version of vue-hot-reload-api that is ' + 'only compatible with Vue.js core ^2.0.0.' ); return } }; /** * Create a record for a hot module, which keeps track of its constructor * and instances * * @param {String} id * @param {Object} options */ exports.createRecord = function (id, options) { if(map[id]) { return } var Ctor = null; if (typeof options === 'function') { Ctor = options; options = Ctor.options; } makeOptionsHot(id, options); map[id] = { Ctor: Ctor, options: options, instances: [] }; }; /** * Check if module is recorded * * @param {String} id */ exports.isRecorded = function (id) { return typeof map[id] !== 'undefined' }; /** * Make a Component options object hot. * * @param {String} id * @param {Object} options */ function makeOptionsHot(id, options) { if (options.functional) { var render = options.render; options.render = function (h, ctx) { var instances = map[id].instances; if (ctx && instances.indexOf(ctx.parent) < 0) { instances.push(ctx.parent); } return render(h, ctx) }; } else { injectHook(options, initHookName, function() { var record = map[id]; if (!record.Ctor) { record.Ctor = this.constructor; } record.instances.push(this); }); injectHook(options, 'beforeDestroy', function() { var instances = map[id].instances; instances.splice(instances.indexOf(this), 1); }); } } /** * Inject a hook to a hot reloadable component so that * we can keep track of it. * * @param {Object} options * @param {String} name * @param {Function} hook */ function injectHook(options, name, hook) { var existing = options[name]; options[name] = existing ? Array.isArray(existing) ? existing.concat(hook) : [existing, hook] : [hook]; } function tryWrap(fn) { return function (id, arg) { try { fn(id, arg); } catch (e) { console.error(e); console.warn( 'Something went wrong during Vue component hot-reload. Full reload required.' ); } } } function updateOptions (oldOptions, newOptions) { for (var key in oldOptions) { if (!(key in newOptions)) { delete oldOptions[key]; } } for (var key$1 in newOptions) { oldOptions[key$1] = newOptions[key$1]; } } exports.rerender = tryWrap(function (id, options) { var record = map[id]; if (!options) { record.instances.slice().forEach(function (instance) { instance.$forceUpdate(); }); return } if (typeof options === 'function') { options = options.options; } if (record.Ctor) { record.Ctor.options.render = options.render; record.Ctor.options.staticRenderFns = options.staticRenderFns; record.instances.slice().forEach(function (instance) { instance.$options.render = options.render; instance.$options.staticRenderFns = options.staticRenderFns; // reset static trees // pre 2.5, all static trees are cached together on the instance if (instance._staticTrees) { instance._staticTrees = []; } // 2.5.0 if (Array.isArray(record.Ctor.options.cached)) { record.Ctor.options.cached = []; } // 2.5.3 if (Array.isArray(instance.$options.cached)) { instance.$options.cached = []; } // post 2.5.4: v-once trees are cached on instance._staticTrees. // Pure static trees are cached on the staticRenderFns array // (both already reset above) // 2.6: temporarily mark rendered scoped slots as unstable so that // child components can be forced to update var restore = patchScopedSlots(instance); instance.$forceUpdate(); instance.$nextTick(restore); }); } else { // functional or no instance created yet record.options.render = options.render; record.options.staticRenderFns = options.staticRenderFns; // handle functional component re-render if (record.options.functional) { // rerender with full options if (Object.keys(options).length > 2) { updateOptions(record.options, options); } else { // template-only rerender. // need to inject the style injection code for CSS modules // to work properly. var injectStyles = record.options._injectStyles; if (injectStyles) { var render = options.render; record.options.render = function (h, ctx) { injectStyles.call(ctx); return render(h, ctx) }; } } record.options._Ctor = null; // 2.5.3 if (Array.isArray(record.options.cached)) { record.options.cached = []; } record.instances.slice().forEach(function (instance) { instance.$forceUpdate(); }); } } }); exports.reload = tryWrap(function (id, options) { var record = map[id]; if (options) { if (typeof options === 'function') { options = options.options; } makeOptionsHot(id, options); if (record.Ctor) { if (version[1] < 2) { // preserve pre 2.2 behavior for global mixin handling record.Ctor.extendOptions = options; } var newCtor = record.Ctor.super.extend(options); // prevent record.options._Ctor from being overwritten accidentally newCtor.options._Ctor = record.options._Ctor; record.Ctor.options = newCtor.options; record.Ctor.cid = newCtor.cid; record.Ctor.prototype = newCtor.prototype; if (newCtor.release) { // temporary global mixin strategy used in < 2.0.0-alpha.6 newCtor.release(); } } else { updateOptions(record.options, options); } } record.instances.slice().forEach(function (instance) { if (instance.$vnode && instance.$vnode.context) { instance.$vnode.context.$forceUpdate(); } else { console.warn( 'Root or manually mounted instance modified. Full reload required.' ); } }); }); // 2.6 optimizes template-compiled scoped slots and skips updates if child // only uses scoped slots. We need to patch the scoped slots resolving helper // to temporarily mark all scoped slots as unstable in order to force child // updates. function patchScopedSlots (instance) { if (!instance._u) { return } // https://github.com/vuejs/vue/blob/dev/src/core/instance/render-helpers/resolve-scoped-slots.js var original = instance._u; instance._u = function (slots) { try { // 2.6.4 ~ 2.6.6 return original(slots, true) } catch (e) { // 2.5 / >= 2.6.7 return original(slots, null, true) } }; return function () { instance._u = original; } } }); var hotReloadApi = unwrapExports(dist); var dist_1 = dist.install; var dist_2 = dist.compatible; var dist_3 = dist.createRecord; var dist_4 = dist.isRecorded; var dist_5 = dist.rerender; var dist_6 = dist.reload; /** * https://github.com/thiled/vue-scooter * Released under the MIT License. */ hotReloadApi.install(Vue); // const defaultRoot = location.origin + location.pathname; let rootPath = defaultRoot; let vueComponents = (window.vueComponents = {}); //vue组件存储 let vueFileLoadCount = 0; /** * 更改默认前缀,部署用 * @param {*} path */ const setRoot = (path) => { rootPath = path; }; /** * 相对路径转换 * @param {*} currentPath * @param {*} relativePath */ const _resolvePath = (currentPath, relativePath) => { // relativePath // 如果是url,取消处理 if (relativePath.match(/^https?:\/\//)) { return relativePath; } // remove ./ relativePath = relativePath.replace(/^\.\//, ''); // 拆分'../' let match = relativePath.match(/\.\.\//g); let count = (match && match.length) || 0; // 去掉相对路径后的剩余路径 let cleanPath = relativePath.replace(/\.\.\//g, ''); // currentPath // 拆分'.+/' let currentPathArr = currentPath.match(/.+?\/+/g); // 移除相对路径层级 currentPathArr.splice(currentPathArr.length - count, count); return currentPathArr.join('') + cleanPath; }; /** * 从vue文件读取template/script/style tag * @param {*} data * @param {*} tag */ const _getBlock = (data, tag) => { let regx = new RegExp(`<${tag}(.*?)>([\\w\\W]*)<\\/${tag}>`); let templateStr = data.match(regx); return { value: templateStr[2].replace(/\s*(.*)/, '$1'), attrs: templateStr[1], }; }; /** * 处理script * @param {*} vueFileUrl * @param {*} script */ const _handleScript = (vueFileUrl, script) => { return new Promise(async (resolve) => { let vueImports = []; // 清除注释 script = script.replace(/(?:^|\s)(\/\/.*)|(\/\*[\w\W]*?\*\/)/g, ''); // 识别import,转换路径,提取vue import script = script.replace( /(import\s+(\w+|.*)\s+from\s+['"])(.*?)(['"])/gs, ($0, $1, $2, $3, $4) => { // import相对路径转换 let fullPath = _resolvePath(vueFileUrl, $3); let importStatement = $1 + fullPath + $4; if ($3.match(/\.vue$/)) { // 识别import vue语句 vueImports.push({ importStatement: importStatement, name: $2, path: fullPath, }); } return importStatement; } ); // 遍历vue文件, 递归解析 while (vueImports.length > 0) { let element = vueImports.shift(); let componentName = element.name; let componentPath = element.path; await _load(componentPath, true); // 替换import vue文件的语句 script = script.replace( element.importStatement, `const ${componentName}= window.vueComponents['${componentPath}']` ); } resolve(script); }); }; /** * 处理template * @param {*} vueFileUrl * @param {*} template * @param {*} scopedDataAttr */ const _handleTemplate = (vueFileUrl, template, scopedDataAttr) => { // 替换tempate中src的相对路径 template = template.replace(/(\ssrc=['"])(.*?)(?=['"])/g, ($0, $1, $2) => { return $1 + _resolvePath(vueFileUrl, $2); }); if (scopedDataAttr) { // template中所有tag加scoped相关data属性 template = template.replace(/(<[\w-]+)/g, `$1 ${scopedDataAttr}`); } return template; }; /** * 处理style * @param {*} vueFileUrl * @param {*} style * @param {*} scopedDataAttr */ const _handleStyle = (vueFileUrl, style, scopedDataAttr) => { // 替换style中的url路径 style = style.replace(/(url\(['"])(.*?)(?=["']\))/g, ($0, $1, $2) => { return $1 + _resolvePath(vueFileUrl, $2); }); // 嵌套解析 if (scopedDataAttr) { // style中所有选择器添加scoped相关data属性匹配 style = se( ee(style), ie([ (element) => { if (element.type === 'rule') { element.props[0] += `[${scopedDataAttr}]`; } }, ue, ]) ); } else { style = se(ee(style), ue); } // 应用style let styleElement = document.createElement('style'); styleElement.append(style); document.head.appendChild(styleElement); }; /** * vue文件加载 * @param {*} vueFileUrl * @param {*} isFullPath */ const _load = (vueFileUrl, isFullPath = false, isReload = false) => { let scopedDataAttr; if (!isFullPath) { vueFileUrl = _resolvePath(rootPath, vueFileUrl); } return new Promise((resolve, reject) => { if (vueComponents[vueFileUrl] && !isReload) { // 读取缓存的组件对象 resolve(vueComponents[vueFileUrl]); return; } var xhr = new XMLHttpRequest(); xhr.onload = async (e) => { vueFileLoadCount++; let data = xhr.responseText; // 解析vue文件 let template = _getBlock(data, 'template').value; let script = _getBlock(data, 'script').value; let styleObj = _getBlock(data, 'style'); let style = styleObj.value; if (/scoped/.test(styleObj.attrs)) { scopedDataAttr = `data-v-${vueFileLoadCount}`; } script = await _handleScript(vueFileUrl, script); // 字符串js转脚本, 未知是否需要URI编码? // script = encodeURIComponent(script); const dataUri = 'data:text/javascript;charset=utf-8,' + script + vueFileLoadCount; import(dataUri).then((res) => { let component = res.default; template = _handleTemplate(vueFileUrl, template, scopedDataAttr); component.template = template; if (!vueComponents[vueFileUrl]) { // 组件对象存储, 相同路径组件只加载一次 vueComponents[vueFileUrl] = component; // hotReloadApi.createRecord(vueFileUrl, component); } else if (isReload) { hotReloadApi.reload(vueFileUrl, component); } _handleStyle(vueFileUrl, style, scopedDataAttr); // resolve(component); }); }; xhr.open('GET', vueFileUrl); xhr.send(); }); }; /** * load vue file * @param {*} vueFileUrl * @param {*} isFullPath */ const load = (vueFileUrl, isFullPath = false) => { return _load(vueFileUrl, isFullPath); }; /** * hot reload vue file * @param {*} vueFileUrl */ const reload = (vueFileUrl) => { _load(vueFileUrl, false, true); }; var index = { load, setRoot, reload, }; return index; }());