如何開發跨框架的組件

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"背景"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"筆者所在的業務中臺團隊,需要提供業務組件給不同的上層業務方使用,但因爲一些歷史遺留問題,不同業務線使用的框架不統一,包括  jQuery、React 、Vue。爲了滿足不同業務方的需求,往往需要根據業務方使用的框架,開發對應框架的組件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣做就會產生一些"},{"type":"text","marks":[{"type":"strong"}],"text":"痛點"},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每種選型都需要開發一次,費時勞力"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"組件升級,需要業務方同步發版升級,溝通成本高、迭代效率低"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"理想中的組件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"跨框架:"},{"type":"text","marks":[{"type":"strong"}],"text":"Write once, run everywhere"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"少升級:組件升級,業務方少升級不升級(注意:組件升級後業務線迴歸還是必要的)"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"實現方案"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如何設計一個符合上面提到的跨框架、少升級期望的通用方案呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很容易想到用原生 JS 來實現,避免跨框架的問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"原生實現"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用原生 JS 實現,包含頁面裏用到的 UI 組件,不依賴任何框架。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"優點:"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"跨框架:不依賴於框架實現"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"輕量:可以不依賴其他 UI 組件,體積較小"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"缺點:"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"投入產出比低:實現一套常用工具方法和 UI 組件,投入時間長"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"踩坑:兼容性問題的坑要走一遍,風險大"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很難滿足複雜業務場景的需求"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"適用場景"},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不需要複雜交互的場景,如前臺吊頂、後臺菜單側邊欄可採用這種方式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但在實際的業務場景中,業務組件中有比較多複雜的交互場景, 上面的方案不太能滿足要求,所以我們在上面的方案之上進行迭代:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"原生容器組件 + iframe 加載業務邏輯組件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們將業務組件拆分爲兩部分:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"一、容器組件"},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用原生 JS 實現中間層容器組件,解決跨框架的加載問題,容器組件主要負責:"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"收集組件需要的參數"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"註冊全局回調"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"組件掛載"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"加載 iframe"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"二、業務邏輯組件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據 iframe 天然的沙箱特性,業務邏輯用 iframe 頁面加載,就保證了業務組件的實現不受框架的限制,可以完美解決問題。業務邏輯組件主要負責:"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"與容器組件通信"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"運行環境隔離,可以使用任意框架實現業務邏輯"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"缺點:"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"動態加載靜態資源,iframe 加載略慢,實際體驗在接受範圍內"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"跨域通信問題"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此方案容器組件作爲中間層,封裝不變的邏輯,將多變的業務邏輯隔離出來,從而保證協作方儘量少升級或不升級。業務定製性可根據接口配置,返回不同的 iframe 地址,加載不同的業務邏輯組件,一次開發任意使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"如何實現"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"下面是整個組件的邏輯圖"},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/25\/25d92589b054d8db28accbfc4b7949c4.png","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用方通過容器組件初始化參數、並註冊相應的回調:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"容器組件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"初始化"},{"type":"text","text":":"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"設置 document.domain,讓外部組件和 iframe 可以通信"}]}]}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"\/\/ 獲取主域名\nfunction getTopLevelDomain(host) {\n    let data = host || window.location.host;\n    return data.split('.').slice(-2).join('.');\n}\n\/\/ 設置主域名\nfunction setDomainToTopLevelDomain() {\n  try {\n    window.document.domain = getTopLevelDomain();\n  } catch (error) {\n    console.error(\"設置domain失敗\")\n  }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"render:"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"生成外部容器 div ,設置 loading 圖,掛載組件"}]}]}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"class Vanilla {\n \/\/ 獲取配置信息\n constructor(config) {\n const options = { ...defaultConfig, ...config };\n this.options = options; \n this.elCls = options.elCls;\n }\n \/\/ 生成容器 div\n render() {\n const div = document.createElement('div');\n this.el = div;\n \n const { width, height } = this.options;\n div.className = `${prefixCls}-wrap ${prefixCls}-wrap-loading ${this.elCls || ''}`;\n const maskNode = getMaskNode(prefixCls);\n const iframeNode = getIframeNode(prefixCls, width, height);\n div.innerHTML = maskNode + iframeNode;\n document.body.appendChild(div);\n this.fn();\n }\n init() {\n \/\/ 設置主域名\n setDomainToTopLevelDomain();\n \/\/ 初始化 div\n this.render();\n \/\/ 初始化全局回調函數\n this.initCallbacks();\n }\n ...\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"註冊回調函數"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過註冊全局回調函數,用於業務邏輯組件與容器組件進行通信"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"typescript"},"content":[{"type":"text","text":"class Vanilla {\n ... \n initCallbacks() {\n const self = this;\n const options = this.options;\n \/\/ 初始化全局變量\n window[paramsName] = options;\n \n window.onSuccess = function onSuccess(data, res) {\n options.onSuccess && options.onSuccess(data, res);\n \/\/ 延遲1500ms刪除用來顯示成功提示\n setTimeout(() => {\n self.removeNode();\n }, 1500);\n self.resetCallbacks && self.resetCallbacks();\n };\n window.onCancel = function onCancel() {\n options.onCancel && options.onCancel();\n self.removeNode();\n self.resetCallbacks && self.resetCallbacks();\n };\n window.onError = function onError(data) {\n options.onError && options.onError(data);\n };\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"加載 iframe 頁面:"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過接口獲取 iframe 地址,業務方可以根據配置動態,加載不同的業務組件"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"typescript"},"content":[{"type":"text","text":"let timer = function timer() {};\nclass Vanilla {\n ...\n $mount() {\n ...\n this.fn();\n }\n fn() {\n const {\n width,\n height,\n isAutoSize,\n } = this.options;\n const el = this.el;\n const url = getContentUrl('你的iframe地址');\n const iframeWidth = width;\n const iframeHeight = height;\n const iframeEle = el.querySelector('.J_CreditIframe');\n const modalNode = el.querySelector(`.${prefixCls}`);\n\n if (!isAutoSize && (iframeWidth !== width || iframeHeight !== height)) {\n this.setNodeSizeAndPostion(modalNode, iframeEle, iframeWidth, iframeHeight);\n }\n iframeEle.setAttribute('src', url);\n \/\/ 監聽load後,隱藏loading\n addEvent(iframeEle, 'load', () => {\n el.className = `${prefixCls}-wrap ${this.elCls || ''}`;\n const maxTime = 3000;\n const duration = 1000;\n let timerCounter = 0;\n let w = defaultConfig.width;\n let h = defaultConfig.height;\n \/\/ 自適應寬高\n if (isAutoSize) {\n timer = setInterval(() => {\n ...\n \/\/\n this.setNodeSizeAndPostion(modalNode, iframeEle, scrollWidth, scrollHeight);\n }\n timerCounter += duration;\n if (timerCounter >= maxTime) {\n clearInterval(timer);\n }\n }, duration);\n }\n });\n }\n \n \/\/ 設置iframe寬高\n setNodeSizeAndPostion(container, iframe, width, height) {\n container.style.cssText = `width: ${width}px; height: ${height}px;margin-left: -${width \/ 2}px;margin-top: -${height \/ 2}px;`;\n iframe.style.cssText = `width: ${width}px; height: ${height}px;`;\n }\n \/\/ 刪除DOM\n removeNode() {\n timer && clearInterval(timer);\n if (this.el) {\n document.body.removeChild(this.el);\n }\n }\n ...\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面我們完成了整個業務組件的加載過程,下面我們需要處理的就是業務邏輯組件如何與容器組件之間進行通信:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常我們可以這樣處理:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"\/\/ 獲取父頁面屬性\nconst params = window.parent.paramsName;\n\/\/ 調用父頁面方法\nwindow.parent.onSuccess && window.parent.onSuccess(data);\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但在實際的業務場景中,我們可能會面臨的問題是業務方的域名與 iframe 加載的組件地址域名不一致,這個時候我們就必須要解決組件的跨域通信問題了."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"跨域的通信問題"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以通過以下三種方式去解決:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"postMessage"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"postMessage"},{"type":"text","text":" (https:\/\/developer.mozilla.org\/zh-CN\/docs\/Web\/API\/Window\/postMessage) 可以跨文檔通信, 在 IE10 的支持性有問題,在 IE11 及以上可以完美解決跨域問題。筆者需要支持 IE9+,所以沒有采用 postMessage"}]}]}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/95\/9564a05d4b97698d33eb32f27492a200.webp","alt":"Image","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"主域名修改"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"document.domain + iframe : 設置 document.domain 爲主域名,業務方與 iframe 主域名相同,實現父子同域通信。這種實現的前提是兩個域的主域名必須一致"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Nginx 代理"}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Nginx 配置:iframe 頁面的路徑配置爲通用路徑,反向代理依賴接口,實現全域名可訪問。將業務邏輯組件整合到一個或多個項目中使用,組件打包和發佈邏輯可單獨定製,適合大量跨框架組件"}]}]}]},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"\/\/ 靜態頁面地址\nlocation ~ ^\/your-project\/ {\n  root \/opt\/front\/your-project\/;\n  try_files $uri $uri\/ \/index.html = 404;\n  access_log off;\n}\n\/\/ 反向代理\nlocation ~ ^\/api\/service\/(.*)$ {\n   proxy_pass http:\/\/your-ip;\n   proxy_set_header        X-Real-IP $remote_addr;\n   proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;\n   proxy_set_header        Host $http_host;\n   proxy_set_header        requestId $request_id;\n   proxy_http_version      1.1;\n   proxy_set_header        Connection \"\";\n   expires 30d;\n   access_log off;\n }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"需要注意的點"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意處理非白色背景的圓角部分,容易出現毛邊。處理方法是 iframe 容器不設置背景色,由 iframe 裏面設置圓角"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"版本控制:小版本保證向前兼容,大版本可通過動態獲取 iframe 地址來實現版本控制"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"多框架背景下的組件重複開發問題,根源還是多框架的歷史債務問題。更好的方式則是推動技術棧的統一,從根源上避免出現此種情況。此場景下更爲完善的解決方案則是微前端,我們也在向這個方向探索,本文提供的是一種基礎的解決多技術棧業務場景的思路,如果有更好的方案歡迎大家一起討論~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"頭圖:Unsplash"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作者:明明"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文:https:\/\/mp.weixin.qq.com\/s\/BehjH5xVXFWohQXFl3u-kQ"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文:如何開發跨框架的組件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"來源:政採雲前端團隊 - 微信公衆號 [ID:Zoo-Team]"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"轉載:著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。"}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章