首頁 > 軟體

微前端之 js隔離 樣式隔離 元素隔離問題詳解

2022-08-23 18:02:00

WebComponent 介紹

微前端框架中,js隔離、樣式隔離、元素隔離是必須解決的三個問題,下面我們就來分別說說這三個問題是什麼?怎麼解決?

涉及的核心點是 Proxy,WebComponent,shadowDOM

WebComponent 不在這三個問題中,但是我們做個簡單介紹

瀏覽器預設的標籤有 div,a,p等等,瀏覽器是會自動識別,並且有預設的事件和樣式。

瀏覽器相對於提供了WebComponent,我們可以自定義Html標籤,注意規定自定義標籤需要包含橫線,如<user-card>,父類別都是HTMLElement

attachShadow 是大多數標籤都支援的,比如 div,p,selection,但是a,ul,li等不支援,如果執行attachShadow,那麼元件的html結構會掛在shadowRoot下,否則直接掛著元件下。

用法

class UserCard extends HTMLElement {
  static get observedAttributes() {return ['name', 'url']; }
  constructor() {
    super();
    // 可以創Shadom,通過this.shadowRoot獲取
    // this.attachShadow({ mode: 'open' })
  }
  connectedCallback (){
      console.log('勾點,元素append到ducument觸發')
  }
  disconnectedCallback (){
      console.log('勾點,元素從document刪除觸發')
  }
  // 只有observedAttributes 中監聽的屬性name,url變化會觸發下面回撥
  attributeChangedCallback (attr, oldVal, newVal){
      console.log('勾點,元素屬性改變時觸發')
  }
}
window.customElements.define('user-card', UserCard);

更新細節檢視 web自定義元素

js隔離

問題

主要是很對全域性變數window

  • 情況1:都對全域性變數賦值

應用A,寫 window.r = 1;

然後有應用B,又寫 window.r = 2,這就亂套了

  • 情況2:都設定事件

應用A,window.addEventListener('click',()=>console.log('A'));

應用B,window.addEventListener('click',()=>console.log('B'));

這就亂套了

解決

對應全域性物件,各個應該要獨立,怎麼實現呢,有兩種方法

方法一用 Proxy 代理

es2015 Reflect屬於一個靜態類或者設定屬性等用法

const rawWindow = window
const proxyWindow = new Proxy({},{
    get: (target, key): unknown => {
        // 原 target 上有就返回,否則返回 rawWindow 屬性
        return Reflect.has(target, key) ? Reflect.get(target, key) : Reflect.get(rawWindow, key)
    },
    set: (target, key, value): boolean => {
        if(!Object.prototype.hasOwnProperty.call(target, key) && Object.prototype.hasOwnProperty.call(rawWindow, key)){
            const descriptor = Object.getOwnPropertyDescriptor(rawWindow, key)
            const { configurable, enumerable, writable, set } = descriptor!
            // set value because it can be set
            rawDefineProperty(target, key, {
              value,
              configurable,
              enumerable,
              writable: writable ?? !!set,
            })
        } else {
            Reflect.set(target, key, value)
        }
    }
})

將 window.r = 1 和 window.addEventListener('click',()=>console.log('A')) 包括到自執行函數裡面

A和B互不干擾

;(function(window){
    window.r = 1
    window.addEventListener('click',()=>console.log('A'))
})(proxyWindowA)
;(function(window){
    window.r = 2
    window.addEventListener('click',()=>console.log('B'))
})(proxyWindowB)

方法二 用快照

快照隔離有個前提條件是,當前還有一個應用顯示,不能出現多個應用並存顯示在介面上,應用A,B切換時,比如當前應用是A,現在要切入到應用B

  • 暫存起來應用A的全域性變數和事件
  • 恢復全域性變數和事件到應用A之前
  • 檢查之前是否保持有應用B的全域性變數和事件,如果有,則載入

樣式隔離

問題

同理,各個應用之前可能相互設定標籤樣式,會相互影響,或者影響全域性樣式,比如應用A給body設定樣式,應用B也給body設定樣式

方法一 樣式增加不同字首

每個應用通過字首獨立區分開,京東micro-app預設是採用的這個策略,唯一注意的一個小點是,基座樣式會影響子應用的樣式,所以需要注意基座中不要寫太多樣式

方法二 ShadawDom

大多數Html標籤都有 attachShadow() 方法給指定的元素掛載一個 Shadow DOM。引數是open或closed

ShadawDom 樣式絕對隔離,不用加字首,如下圖

用法

//open 是外界可以存取到Element.shadowRoot再存取到內部元素,closed就是完全不能存取內部元素
var shadowroot = element.attachShadow('open|closed')  

元素隔離

元素隔離是 基座應用和子應用都有一個元素<div id='root'></div>,此時子應用通過document.querySelector('#root'),因為js隔離已經做了代理,此時document.querySelector只是子應用本身了

以上就是微前端之 js隔離 樣式隔離 元素隔離問題詳解的詳細內容,更多關於微前端js 樣式 元素隔離的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com