日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

微前端代碼隔離方案,手把手實現一個 JS 沙箱隔離!

來源: 責編: 時間:2024-07-16 16:59:48 176觀看
導讀今天我們一起來探究一下前端 js 沙箱的核心實現邏輯,我們將從以下幾個方面來展開討論:準備調試環境,探究沙箱需要解決的問題。創建沙箱環境。通過 with 語句改變沙箱變量作用域鏈。通過 proxy 攔截 with 上下文的get,set

今天我們一起來探究一下前端 js 沙箱的核心實現邏輯,我們將從以下幾個方面來展開討論:Y6t28資訊網——每日最新資訊28at.com

  1. 準備調試環境,探究沙箱需要解決的問題。
  2. 創建沙箱環境。
  3. 通過 with 語句改變沙箱變量作用域鏈。
  4. 通過 proxy 攔截 with 上下文的get,set操作。

這幾個方面一步一步實現一個簡易的js沙箱。Y6t28資訊網——每日最新資訊28at.com

準備調試環境,探究沙箱需要解決的問題:

js沙箱大量應用在微前端框架執行子應用代碼的場景中。我們這里采用最簡單的方式來模擬一下這個場景。Y6t28資訊網——每日最新資訊28at.com

我們準備一個js文件,這個js文件的內容就模擬微前端應用基座應用的執行環境,在里面通過 var 定義一個全局變量:Y6t28資訊網——每日最新資訊28at.com

圖片圖片Y6t28資訊網——每日最新資訊28at.com

我們準備兩段字符串,字符串的內容就是js代碼,我們假設這兩段代碼就分別代表了兩個子應用的代碼:Y6t28資訊網——每日最新資訊28at.com

圖片圖片Y6t28資訊網——每日最新資訊28at.com

假如現在微前端框架需要執行子應用1的代碼,那么就會 fetch 子應用1的靜態資源服務器,獲取到類似于 subCode1 這樣的一個代碼字符串。執行子應用2也是同理。那么現在第一個問題來了,怎樣自動執行字符串內部的js代碼?在js中有兩種比較常見的方式:Y6t28資訊網——每日最新資訊28at.com

  1. Function:
  2. eval

Function 是js提供的一個內置構造函數,基于它,可以通過字符串,創建出一個js函數,因此我們可以這樣嘗試:Y6t28資訊網——每日最新資訊28at.com

圖片圖片Y6t28資訊網——每日最新資訊28at.com

因為 Function(...)本身就是一個表達式,所以我們可以直接進行調用:Y6t28資訊網——每日最新資訊28at.com

圖片圖片Y6t28資訊網——每日最新資訊28at.com

我們查看一下控制臺:Y6t28資訊網——每日最新資訊28at.com

圖片圖片Y6t28資訊網——每日最新資訊28at.com

可以看到子應用1的代碼就被成功執行了,我們嘗試子應用2的代碼也是同理。Y6t28資訊網——每日最新資訊28at.com

eval是js內置的一個函數,這個函數接收一個字符串,eval函數會將這個字符串作為標準的js代碼進行執行:Y6t28資訊網——每日最新資訊28at.com

圖片圖片Y6t28資訊網——每日最新資訊28at.com

我們查看輸出:Y6t28資訊網——每日最新資訊28at.com

圖片圖片Y6t28資訊網——每日最新資訊28at.com

可以看到子應用代碼同樣被正常執行了。 這樣很方便呀,實際上微前端框架內部也是使用類似的方式來執行子應用的js代碼的,但是這樣會有什么問題呢?Y6t28資訊網——每日最新資訊28at.com

  1. 目前子應用中的變量定義會污染全局環境:

目前我們在子應用中定義的變量在全局環境中可以直接訪問,所以我們需要調整一下 subCode1 以及 subCode2 兩個子應用的測試代碼:Y6t28資訊網——每日最新資訊28at.com

圖片圖片Y6t28資訊網——每日最新資訊28at.com

我們將兩端子應用的測試代碼包裹到了一個立即執行函數中,這樣子應用內部定義的變量就不會污染全局了。大部分構建工具構建的產物也是這種方式。Y6t28資訊網——每日最新資訊28at.com

  1. 在子應用內部可以赤裸裸的訪問 window 對象,并且可以直接對它們進行操作:

圖片圖片Y6t28資訊網——每日最新資訊28at.com

圖片圖片Y6t28資訊網——每日最新資訊28at.com

