UNPKG

mustard-app

Version:

个人前端微应用建设中。。。

147 lines (135 loc) 5.3 kB
import { fetchSource, getCompletePath } from '.'; import { IApp } from '../typings'; import { scopedCSS } from './scopedcss'; import { v4 } from 'uuid'; import { isRelativePath, isRemotezElement } from './tools'; // 请求link function fetchLinkFormHtml (app:IApp, htmlDom: HTMLDivElement) { const head = htmlDom.querySelector('mustard-app-head'); const linkEntries = Array.from(app.source.links.entries()); // 通过fetch请求所有css资源 const fetchLinkPromise:Promise<string>[] = []; for (const [url, info] of linkEntries) { fetchLinkPromise.push( info.isExternal ? fetchSource(url, app.url) : Promise.resolve(info.code) ); } Promise.all(fetchLinkPromise).then(res =>{ res.forEach(code => { const styleEle = document.createElement('style'); styleEle.textContent = code; // 处理css,加上前缀 mustard-app[name='${appName}'] scopedCSS(styleEle, app.name); head && head.appendChild(styleEle); }); app.onLoad(htmlDom); }).catch(error => app.error(error)); } function fetchScriptFormHtml (app:IApp, htmlDom: HTMLDivElement) { const scriptEntries = Array.from(app.source.scripts.entries()); // 通过fetch请求所有css资源 const fetchPromise:Promise<string>[] = []; for (const [url, info] of scriptEntries) { fetchPromise.push( info.isExternal ? fetchSource(url, app.url) : Promise.resolve(info.code)); } Promise.all(fetchPromise).then(res =>{ res.forEach((code, i) => { // 将代码放入缓存,再次渲染时可以从缓存中获取 scriptEntries[i][1].code = code; }); app.onLoad(htmlDom); }).catch(error => app.error(error)); } /** * 递归并处理dom * 收集静态的样式和js * 处理远程资源 e.g img,video,audio * @param parent * @param app */ function extractSourceDom (parent:Element, app:IApp) { const children = Array.from(parent.children); children?.length && children.forEach(child => extractSourceDom(child, app)); for (const dom of children) { // const attrs = dom.getAttributeNames(); // attrs.forEach(attr => { // // 处理dom上直接绑定的事件 // if(/^on[a-zA_Z]+/.test(attr)){ // const time = attr + v4(); // app.source.domClick += ` // this.${time} = function(){ // ${dom.getAttribute(attr)}}\n; // `; // dom.setAttribute(attr,`proxyWindow.${time}()`); // } // }); // link 记录并收集,并提取src if(dom instanceof HTMLLinkElement) { const href = dom.getAttribute('href'); if (dom.getAttribute('rel') === 'stylesheet' && href) { app.source.links.set(href, { code: '', isExternal: true }); } parent.removeChild(dom); }else if(dom instanceof HTMLStyleElement) { // style 记录并收集,并提取code app.source.links.set(v4(), { code: dom.textContent ?? '' }); parent.removeChild(dom); }else if(dom instanceof HTMLScriptElement) { // script 记录并收集,并提取code const src = dom.getAttribute('src'); // 远程js if(src) { app.source.scripts.set(src, { code: '', isExternal: true // 是否远程js }); }else{ app.source.scripts.set(v4(), { code: dom.textContent ?? '' }); } parent.removeChild(dom); }else if(isRemotezElement(dom)) { // 远程资源相对地址处理 const src = dom.getAttribute('src'); if(isRelativePath(src)) { dom.setAttribute('src', getCompletePath(src, app.url)); } } } } export function loadHtml (app:IApp) { fetchSource(app.url).then(html => { html = html .replace(/<head[^>]*>[\s\S]*?<\/head>/i, match => match.replace(/<head/i, '<mustard-app-head').replace(/<\/head>/i, '</mustard-app-head>') ).replace(/<body[^>]*>[\s\S]*?<\/body>/i, match => match.replace(/<body/i, '<mustard-app-body').replace(/<\/body>/i, '</mustard-app-body>') ); // htmlText -> Dom const Box = document.createElement('div'); Box.innerHTML = html; // 提取静态js和link,处理style extractSourceDom(Box, app); if(app.source.links.size) { fetchLinkFormHtml(app, Box); }else{ app.onLoad(Box); } if(app.source.scripts.size) { fetchScriptFormHtml(app, Box); }else{ app.onLoad(Box); } }).catch(error => app.error(error)); }