当前位置: 首页 > news >正文

电商网站前后台模板小说风云榜

电商网站前后台模板,小说风云榜,怎么做网站版面分析,做网站靠什么收入乾坤 乾坤js隔离机制及发展历程 qiankun有三种js隔离机制,分别是SnapshotSandbox、LegacySandbox、ProxySandbox。这三种沙箱模式的中文解释分别为快照沙箱、支持单应用的代理沙箱和支持多应用的代理沙箱。 一开始乾坤也只有一种沙箱叫“快照沙箱”,也就是由Snapsh…

乾坤

乾坤js隔离机制及发展历程

qiankun有三种js隔离机制,分别是SnapshotSandbox、LegacySandbox、ProxySandbox。这三种沙箱模式的中文解释分别为快照沙箱、支持单应用的代理沙箱和支持多应用的代理沙箱。

一开始乾坤也只有一种沙箱叫“快照沙箱”,也就是由SnapshotSandbox类来实现的沙箱。这个沙箱有个缺点,就是需要遍历window上的所有属性,性能较差。随着ES6的普及,利用Proxy可以比较良好的解决这个问题,这就诞生了LegacySandbox,可以实现和快照沙箱一样的功能,但是却性能更好,和SnapshotSandbox一样,由于会污染全局的window,LegacySandbox也仅仅允许页面同时运行一个微应用,所以我们也称LegacySandbox为支持单应用的代理沙箱。从LegacySandbox这个类名可以看出,一开始肯定是不叫LegacySandbox,是因为有了更好的机制,才将这个名字强加给它了。那这个更好的机制是什么呢,就是ProxySandbox,它可以支持一个页面运行多个微应用,因此我们称ProxySandbox为支持多应用的代理沙箱。事实上,LegacySandbox在未来应该会消失,因为LegacySandbox可以做的事情,ProxySandbox都可以做,而SanpsshotSandbox因为向下兼容的原因反而会和ProxySandbox长期并存。

ProxySandbox的实现原理

先看下极简版的代码

class ProxySandBox{proxyWindow;isRunning = false;active(){this.isRunning = true;}inactive(){this.isRunning = false;}constructor(){const fakeWindow = Object.create(null);this.proxyWindow = new Proxy(fakeWindow,{set:(target, prop, value, receiver)=>{if(this.isRunning){target[prop] = value;}},get:(target, prop, receiver)=>{return  prop in target ? target[prop] : window[prop];}});}
}
// 验证:
let proxySandBox1 = new ProxySandBox();
let proxySandBox2 = new ProxySandBox();

从上面的代码可以发现,ProxySandbox,完全不存在状态恢复的逻辑,同时也不需要记录属性值的变化,因为所有的变化都是沙箱内部的变化,和window没有关系,window上的属性至始至终都没有受到过影响。

通过代理的window的proxy,子应用的js执行的时候是如何把它当作window使用?

执行子应用js代码的时候会将this.proxyWindow作为参数传入,这样子应用原本应该直接操作window的地方,都是操作这个proxyWindow对象,实现了代理功能。具体代码体现如下:

window.proxy = proxy; // 这里的proxy就是我们通过参数传入的proxyWindow对象
return `;(function(window, self, globalThis){;${scriptText}\n${sourceUrl}}).bind(window.proxy)(window.proxy, window.proxy, window.proxy);`; // 这里与实际代码相比做了一定简化 

import-html-entry

import-html-entry 是 qiankun 中一个举足轻重的依赖,用于获取子应用的 HTML 和 JS,同时对 HTML 和 JS 进行了各自的处理,以便于子应用在父应用中加载

import-html-entry的工作流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FEQ7jZnB-1690978348321)(media/16885204415196/%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A120230705-095948@2x.png)]

import-html-entry主要解析原理

  1. 通过fetch获取entry资源
  2. processTpl通过正则解析html模版并抽取script、style并删除注释
  3. getExternalStyleSheets将link样式转换为inlineStyle
  4. execScripts执行抽取的script脚本

无界