我們可以直接在子應用2中通過訪問 window 對象來改變 window 對象的內容。如何攔截子應用直接污染瀏覽器宿主環境的 window 對象是實現js沙箱最核心的問題。Y6t28資訊網——每日最新資訊28at.com

創建沙箱環境

我們先編寫一個沙箱構造器函數:Y6t28資訊網——每日最新資訊28at.com

var a = 1const sandbox = () => {  return (subCode1) => {       }}

我們編寫了一個高階函數,這個高階函數返回了一個可以接收并且執行子應用代碼的函數。我們期望如果需要執行子應用的結果代碼,只需要這樣進行調用:Y6t28資訊網——每日最新資訊28at.com

const box = sandbox()box(子應用1代碼)box(子應用2代碼)box(子應用3代碼)

接下來就是要在高階函數中去編寫執行子應用的代碼了,我們沿著沙箱的需求來思考解決方案。 我們的需求就是屏蔽瀏覽器window對象,為了實現這個需求,我們可以在返回的高階函數中編寫如下的邏輯:Y6t28資訊網——每日最新資訊28at.com

const boxCtx = {}return (subCode1) => {  const boxFn = `(function (window) {    ${code}  })(boxCtx)`  // 通過eval函數執行子應用代碼  eval(boxFn)}

實際上就是我們在父函數中創建了一個沙箱的上下文環境,在高階函數中,將子應用代碼放到一個立即執行函數中去進行執行,立即執行函數的參數就是一個window變量,這樣,當我們在子應用代碼內部訪問 window 的時候,因為作用域鏈的原理,就只會訪問到我們在沙箱環境中設置的 window 參數,而無法訪問到瀏覽器全局的 window 對象了。我們可以測試一下:Y6t28資訊網——每日最新資訊28at.com

sandbox()('(function() { window.testVal1 = "testVal1"; window.a = 2; })()')console.log(' window.testVal1',  window.testVal1)

圖片圖片Y6t28資訊網——每日最新資訊28at.com

這樣做了之后我們就已經可以攔截子應用中對于 window 對象屬性的 set 操作了。 但是如果我們在子應用中嘗試直接通過變量通過 a 來訪問剛剛設置的值的時候,卻是這樣的結果:Y6t28資訊網——每日最新資訊28at.com

sandbox('(function() { window.a = 2; console.log("ssss", a); })()')

圖片圖片Y6t28資訊網——每日最新資訊28at.com

依然訪問的是全局變量a,原因其實很簡單,因為我們目前構建的沙箱函數內部只有一個 window 變量,并沒有 a 變量。所以js在執行這里的時候只能沿著作用域鏈訪問到全局作用域中的a變量了。要解決這個問題,我們必須確保,我們設置的 window 對象上的所有的屬性全部掛載到沙箱的作用域中。Y6t28資訊網——每日最新資訊28at.com

通過 with語句改變沙箱變量作用域鏈:

with 語句的能力可以這樣理解:在js運行時將指定代碼段的執行上下文設置為指定的對象,從而調整該代碼端的作用域鏈。 使用with,我們可以這樣改動并且測試沙箱代碼:Y6t28資訊網——每日最新資訊28at.com

const boxCtx = { b: "b" }return (subCode1) => {  const boxFn = `(function boxFn (window) {    with(window) { ${code} }  })(boxCtx)`  // 通過eval函數執行子應用代碼  eval(boxFn)}
sandbox('(function sub1() { window.a = 2; console.log("b", b) })()')

圖片圖片Y6t28資訊網——每日最新資訊28at.com

此時 with 語句使得沙箱部分的作用域鏈變成了類似于這樣的結構:Y6t28資訊網——每日最新資訊28at.com

sub1(子應用函數上下文) ---> window(with語句設置的代碼上下文) ---> boxFn 函數執行上下文 ---> 全局執行上下文

因此當我們直接在子應用內部訪問 a 變量的時候:Y6t28資訊網——每日最新資訊28at.com

圖片圖片Y6t28資訊網——每日最新資訊28at.com

就可以正常訪問子應用內部設置的全局變量了。Y6t28資訊網——每日最新資訊28at.com

但是依然有問題沒有解決,比如我們嘗試執行以下的函數:Y6t28資訊網——每日最新資訊28at.com

sandbox('(function() { window.a = 2; window.console.log("ssss", a); })()')

圖片圖片Y6t28資訊網——每日最新資訊28at.com

原因其實很簡單,因為此時的沙箱的window對象并沒有 console 屬性,要解決這個問題,我們可以嘗試擴展以下沙箱的上下文的內容,一個很暴力的解法就是:Y6t28資訊網——每日最新資訊28at.com

const boxCtx = {    console  }

我們可能會想到一個更加簡單的擴展沙箱上下文的方法:Y6t28資訊網——每日最新資訊28at.com

const boxCtx = {    ...window  }

這樣做了之后我們再利用 window.console 來輸出 a 的值:Y6t28資訊網——每日最新資訊28at.com

圖片圖片Y6t28資訊網——每日最新資訊28at.com

為什么會是1呢?如果我們此時在控制臺中訪問一下window.a的值會看到一個更加詭異的現象:Y6t28資訊網——每日最新資訊28at.com

圖片圖片Y6t28資訊網——每日最新資訊28at.com

全局的window的a屬性被修改了。Y6t28資訊網——每日最新資訊28at.com

全局window的a屬性被修改的原因其實很簡單,因為瀏覽器宿主的 window 上嵌套了一個 window 屬性,并且這個屬性指向了全局window對象,因此window.window是一個無限嵌套的循環引用:Y6t28資訊網——每日最新資訊28at.com

圖片圖片Y6t28資訊網——每日最新資訊28at.com

正是因為這樣,所以當我們直接使用 ... 淺拷貝一個 window 對象的時候,實際上 嵌套的 window 屬性也就被拷貝過來了,因此此時沙箱上下文是一個類似于這樣的對象:Y6t28資訊網——每日最新資訊28at.com

{  ...,  window: {    ...,    window  }}

因此,當沙箱中查找 window 變量的時候是可以直接在 boxCtx 上查找到,而查找的結果就是全局 window 對象的淺拷貝。因此就導致了我們在沙箱內部對于 window 的所有操作實際上操作在了全局window對象上。因此直接拷貝 window 對象來擴展沙箱上下文的方式是不行的。我們得找其他更加簡單的方案。Y6t28資訊網——每日最新資訊28at.com

通過 proxy 攔截 with 上下文的get,set操作:

我們可以提出這樣的設想,我們在沙箱內部通過 window.xxx 進行 get 的時候,如果 xxx 在沙箱內部的 window 上存在,那么就直接使用該值,如果不存在,就前往 window 對象上去查找 xxx 的值,這樣是不是就可以解決這個問題了呢?而做到這一步的關鍵就是攔截沙箱上下文的 get 操作,因此首先我們將傳遞給 with 語句的上下文對象變成一個 Proxy 代理對象:Y6t28資訊網——每日最新資訊28at.com

const createSandboxCtxProxy = () => {  return new Proxy({}, {    get(target, key, receiver) {          },    set(target, key, value, receiver) {    }  })}const boxCtx = createSandboxCtxProxy()

緊接著可以這樣去攔截 get 和 set 操作:Y6t28資訊網——每日最新資訊28at.com

get(target, key, receiver) {      console.log('get', key)      //優先從代理對象上取值      if(Reflect.has(target,key)){         return Reflect.get(target,key);      }      //如果找不到,就直接從window對象上取值      const rawValue = Reflect.get(window,key);      //其他情況直接返回      return rawValue    },    set(target, key, value, receiver) {      return Reflect.set(target, key, value)    },

這樣操作之后我們再次來進行測試:Y6t28資訊網——每日最新資訊28at.com

sandbox('(function() { window.a = 2; window.b = "b"; })()')

圖片圖片Y6t28資訊網——每日最新資訊28at.com

這樣就達到了和改造之前一樣的效果了,緊接著我們這樣測試:Y6t28資訊網——每日最新資訊28at.com

sandbox('(function() { window.a = 2; window.b = "b"; window.console.log("a,b", a, b) })()')

圖片圖片Y6t28資訊網——每日最新資訊28at.com

可以看到 console 對象已經可以正常的訪問到了。那么是不是就大功告成了呢?我們再來試一下 window.alert 這類方法:Y6t28資訊網——每日最新資訊28at.com

sandbox('(function() { window.a = 2; window.b = "b"; window.alert(`a, b: ${a}, ${b}`) })()')

圖片圖片Y6t28資訊網——每日最新資訊28at.com

這個錯誤是因為瀏覽器 window 上的方法在調用的時候函數內部的 this 一定要指向瀏覽器 window 對象。比如我們使用一個更好理解的方式來復原這個錯誤:Y6t28資訊網——每日最新資訊28at.com

圖片圖片Y6t28資訊網——每日最新資訊28at.com

定位到了錯誤的原因,我們就很容易可以解決了,只需要調整 get 方法:Y6t28資訊網——每日最新資訊28at.com

get(target, key, receiver) {      console.log('get', key)    //優先從代理對象上取值    if(Reflect.has(target,key)){      return Reflect.get(target,key);    }    //如果找不到,就直接從window對象上取值    const rawValue = Reflect.get(window,key);    //如果兜底的是一個函數,需要綁定window對象,比如window.addEventListener    if(typeof rawValue === 'function'){      const valueStr = rawValue.toString();      if(!/^function/s+[A-Z]/.test(valueStr) && !/^class/s+/.test(valueStr)){        return rawValue.bind(window); // 所有 window 上非構造函數調用時候的 this 綁定window對象      }    }    //其他情況直接返回    return rawValue    },

再次測試:Y6t28資訊網——每日最新資訊28at.com

圖片圖片Y6t28資訊網——每日最新資訊28at.com

問題已經解決了。至此我們就一步步最簡化的實現了一個js沙箱。Y6t28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-101118-0.html微前端代碼隔離方案,手把手實現一個 JS 沙箱隔離!

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 協方差矩陣適應進化算法實現高效特征選擇

下一篇: 說說MQ延遲隊列實現原理?

標簽:
  • 熱門焦點
  • 服務存儲設計模式:Cache-Aside模式

    Cache-Aside模式一種常用的緩存方式,通常是把數據從主存儲加載到KV緩存中,加速后續的訪問。在存在重復度的場景,Cache-Aside可以提升服務性能,降低底層存儲的壓力,缺點是緩存和底
  • 從 Pulsar Client 的原理到它的監控面板

    背景前段時間業務團隊偶爾會碰到一些 Pulsar 使用的問題,比如消息阻塞不消費了、生產者消息發送緩慢等各種問題。雖然我們有個監控頁面可以根據 topic 維度查看他的發送狀態,
  • Java NIO內存映射文件:提高文件讀寫效率的優秀實踐!

    Java的NIO庫提供了內存映射文件的支持,它可以將文件映射到內存中,從而可以更快地讀取和寫入文件數據。本文將對Java內存映射文件進行詳細的介紹和演示。內存映射文件概述內存
  • 讓我們一起聊聊文件的操作

    文件【1】文件是什么?文件是保存數據的地方,是數據源的一種,比如大家經常使用的word文檔、txt文件、excel文件、jpg文件...都是文件。文件最主要的作用就是保存數據,它既可以保
  • 花7萬退貨退款無門:誰在縱容淘寶珠寶商家造假?

    來源:極點商業作者:楊銘在淘寶購買珠寶玉石后,因為保證金不夠賠付,店鋪關閉,退貨退款難、維權無門的比比皆是。“提供相關產品鑒定證書,支持全國復檢,可以30天無理由退換貨。&
  • 阿里瓴羊One推出背后,零售企業迎數字化新解

    作者:劉曠近年來隨著數字經濟的高速發展,各式各樣的SaaS應用服務更是層出不窮,但本質上SaaS大多局限于單一業務流層面,對用戶核心關切的增長問題等則沒有提供更好的解法。在Saa
  • 華為發布HarmonyOS 4:更好玩、更流暢、更安全

    在8月4日的華為開發者大會2023(HDC.Together)大會上,HarmonyOS 4正式發布。自2019年發布以來,HarmonyOS一直以用戶為中心,經歷四年多的發展HarmonyOS已
  • iQOO 11S評測:行業唯一的200W標準版旗艦

    【Techweb評測】去年底,iQOO推出了“電競旗艦”iQOO 11系列,作為一款性能強機,該機不僅全球首發2K 144Hz E6全感屏,搭載了第二代驍龍8平臺及144Hz電競
  • OPPO K11樣張首曝:千元機影像“卷”得真不錯!

    一直以來,OPPO K系列機型都保持著較為均衡的產品體驗,歷來都是2K價位的明星機型,去年推出的OPPO K10和OPPO K10 Pro兩款機型憑借各自的出色配置,堪稱有
Top 主站蜘蛛池模板: 鸡东县| 忻州市| 临海市| 辉南县| 九寨沟县| 盖州市| 泰州市| 乌鲁木齐市| 安溪县| 赣州市| 民和| 县级市| 巢湖市| 常熟市| 桃江县| 岐山县| 黄骅市| 什邡市| 靖边县| 额敏县| 东台市| 黔东| 额尔古纳市| 含山县| 涿州市| 双辽市| 磴口县| 仪征市| 昭苏县| 柳州市| 大冶市| 浦江县| 六盘水市| 武强县| 建始县| 故城县| 咸阳市| 三都| 洮南市| 滕州市| 安康市|