成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何“取巧”實現一個微前端沙箱?

開發 架構
如今微前端已經成為前端領域比較火爆的話題,在技術方面,微前端有一個始終繞不過去的話題就是前端沙箱。本文將分享阿里云開放平臺微前端方案的沙箱實現原理,具體探討在微前端領域如何實現前端沙箱。

 如今微前端已經成為前端領域比較火爆的話題,在技術方面,微前端有一個始終繞不過去的話題就是前端沙箱。本文將分享阿里云開放平臺微前端方案的沙箱實現原理,具體探討在微前端領域如何實現前端沙箱。

[[326638]]

背景

應用沙箱可能是微前端技術體系里面最有意思的部分。一般來說沙箱是微前端技術體系中不是必須要做的事情,因為如果規范做的足夠好,是能夠避免掉一些變量沖突讀寫,CSS 樣式沖突的情況。但是如果你在一個足夠大的體系中,總不能僅僅通過規范來保證應用的可靠性,還是需要技術手段去治理運行時的一些沖突問題,這個也是沙箱方案成為微前端技術體系的一部分原因。

首先縱觀各類技術方案,有一個大前提決定了這個沙箱如何做:最終微應用是單實例 or 多實例存在宿主應用中。這個直接決定了這個沙箱的復雜度和技術方案。

  • 單實例:同一個時刻只有一個微應用實例存在,此刻瀏覽器所有瀏覽器資源都是這個應用獨占的,方案要解決的很大程度是應用切換的時候的清理和現場恢復。比較輕量,實現起來也簡單。
  • 多實例:資源不是應用獨占,就要解決資源共享的情況,比如路由,樣式,全局變量讀寫,DOM。可能需要考慮的情況比較多,實現較為復雜。

最開始我們的想法是:

從業務場景:我們可能存在的情況是當用戶操作一個產品 A 的同時和另一個產品 B 發生了關聯操作,需要喚醒應用 B 做操作。雖然從產品維度可以規避掉,比如先切到 B,然后切回 A,但是從某種程度上因為技術的原因,我們限制了產品交互的發揮。

從技術角度:解決了多實例當然單實例的場景也不在話下,并且單實例的方案某種程度上給編碼上帶來了一定復雜度,比如業務代碼需要自己做業務上下文的切換。

最近 qiankun 2 也轉變了思路,從單實例的支持到開始支持多實例,多多少少也側面說明了,多實例是一個值得投入和技術攻克的場景。

基于上面的考量,我們就開始了我們 Browser VM 沙箱的實現探索。總結起來可以用下圖表示:

 

JavaScript 沙箱實現

沙箱環境構造

