qiankun proxySand 沙箱

qiankun 內(nèi)為微應(yīng)用實(shí)現(xiàn)了沙箱機(jī)制妻导, 以實(shí)現(xiàn)js隔離的目的, 沙箱的重點(diǎn)在于初始化時(shí)對(duì)全局對(duì)象的copy 及代理

使用

const sand = new ProxySand(name)
sand.active() // 啟動(dòng)
sand.inacitve() // 關(guān)閉

屬性

  • name 沙箱名
  • type 沙箱類型
    • Proxy proxy沙箱
    • Snapshot
    • LegacyProxy 舊沙箱實(shí)現(xiàn)
  • sandboxRunning 沙箱是否運(yùn)行中
  • proxy 全局對(duì)象的proxy副本, 沙箱實(shí)體
  • active 啟動(dòng)沙箱
  • inactive 關(guān)閉沙箱

實(shí)現(xiàn)

沙箱的實(shí)現(xiàn)過(guò)程都在 constructor 實(shí)例的創(chuàng)建中

設(shè)置初始值

 this.name = name;
 this.type = SandBoxType.Proxy;
 const { updatedValueSet } = this;

 const self = this;
 const rawWindow = window;

復(fù)制window

const { fakeWindow, propertiesWithGetter } = createFakeWindow(rawWindow);

createFakeWindow

  • getOwnPropertyDescriptor 返回對(duì)象指定的屬性配置
  • defineProperty 給對(duì)象添加一個(gè)屬性并指定該屬性的配置
  • hasOwnProperty 返回一個(gè)布爾值 乾翔,表示某個(gè)對(duì)象是否含有指定的屬性浊仆,而且此屬性非原型鏈繼承的
  • freeze 凍結(jié)對(duì)象:其他代碼不能刪除或更改任何屬性
const rawObjectDefineProperty = Object.defineProperty;

function createFakeWindow(global: Window) {

  const propertiesWithGetter = new Map<PropertyKey, boolean>();
  const fakeWindow = {} as FakeWindow;


  Object.getOwnPropertyNames(global) // 獲取全局對(duì)象屬性
    .filter(p => { // 篩選不可修改屬性
      const descriptor = Object.getOwnPropertyDescriptor(global, p);
      return !descriptor?.configurable;
    })
    .forEach(p => {

      // 獲取屬性配置
      const descriptor = Object.getOwnPropertyDescriptor(global, p);
        
      if (descriptor) {
          
        // 是否有g(shù)et屬性
        const hasGetter = Object.prototype.hasOwnProperty.call(descriptor, 'get');

        // 修改部分特殊屬性為可修改
        if (
          p === 'top' ||
          p === 'parent' ||
          p === 'self' ||
          p === 'window' ||
          (process.env.NODE_ENV === 'test' && (p === 'mockTop' || p === 'mockSafariTop'))
        ) {
            
          // 屬性可delete
          descriptor.configurable = true;   
     
          if (!hasGetter) {
            // 屬性可修改
            descriptor.writable = true;
          }
        }

        // 記錄可訪問(wèn)屬性
        if (hasGetter) propertiesWithGetter.set(p, true);

        // 將屬性綁定到新對(duì)象上,并凍結(jié)
        rawObjectDefineProperty(fakeWindow, p, Object.freeze(descriptor));
      }

    });

  return {
    fakeWindow,
    propertiesWithGetter,
  };
}

這里只將可修改屬性綁定到fakeWindow上, 對(duì)于不可修改屬性將直接使用window屬性

創(chuàng)建window代理

// 目標(biāo)標(biāo)識(shí)映射   
const descriptorTargetMap = new Map<PropertyKey, SymbolTarget>();

    // fakeWindow 只綁定了部分window上的不可修改屬性
    const hasOwnProperty = (key: PropertyKey) => fakeWindow.hasOwnProperty(key) || rawWindow.hasOwnProperty(key);
    
   
    const proxy = new Proxy(fakeWindow, {
        
       // 設(shè)置值
      set(target: FakeWindow, p: PropertyKey, value: any): boolean {

        // 沙箱是否運(yùn)行中
        if (self.sandboxRunning) {
          
          target[p] = value;
          
          // set 緩存數(shù)據(jù)
          updatedValueSet.add(p);
          
          // 如果屬性為 System, __cjsWrapper 模塊加載器,將屬性綁定到window上
          interceptSystemJsProps(p, value);
          return true;
        }
        
        if (process.env.NODE_ENV === 'development') {
          console.warn(`[qiankun] Set window.${p.toString()} while sandbox destroyed or inactive in ${name}!`);
        }

        // 在 strict-mode 下呜叫,Proxy 的 handler.set 返回 false 會(huì)拋出 TypeError帐要,在沙箱卸載的情況下應(yīng)該忽略錯(cuò)誤
        return true;
      },
    
          

      // 獲取值
      get(target: FakeWindow, p: PropertyKey): any {
        
        
        if (p === Symbol.unscopables) return unscopables;

        // widow, self 返回全局代理
        if (p === 'window' || p === 'self') {
          return proxy;
        }

        if (
          p === 'top' ||
          p === 'parent' ||
          (process.env.NODE_ENV === 'test' && (p === 'mockTop' || p === 'mockSafariTop'))
        ) {
          // if your master app in an iframe context, allow these props escape the sandbox
          // iframe 嵌套
          if (rawWindow === rawWindow.parent) {
            return proxy;
          }
          return (rawWindow as any)[p];
        }

        // 返回包裝后的 hasOwnProperty
        if (p === 'hasOwnProperty') {
          return hasOwnProperty;
        }
        
        // 當(dāng)訪問(wèn)作為文檔時(shí),標(biāo)記要文檔的符號(hào)落塑。createElement可以知道是由哪個(gè)沙箱調(diào)用的動(dòng)態(tài)追加補(bǔ)丁程序
        if (p === 'document') {
          
          // 為document掛載代理對(duì)象
          document[attachDocProxySymbol] = proxy;
            
          // 將刪除函數(shù)放入微隊(duì)列中
          nextTick(() => delete document[attachDocProxySymbol]);
          return document;
        }

        // 判斷屬性
        const value = propertiesWithGetter.has(p) ? (rawWindow as any)[p] : (target as any)[p] || (rawWindow as any)[p];
        return getTargetValue(rawWindow, value);
      },

      
      has(target: FakeWindow, p: string | number | symbol): boolean {
        return p in unscopables || p in target || p in rawWindow;
      },
        
      // 獲取屬性描述
      getOwnPropertyDescriptor(target: FakeWindow, p: string | number | symbol): PropertyDescriptor | undefined {
  
        if (target.hasOwnProperty(p)) {
          const descriptor = Object.getOwnPropertyDescriptor(target, p);
          // 緩存屬性配置, 設(shè)置屬性配置時(shí)使用
          descriptorTargetMap.set(p, 'target');
          return descriptor;
        }

        if (rawWindow.hasOwnProperty(p)) {
          const descriptor = Object.getOwnPropertyDescriptor(rawWindow, p);
         // 緩存屬性配置, 設(shè)置屬性配置時(shí)使用
          descriptorTargetMap.set(p, 'rawWindow');
          return descriptor;
        }

        return undefined;
      },

      // 返回屬性列表
      ownKeys(target: FakeWindow): PropertyKey[] {
        return uniq(Reflect.ownKeys(rawWindow).concat(Reflect.ownKeys(target)));
      },
        
      // 設(shè)置屬性配置
      defineProperty(target: Window, p: PropertyKey, attributes: PropertyDescriptor): boolean {
        const from = descriptorTargetMap.get(p);
          
        switch (from) {
          case 'rawWindow':
            return Reflect.defineProperty(rawWindow, p, attributes);
          default:
            return Reflect.defineProperty(target, p, attributes);
        }
      },
        
      // 屬性刪除
      deleteProperty(target: FakeWindow, p: string | number | symbol): boolean {
        if (target.hasOwnProperty(p)) {
          // @ts-ignore
          delete target[p];
          updatedValueSet.delete(p);

          return true;
        }

        return true;
      },
    });
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末纽疟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子憾赁,更是在濱河造成了極大的恐慌污朽,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件龙考,死亡現(xiàn)場(chǎng)離奇詭異蟆肆,居然都是意外死亡矾睦,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)炎功,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)枚冗,“玉大人,你說(shuō)我怎么就攤上這事蛇损×尬拢” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵淤齐,是天一觀的道長(zhǎng)股囊。 經(jīng)常有香客問(wèn)我,道長(zhǎng)更啄,這世上最難降的妖魔是什么稚疹? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮祭务,結(jié)果婚禮上内狗,老公的妹妹穿的比我還像新娘。我一直安慰自己待牵,他們只是感情好其屏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著缨该,像睡著了一般偎行。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贰拿,一...
    開(kāi)封第一講書(shū)人閱讀 51,521評(píng)論 1 304
  • 那天蛤袒,我揣著相機(jī)與錄音,去河邊找鬼膨更。 笑死妙真,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的荚守。 我是一名探鬼主播珍德,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼矗漾!你這毒婦竟也來(lái)了锈候?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤敞贡,失蹤者是張志新(化名)和其女友劉穎泵琳,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡获列,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年谷市,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片击孩。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡迫悠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出溯壶,到底是詐尸還是另有隱情及皂,我是刑警寧澤甫男,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布且改,位于F島的核電站,受9級(jí)特大地震影響板驳,放射性物質(zhì)發(fā)生泄漏又跛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一若治、第九天 我趴在偏房一處隱蔽的房頂上張望慨蓝。 院中可真熱鬧,春花似錦端幼、人聲如沸礼烈。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)此熬。三九已至,卻和暖如春滑进,著一層夾襖步出監(jiān)牢的瞬間犀忱,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工扶关, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留阴汇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓节槐,卻偏偏與公主長(zhǎng)得像搀庶,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子铜异,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355