安装
`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和bodyshadowRoot.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
- 所有的元素的查询全部代理到shadowRoot内去查询
- head和body代理到shadowRoot的对应html元素上
- 由于iframe的url的host是主应用的,所以需要将host改回子应用自己的
- 对于location.href特殊逻辑的处理