Wujie 主流程源码阅读记录
1. 主应用 index.js 执行 npm 包 wujie-react,内部执行 npm 包 wujie:
2. wujie 执行
1. new EventBus
2. 处理子应用链接跳转 processAppForHrefJump,即监听 popstate 处理 href 的跳转,前进则 renderIframeReplaceApp,后退则 renderElementToContainer;
3. 定义 webComponent 容器 defineWujieWebComponent:<wujie-app>
3. 回到主应用开始执行 setupApp->addSandboxCacheWithOptions->存入 idToSandboxCacheMap(Wujie 实例里的 inject.idToSandboxMap)
4. 主应用继续执行 preloadApp,内部执行:【异步 1】requestIdleCallback;
5. 主应用执行正常渲染页面逻辑,本轮结束;
执行异步 1,requestIdleCallback 里
1. 合并配置
2. 执行 new WuJie({}),constructor 执行:
1. 创建目标地址的解析:appRouteParse(url),得到 urlElement, appHostPath, appRoutePath;
2. 创建 iframe(与主应用同域)【js 沙箱】:iframeGenerator(this, attrs, mainHostPath, appHostPath, appRoutePath);
1. 创建同域 iframe 并 appendChild 到 body 上
2. 变量需要提前注入,在入口函数通过变量防止死循环 patchIframeVariable
3. 【异步 2】停止加载 iframe:stopIframeLoading,内部通过 loop 递归比较两者 document 是否一致从而判断 iframe 是否已经 append 了,如果 append 则执行 stop;停止加载完成后执行 initIframeDom 初始化 iframe 的 dom 结构:
1. initBase:初始化 base 标签
2. patchIframeHistory:重写 pushState 和 replaceState,内部会执行 updateBase 和 syncUrlToWindow
3. patchIframeEvents:iframewindow 新增 addEventListener 和 removeEventListener 事件,分别处理事件挂载到 iframe 还是 window 下
4. syncIframeUrlToWindow:增加事件 hashchange、popstate,内执行 syncUrlToWindow。子应用前进后退,同步路由到主应用
5. patchWindowEffect:遍历 window 的所有属性并处理,部分属性被主 window 的属性覆写
6. patchDocumentEffect:! addEventListener/removeEventListener、document 的重写;
7. patchNodeEffect:重写 getRootNode、appendChild、insertBefore;
8. patchRelativeUrlEffect:执行 fixElementCtrSrcOrHref 对接口的 prototype 进行重写 set,设置正确的路径
3. 执行 window、document、location 代理:proxyGenerator(),供 initIframeDom 里或代码注入时使用;
4. 更新 idToSandboxCacheMap: addSandboxCacheWithWujie(),new Wujie 执行完毕。
5. 《总结:new Wujie 主要是创建了同域 iframe 后立即挟持 web api,并搞了一套 proxy》
3. 执行 runPreload
1. 执行主应用 beforeLoad 钩子
2. 【异步 3】【此时会先执行异步 2 的回调】执行 importHTML 得到 template, getExternalScripts, getExternalStyleSheets
3. 【异步 4】在异步 3 的回调里执行,执行 processCssLoader:处理 css-loader
4. 【异步 5】在异步 4 的回调里执行,执行 sandbox.active:激活子应用
1. await this.iframeReady;确保 iframe 加载完毕【即异步 2】且 initIframeDom 执行完毕;
2. 处理子应用自定义 fetch;
3. 同步路由 syncUrlToIframe,syncUrlToWindow;
4. 创建自定义元素<wujie-app/> :createWujieWebComponent
5. 将<wujie-app />插入到 iframe 里的 document.body 里
1. renderElementToContainer,插入后执行 defineWujieWebComponent->connectedCallback 里的回调:初始化 shadowDom 得到 shadowRoot,并执行 patchElementEffect 修正 shadowRoot element
6. 将 template 渲染到 shadowRoot,renderTemplateToShadowRoot
1. 将 template 渲染为 html 元素: renderTemplateToHtml,内部通过 createTreeWalker 遍历 html 树,并对每个 element 执行 patchElementEffect 修正,同时将 IMG、A、SOURCE 的相对路径修正为绝对路径;
2. 执行 processCssLoaderForTemplate,处理 css-before-loader 以及 css-after-loader,得到处理后的 html;
3. 将该 html 挂载到 shadowRoot 节点上即<wujie-app />里
4. html 插入一个 div;
5. 修复 html parentNode:
6. 执行 patchRenderEffect:内部执行 patchEventListener 记录 shadowRoot 的 head 和 body 的事件,等重新渲染复用 head 和 body 时需要清空事件;重写 shadowRoot 的 contains、shadowRoot 的 head 的 appendChild、insertBefore、removeChild、contains,body 的 appendChild、insertBefore 方法:rewriteAppendOrInsertChild。内部对 LINK、STYLE、SCRIPT、IFRAME 做了处理; 配合 proxy 使用
7. 执行 patchCssRules:子应用样式打补丁;sandbox.active 执行完毕;
8. 《总结:sandbox.active 同步路由后,创建<wujie-app>插入对应 dom(iframe document body 或自定义 dom)并将 template 渲染到 shadowRoot 后,对 shadowRoot 上的 dom 操作方法进行挟持以特殊处理 LINK、STYLE、SCRIPT、IFRAME》
5. 【异步 6】在异步 5 的回调里执行,因 exec 为 true,则执行 sandbox.start:启动子应用运行 js,处理兼容模式
1. 执行 beforeScriptResultList,内部执行 insertScriptToIframe
2. 执行 syncScriptResultList 及 deferScriptResultList,内部执行 insertScriptToIframe
3. 执行 wujie 实例方法 mount()
4. 执行 domContentLoadedTrigger,触发 DOMContentLoaded 事件
5. 执行 afterScriptResultList,内部执行 insertScriptToIframe
6. 执行 domLoadedTrigger,触发 loaded 事件
7. sandbox.start 执行完毕,即 runPreload 执行完毕,即 requestIdleCallback 执行完毕,即 preloadApp 执行完毕。回头说明两个方法:
1. insertScriptToIframe:通过 scriptResult 获取 script 脚本,对于内联脚本进行包裹后 appendChild 到 el 上,然后执行 afterExecScript 执行下一个 script;
2. mount:
1. beforeMount 钩子执行
2. 执行 iframeWindow.\_\_WUJIE_MOUNT();即子应用渲染流程;
3. afterMount 钩子执行
4. 执行下一个 script;
—————————
点击左侧导航,跳转 react16,即渲染 WujieReact react 组件,内部执行 componentDidMount->startApp->wujie startApp->
1. 合并配置
2. 执行 sandbox.unmount()清理;
3. 执行 sandbox.active 激活子应用
4. 重建 css,sandbox.rebuildStyleSheets();
5. beforeMount 钩子执行
6. 执行 iframeWindow.\_\_WUJIE_MOUNT();即子应用渲染流程;
7. afterMount 钩子执行;完毕。
—————————