要實現沙箱,我們需要隔離掉瀏覽器的原生對象,但是如何隔離,建立一個沙箱環境呢?Node 中 有 vm 模塊,來實現類似的能力,但是瀏覽器就不行了,但是我們可以利用閉包的能力、利用變量作用域去模擬一個沙箱環境,比如下面的代碼:

  1. function foo(window) { 
  2.   console.log(window.document); 
  3. foo({ 
  4.     document: {}; 
  5. }); 

比如這段代碼的輸出一定是 {},而不是原生瀏覽器的 document。

所以 ConsoleOS(面向阿里云管體系的微前端方案) 實現了一個 Wepback 的插件,在應用代碼構建的時候給子應用代碼加上一層 wrap 代碼,創建一個閉包,把需要隔離的瀏覽器原生對象變成從下面函數閉包中獲取,從而我們可以在應用加載的時候,傳入模擬的 window、document 之類的對象。

  1. // 打包代碼 
  2. __CONSOLE_OS_GLOBAL_HOOK__(id, function (require, module, exports, {window, document, location, history}) { 
  3.   /* 打包代碼 */ 
  4. }) 
  5. function __CONSOLE_OS_GLOBAL_HOOK__(id, entry) { 
  6.   entry(require, module, exports, {window, document, location, history}) 

當然也可以不靠工程化的手段來實現,也可以通過請求腳本,然后在運行時拼接這段代碼,然后eval 或者 new Function 來達到相同的目的。

原生對象模擬

沙箱隔離能力有了,剩下的問題就是如何實現這一堆瀏覽器的原生對象了。最開始的想法是我們根據 ECMA 的規范實現(現在仍然有類似的想法),但是發現成本太高。不過在我們各種實驗之后,發現了一個很“取巧”的做法,我們可以 new iframe 對象,把里面的原生瀏覽器對象通過contentWindow 取出來,因為這些對象天然隔離,就省去了自己實現的成本。

  1. const iframe = document.createElement( 'iframe' ); 

當然里面有很多的細節需要考量,比如:只有同域的 iframe 才能取出對應的的contentWindow。所以需要提供一個宿主應用空的同域 URL 來作為這個 iframe 初始加載的 URL。當然根據 HTML 的規范,這個 URL 用了 about:blank 一定保證同域,也不會發生資源加載,但是會發生和這個 iframe 中關聯的 history 不能被操作,這個時候路由的變換只能變成 hash 模式。

如下圖所示,我們取出對應的 iframe 中原生的對象之后,就會對特定需要隔離的對象生成對應的 Proxy, 然后對一些屬性獲取和屬性設置,做一些特定的設置,比如 window.document 需要返回特定的沙箱 document 而不是當前瀏覽器的 document。

  1. class Window { 
  2.     constructor(options, context, frame) { 
  3.     return new Proxy(frame.contentWindow, { 
  4.         set(target, name, value) { 
  5.         target[name] = value; 
  6.         return true
  7.       }, 
  8.        
  9.       get(target, name) { 
  10.         switch( name ) { 
  11.           case 'document'
  12.             return context.document; 
  13.           default
  14.         } 
  15.          
  16.         if( typeof target[ name ] === 'function' && /^[a-z]/.test( name ) ){ 
  17.           return target[ name ].bind && target[ name ].bind( target ); 
  18.         }else
  19.           return target[ name ]; 
  20.         } 
  21.       } 
  22.     }); 
  23.   } 

對于每一個對象的實現這里不講細節了,有興趣可以看看我們的開源之后的代碼 :https://github.com/aliyun/alibabacloud-console-os/tree/master/packages/browser-vm

但是為了文檔能夠被加載在同一個 DOM 樹上,對于 document,大部分的 DOM 操作的屬性和方法還是直接用的宿主瀏覽器中的 document 的屬性和方法。

由于子應用有自己的沙箱環境,之前所有獨占式的資源現在都變成了應用獨享(尤其是 location、history),所以子應用也能同時被加載。并且對于一些變量,我們還能在 proxy 中設置一些訪問權限的事情,從而限制子應用的能力,比如 Cookie, LocalStoage 讀寫。

當這個 iframe 被移除時,寫在 window 的變量和設置的一些 timeout 時間也會一并被移除(當然 DOM 事件需要沙箱記錄,然后在宿主中移除)。

總結一下,我們的沙箱可以做到如下的特性:

 

CSS 隔離

CSS 隔離方案相對來說比較常規,常見的有:

  • CSS Module
  • 添加 CSS 的 namespace
  • Dynamic StyleSheet
  • Shadow DOM

CSS Module or CSS Namespace

通過修改基礎組件樣式前綴來實現框架和微應用依賴基礎組件樣式的隔離性(依賴于工程上 CSS 的預處理器編譯和運行時基礎組件庫配置),同時避免全局樣式的書寫(依賴于約定或工程 lint 手段)。

Dynamic StyleSheet

隔離方式是通過 JS 運行時動態加載卸載微應用樣式表來避免樣式的沖突,局限性一是對于站點框架本身或其部件(header/menu/footer)與當前運行的微應用間仍存在樣式沖突的可能性,二是沒有辦法支持多個微應用同時運行顯示的情況。

Shadow DOM

優點是瀏覽器級別提供的樣式隔離能力,可以做到完全隔離。缺點在于,目前兼容性還是不太好,并且改造會涉及到舊應用的業務代碼的改造,對子應用侵入性比較高。

最終經過實踐,我們選擇的方式是 CSS Module + 添加 CSS 的 namespace。CSS module 保證的是應用業務樣式不沖突,Namespace 保證公共庫不沖突。我們實現了一個 postcss 插件,會在應用構建的時候給所有的樣式都加上應用前綴包括應用公共庫的 CSS(這樣方便做到同一個 組件庫新舊版本樣式的兼容)。如下圖所示:

  1. // 宿主 host app 
  2. .next-btn { 
  3.     color: #eee; 
  4. // 子應用 sub app 
  5. aliyun-slb .next-btn { 
  6.     color: #eee; 
  7. //宿主中生成的節點 
  8. <aliyun-slb> 
  9.     <!-- 子應用的節點 --> 
  10. </aliyun-slb> 

這樣實現的好處在于:

  • 每個應用都有 namespace,可以多實例共存。
  • 不依賴特定的 CSS 預處理器。
  • 對于同一個庫不同版本的 CSS(如 fusion1 和 fusion2),可以做到徹底隔離。
  • 鑒于上面 JS 沙箱的存在,對于一些彈窗類的組件,這個微應用獲取的 body 實際上是宿主生成的節點,所以彈窗會被添加到微應用的節點(也就是上面的 aliyun-slb)這個節點,樣式不會失效。

不過也會有一些問題,比如:

  • 嵌套應用組件樣式優先級的問題。由于 CSS module 的存在,一般只會發生在公共 CSS 樣式中,這個就是只能盡量避免嵌套。
  • fusion 不同版本庫公用字體的問題。目前的解決辦法:比較 hack,使用工程化的手段替換掉 next 字體的名字。

如何和其他體系結合

如果看完上面的文章,覺得這個沙箱方案不錯,但是又已經有自己的微前端體系了,想套用咋辦?

目前 ConsoleOS 的代碼已經在 Github 上開源:http://github.com/aliyun/alibabacloud-console-os,這里不妨可以嘗試試用一下。

JS 沙箱部分

如果看懂了上面關于原理的介紹可以看到其實沙箱實現包括兩個層面:

  • 原生瀏覽器對象的模擬(Browser-VM)
  • 如何構建一個閉包環境

Browser-VM 可以直接用起來,這部分完全是通用普適的。但是涉及到閉包構建的這部分,每個微前端體系不太一致,可能需要改造,比如:

  1. import { createContext, removeContext } from '@alicloud/console-os-browser-vm'
  2. const context = await createContext(); 
  3. const run = window.eval(` 
  4.   (() => function({window, history, locaiton, document}) { 
  5.     window.test = 1; 
  6.   })() 
  7. `) 
  8. run(context); 
  9. console.log(context.window.test); 
  10. console.log(window.test); 
  11. // 操作虛擬化瀏覽器對象 
  12. context.history.pushState(nullnull'/test'); 
  13. context.locaiton.hash = 'foo' 
  14. // 銷毀一個 context 
  15. await removeContext( context ); 

當然可以直接選擇沙箱提供好的 evalScripts 方法:

  1. import { evalScripts } from '@alicloud/console-os-browser-vm'
  2. const context = evalScripts('window.test = 1;'
  3. console.log(window.test === undefined) // true 

CSS 沙箱

如果用 Webpack 構建,可以直接配置如下:

  1. const postcssWrap = require('@alicloud/console-toolkit-plugin-os/lib/postcssWrap'
  2. // 下面是 webpack config 
  3.   test: /\.css$/, 
  4.   use: [ 
  5.     'style-loader'
  6.     { 
  7.       loader: 'postcss-loader'
  8.       options: { 
  9.         plugins: [ 
  10.           // 加入插件 
  11.           postcssWrap({ 
  12.             stackableRoot: '.prefix'
  13.             repeat: 1 
  14.             }) 
  15.         ], 
  16.       }, 
  17.     }, 
  18.     'css-loader'
  19.   ], 
  20.   exclude: /^node_modules$/, 

 

責任編輯:武曉燕 來源: 阿里技術
相關推薦

2024-07-16 11:26:35

微前端代碼JS

2021-01-26 10:33:45

前端開發技術

2022-01-17 11:41:50

前端Vite組件

2022-04-08 09:52:13

前端監控系統

2017-12-12 15:24:32

Web Server單線程實現

2020-04-02 09:31:49

微前端架構系統

2020-11-30 06:20:13

javascript

2022-06-07 10:13:22

前端沙箱對象

2023-02-26 01:37:57

goORM代碼

2023-03-01 09:39:40

調度系統

2020-08-17 08:20:16

iOSAOP框架

2016-08-04 14:08:57

前端javascripthtml

2019-03-22 08:25:47

沙箱網絡安全惡意軟件

2023-02-13 00:18:22

前端庫框架集合

2015-08-17 10:32:06

前端工程師優秀

2015-08-24 09:02:49

前端工程師

2011-12-26 16:39:43

局部函數

2022-03-14 10:02:03

散列表鏈表哈希表

2022-10-20 11:00:52

SQL解析器

2016-09-06 19:45:18

javascriptVue前端
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 男人的天堂在线视频 | 免费看欧美一级片 | 亚洲综合一区二区三区 | 亚洲激情综合网 | 超碰在线播 | 天天射天天操天天干 | 羞羞视频网站免费观看 | 免费看黄色视屏 | 国产乱码精品1区2区3区 | 国产成人精品久久二区二区91 | 久久精品国产一区二区 | 国产美女福利在线观看 | 久久精品免费观看 | 亚洲视频一区 | 亚洲男女视频在线观看 | 日韩免费一区二区 | 亚洲精品一区二区 | 国产高清视频一区二区 | 求个av网址| 在线看亚洲 | 欧美成人精品 | 国产精品久久久久久久免费观看 | 欧美一区二区三区国产 | 国产激情一区二区三区 | 国产精品久久久久久久免费观看 | 精品欧美黑人一区二区三区 | av资源在线看 | 日韩一区二区免费视频 | 国产一区二区自拍 | 欧美精选一区二区 | h视频在线免费观看 | 免费视频色 | 欧美日韩1区 | 日本超碰 | 日本精品视频一区二区三区四区 | 国产精品区一区二 | 久久国产亚洲 | 日韩和的一区二区 | 久久久人| 黄瓜av| 手机三级电影 |