无界代码隔离实现原理

  1. 实现自定义webComponent,通过shadowRoot实现原生html及style隔离。在polyfill(浏览器不兼容webCompnent)情况下,会降级为iframe

     /* 降级处理 */
    // 如果浏览器不兼容webComponent
    if (this.degrade) {const iframeBody = rawDocumentQuerySelector.call(iframeWindow.document, "body") as HTMLElement;const { iframe, container } = initRenderIframeAndContainer(this.id, el ?? iframeBody, this.degradeAttrs);this.el = container;// 销毁js运行iframe容器内部domif (el) clearChild(iframeBody);// 修复vue的event.timeStamp问题patchEventTimeStamp(iframe.contentWindow, iframeWindow);// 当销毁iframe时主动unmount子应用iframe.contentWindow.onunload = () => {this.unmount();};if (this.document) {if (this.alive) {iframe.contentDocument.replaceChild(this.document.documentElement, iframe.contentDocument.documentElement);// 保活场景需要事件全部恢复
    recoverEventListeners(iframe.contentDocument.documentElement, iframeWindow);} else {await renderTemplateToIframe(iframe.contentDocument, this.iframe.contentWindow, this.template);// 非保活场景需要恢复根节点的事件,防止react16监听事件丢失recoverDocumentListeners(this.document.documentElement, iframe.contentDocument.documentElement, iframeWindow);}} else {await renderTemplateToIframe(iframe.contentDocument, this.iframe.contentWindow, this.template);}this.document = iframe.contentDocument;return;
    }// 浏览器兼容webComponent
    if (this.shadowRoot) {this.el = renderElementToContainer(this.shadowRoot.host, el);if (this.alive) return;
    } else {// 预执行无容器,暂时插入iframe内部触发Web Component的connectconst iframeBody = rawDocumentQuerySelector.call(iframeWindow.document, "body") as HTMLElement;this.el = renderElementToContainer(createWujieWebComponent(this.id), el ?? iframeBody);
    }
    
  2. script通过iframe加载隔离,不会侵入宿主脚本

webComponent中没有script,怎么实现页面交互行为?怎么加载的iframe中的script?

大概的解析过程如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-16TwhMeT-1690978348322)(media/16885204415196/%E4%BC%81%E4%B8%9A%E5%BE%AE%E4%BF%A120230705-144933@2x.png)]

代理实现代码如下:

export function proxyGenerator(iframe: HTMLIFrameElement,urlElement: HTMLAnchorElement,mainHostPath: string,appHostPath: string
): {proxyWindow: Window;proxyDocument: Object;proxyLocation: Object;
} {const proxyWindow = new Proxy(iframe.contentWindow, {get: (target: Window, p: PropertyKey): any => {// location进行劫持if (p === "location") {return target.__WUJIE.proxyLocation;}// 判断自身if (p === "self" || (p === "window" && Object.getOwnPropertyDescriptor(window, "window").get)) {return target.__WUJIE.proxy;}// 不要绑定thisif (p === "__WUJIE_RAW_DOCUMENT_QUERY_SELECTOR__" || p === "__WUJIE_RAW_DOCUMENT_QUERY_SELECTOR_ALL__") {return target[p];}// 省略代码...},set: (target: Window, p: PropertyKey, value: any) => {checkProxyFunction(value);target[p] = value;return true;},has: (target: Window, p: PropertyKey) => p in target,});// proxy documentconst proxyDocument = new Proxy({},{get: function (_fakeDocument, propKey) {const document = window.document;const { shadowRoot, proxyLocation } = iframe.contentWindow.__WUJIE;// iframe初始化完成后,webcomponent还未挂在上去,此时运行了主应用代码,必须中止if (!shadowRoot) stopMainAppRun();const rawCreateElement = iframe.contentWindow.__WUJIE_RAW_DOCUMENT_CREATE_ELEMENT__;const rawCreateTextNode = iframe.contentWindow.__WUJIE_RAW_DOCUMENT_CREATE_TEXT_NODE__;// need fixif (propKey === "createElement" || propKey === "createTextNode") {// 。。。。}if (propKey === "documentURI" || propKey === "URL") {return (proxyLocation as Location).href;}// from shadowRootif (propKey === "getElementsByTagName" ||propKey === "getElementsByClassName" ||propKey === "getElementsByName") {// 。。。  }if (propKey === "getElementById") {// 。。。}if (propKey === "querySelector" || propKey === "querySelectorAll") {const rawPropMap = {querySelector: "__WUJIE_RAW_DOCUMENT_QUERY_SELECTOR__",querySelectorAll: "__WUJIE_RAW_DOCUMENT_QUERY_SELECTOR_ALL__",};//。。。}if (propKey === "documentElement" || propKey === "scrollingElement") return shadowRoot.firstElementChild;if (propKey === "forms") return shadowRoot.querySelectorAll("form");if (propKey === "images") return shadowRoot.querySelectorAll("img");if (propKey === "links") return shadowRoot.querySelectorAll("a");const { ownerProperties, shadowProperties, shadowMethods, documentProperties, documentMethods } =documentProxyProperties;if (ownerProperties.concat(shadowProperties).includes(propKey.toString())) {if (propKey === "activeElement" && shadowRoot.activeElement === null) return shadowRoot.body;return shadowRoot[propKey];}if (shadowMethods.includes(propKey.toString())) {return getTargetValue(shadowRoot, propKey) ?? getTargetValue(document, propKey);}// from window.documentif (documentProperties.includes(propKey.toString())) {return document[propKey];}if (documentMethods.includes(propKey.toString())) {return getTargetValue(document, propKey);}},});// proxy locationconst proxyLocation = new Proxy({},{get: function (_fakeLocation, propKey) {const location = iframe.contentWindow.location;if (propKey === "host" ||propKey === "hostname" ||propKey === "protocol" ||propKey === "port" ||propKey === "origin") {return urlElement[propKey];}if (propKey === "href") {return location[propKey].replace(mainHostPath, appHostPath);}if (propKey === "reload") {warn(WUJIE_TIPS_RELOAD_DISABLED);return () => null;}if (propKey === "replace") {return new Proxy(location[propKey], {apply(replace, _ctx, args) {return replace.call(location, args[0]?.replace(appHostPath, mainHostPath));},});}return getTargetValue(location, propKey);},set: function (_fakeLocation, propKey, value) {// 如果是跳转链接的话重开一个iframeif (propKey === "href") {return locationHrefSet(iframe, value, appHostPath);}iframe.contentWindow.location[propKey] = value;return true;},ownKeys: function () {return Object.keys(iframe.contentWindow.location).filter((key) => key !== "reload");},getOwnPropertyDescriptor: function (_target, key) {return { enumerable: true, configurable: true, value: this[key] };},});return { proxyWindow, proxyDocument, proxyLocation };
}

应用间通信实现原理

wujie应用间通讯、资源共享等通过主应用window挂载实现。

模版解析原理

与import-html-entry相同

micro-app

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V9Kc1NIf-1690978348322)(media/16885204415196/v2-ded24e5f4f92bb505dd5baa09797fc03_r.jpeg)]

js隔离

沙箱模式,类似乾坤的ProxySandbox。

css隔离

micro-app的css隔离类似CSS MODULE,通过添加唯一name前缀实现的class类名隔离。

元素隔离

micro-app实现了类似shadowDom功能,元素不会逃脱边界。


/*** define element* @param tagName element name*/
export function defineElement (tagName: string): void {class MicroAppElement extends HTMLElement implements MicroAppElementType {static get observedAttributes (): string[] {return ['name', 'url']}private isWaiting = falseprivate cacheData: Record<PropertyKey, unknown> | null = nullprivate connectedCount = 0private connectStateMap: Map<number, boolean> = new Map()public appName = '' // app namepublic appUrl = '' // app urlpublic ssrUrl = '' // html path in ssr modepublic version = version//...someHanlder// create app instanceprivate handleCreateApp (): void {const createAppInstance = () => new CreateApp({name: this.appName,url: this.appUrl,container: this.shadowRoot ?? this,scopecss: this.useScopecss(),useSandbox: this.useSandbox(),inline: this.getDisposeResult('inline'),iframe: this.getDisposeResult('iframe'),ssrUrl: this.ssrUrl,})/*** Actions for destroy old app* If oldApp exist, it must be 3 scenes:*  1. oldApp is unmounted app (url is is different)*  2. oldApp is prefetch, not prerender (url, scopecss, useSandbox, iframe is different)*  3. oldApp is prerender (url, scopecss, useSandbox, iframe is different)*/const oldApp = appInstanceMap.get(this.appName)if (oldApp) {if (oldApp.isPrerender) {this.unmount(true, createAppInstance)} else {oldApp.actionsForCompletelyDestroy()createAppInstance()}} else {createAppInstance()}}/*** Data from the base application*/set data (value: Record<PropertyKey, unknown> | null) {if (this.appName) {microApp.setData(this.appName, value as Record<PropertyKey, unknown>)} else {this.cacheData = value}}/*** get data only used in jsx-custom-event once*/get data (): Record<PropertyKey, unknown> | null {if (this.appName) {return microApp.getData(this.appName, true)} else if (this.cacheData) {return this.cacheData}return null}}globalEnv.rawWindow.customElements.define(tagName, MicroAppElement)
}

通讯

主应用会向所有子应用注入microApp对象,通过统一对象实现应用间通讯

框架对比

对比qiankunwujiemicroApp
体积94kb78kb30kb
数据通讯机制基于props属性传递发布订阅 + CustomEvent发布订阅 + CustomEvent
接入成本
多框架兼容
js沙箱稳定
window侵入x
样式隔离x
元素隔离x
预加载
保活模式x

目前看来,乾坤的接入成本及js沙箱稳定系较差,但生态较强。无界代码隔离较好,但window挂载数据量较大比较适合中小型的微前端集成。microapp与无界较为类似,但window挂载数据量较小,沙箱隔离度较好,但接入适配仍需调研。

附上自己搭建的基于vite、wujie搭建的微前端框架:
主工程:https://github.com/SplitterChan/micro-v3-vite-template
子工程:https://github.com/SplitterChan/micro-v3-vite-sub-template

http://www.ds6.com.cn/news/65385.html

相关文章:

  • 国际建设管理学会网站自己创建个人免费网站
  • 国内优秀的设计网站推荐简易网站制作
  • 广东装饰网站建设百度官方电话24小时
  • 网站开发ipv6升级百度的网站网址
  • 苹果CMS如何做视频网站搜索引擎营销的实现方法
  • 黄骅港属于哪个市安卓优化大师手机版下载
  • 为拟建设的网站申请一个域名网搜网
  • 做门户网站需要什么条件信息流优化师简历怎么写
  • 郑州网站建设网络公司chatgpt 链接
  • 名校长工作室网站建设seo搜索引擎优化名词解释
  • 网站建设的费用计入免费个人网站申请
  • 企业网站 .net网站多少钱
  • 莱芜金点子电子版招聘seo代码优化有哪些方法
  • 网站建设服务天软科技考研培训机构排名前五的机构
  • 用php做电子商务网站杭州网站提升排名
  • 搏彩网站开发建设宁波免费seo排名优化
  • 酒店设计网站推荐专业拓客团队怎么收费
  • 网站建设怎么建设网络营销应用方式
  • 培训制作网站seo公司优化方案
  • 网站定制解决方案长沙seo研究中心
  • 校园网站设计代码怎么下载百度
  • 佛山网站建设企业推荐参考消息网国内新闻
  • 做网站的公司武汉网络推广广告公司
  • nba最新排名榜西部网站优化的方法与技巧
  • 最专业的手机网站建设seo百度网站排名软件
  • 福建微网站建设公司海外推广专员
  • 响应式网站排名如何免费行情网站的推荐理由
  • 安徽网站优化建设重庆百度seo整站优化
  • 上海网站建设系统婚恋网站排名前10
  • 建设企业外贸网站营销广告