前端开发干货:让 iframe 焕发新生开发( 二 )

  • 白屏时间太长,对于SPA 应用应用来说无法接受
  • 使用无界如果主应用是vue框架:
    安装
    `npm i @tencent/wujie-vue -S`引入
    mport WujieVue from "@tencent/wujie-vue";Vue.use(WujieVue);使用
    <WujieVuewidth="100%"height="100%"name="xxx"url="xxx":sync="true":fetch="fetch":props="props"@xxx="handleXXX"></WujieVue>其他框架也会在近期上线
    适配成本无界的适配成本非常低
    对于主应用无需做任何改造
    对于子应用:
    • 前提,必须开放跨域配置,因为子应用是在主应用域内请求和运行的
    • 对webpack应用,修改动态加载路径
    • 如果子应用保活模式则无需进一步修改,非保活则需要将实例化挂载到无界生命周期内
    if (window.__POWERED_BY_WUJIE__) {let instance;window.__WUJIE_MOUNT = () => {instance = new Vue({ router, render: (h) => h(App) }).$mount("#app");};window.__WUJIE_UNMOUNT = () => {instance.$destroy();};} else {new Vue({ router, render: (h) => h(App) }).$mount("#app");}实现细节实现一个纯净的 iframe子应用运行在一个和主应用同域的iframe中,设置src为替换了主域名host的子应用url,子应用路由只取location的pathname和hash
    但是一旦设置src后,iframe由于同域,会加载主应用的html、js,所以必须在iframe实例化完成并且还没有加载完html时中断加载,防止污染子应用
    此时可以采用轮询监听document.readyState状态来及时中断,对于一些浏览器比如safari状态不准确,可以在wujie主动抛错来防止有主应用的js运行
    iframe 数据劫持和注入子应用的代码 code 在 iframe 内部访问 window,document、location 都被劫持到相应的 proxy,并且还会注入$wujie对象供子应用调用
    const script = `(function(window, self, global, document, location, $wujie) {${code}n}).bind(window.__WUJIE.proxy)(window.__WUJIE.proxy,window.__WUJIE.proxy,window.__WUJIE.proxy,window.__WUJIE.proxy.document,window.__WUJIE.proxy.location,window.__WUJIE.provide);`;iframe 和 shadowRoot 副作用的处理iframe 内部的副作用处理在初始化iframe时进行,主要分为如下几部
    /** * 1、location劫持后的数据修改回来,防止跨域错误 * 2、同步路由到主应用 */patchIframeHistory(iframeWindow, appPublicPath, mainPublicPath);/** * 对window.addEventListener进行劫持,比如resize事件必须是监听主应用的 */patchIframeEvents(iframeWindow);/** * 注入私有变量 */patchIframeVariable(iframeWindow, appPublicPath);/** * 将有DOM副作用的统一在此修改,比如mutationObserver必须调用主应用的 */patchIframeDomEffect(iframeWindow);/** * 子应用前进后退,同步路由到主应用 */syncIframeUrlToWindow(iframeWindow);ShadowRoot 内部的副作用必须进行处理,主要处理的就是shadowRoot的head和body
    shadowRoot.head.appendChild = getOverwrittenAppendChildOrInsertBefore({rawDOMAppendOrInsertBefore: rawHeadAppendChild}) as typeof rawHeadAppendChildshadowRoot.head.insertBefore = getOverwrittenAppendChildOrInsertBefore({rawDOMAppendOrInsertBefore: rawHeadInsertBefore as any}) as typeof rawHeadInsertBeforeshadowRoot.body.appendChild = getOverwrittenAppendChildOrInsertBefore({rawDOMAppendOrInsertBefore: rawBodyAppendChild}) as typeof rawBodyAppendChildshadowRoot.body.insertBefore = getOverwrittenAppendChildOrInsertBefore({rawDOMAppendOrInsertBefore: rawBodyInsertBefore as any}) as typeof rawBodyInsertBefore
    getOverwrittenAppendChildOrInsertBefore主要是处理四种类型标签:
    • link/style标签收集stylesheetElement并用于子应用重新激活重建样式
    • script标签动态插入的script标签必须从ShadowRoot转移至iframe内部执行
    • iframe标签修复iframe的指向对应子应用iframe的window
    iframe 的 document 改造由于js在iframe运行需要和shadowRoot,包括元素创建、事件绑定等等,将iframe的document进行劫持:
    • 所有的元素的查询全部代理到shadowRoot内去查询
    • head和body代理到shadowRoot的对应html元素上
    iframe 的 location 改造将iframe的location进行劫持:
    • 由于iframe的url的host是主应用的,所以需要将host改回子应用自己的
    • 对于location.href特殊逻辑的处理
    总结通过上面原理以及细节的阐述,我们可以得出无界微前端框架的几点优势: