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

微前端如何做樣式隔離?

開發 前端
樣式隔離實現起來不復雜,各種方案都有其局限性。目前比較穩定的方案還是使用 css Modules 之類的工具配合團隊之間協商好樣式前綴,從樣式命名 & 優先級上解決問題。

問題示例

className 命名重復導致的樣式沖突

我們先創建一個問題,驗證樣式沖突的存在:

在主應用和子應用上分別使用 div 元素插入一段標題,兩個 div 元素使用相同的 class 名 title,分別在 class 中設置文字顏色,主應用 color 值為 yellow,子應用為 red。

由于子應用的樣式晚于主應用加載,所以主應用的樣式會被覆蓋。

圖片

以上問題在同時加載多個子應用時也會存在:各個應用之間也可能存在同名的 className 或者給相同條件的選擇器添加了樣式, 那么最終只有優先級最高的樣式才會生效。要確保應用之間的樣式不會互相影響,就需要對應用間的樣式進行隔離。

html、body 標簽的樣式沖突

html 、 body 標簽, 在各個應用中都是唯一的元素,其樣式必然會對主應用的樣式產生影響。

圖片

解決方案

為了以上樣式沖突問題,通常有以下兩種思路:

  • 通過樣式命名 & 樣式優先級解決
  • 通過宿主環境隔離來達到樣式隔離

樣式命名 & 樣式優先級

假設各個應用之間的樣式 className 都是全局唯一的, 那么不同 className 下的樣式就一定不會發生沖突。

再加上樣式優先級來配合解決,就能解決標簽選擇器的樣式沖突:

  • 例如在原 className 、標簽選擇器前面再添加一個 selector
  • 標簽選擇器 + 屬性選擇器

子應用改造

這里需要處理的樣式也分為以下兩種:

UI 組件庫等引入的全局樣式

默認情況下,UI 組件庫的 prefixCls 都是相同的,不過它們提供了 ConfigProvider 可以用來修改 UI 組件庫全局樣式的 prefixCls:全局化配置 ConfigProvider - Ant Design[1] 全局配置 ConfigProvider | ArcoDesign[2]

自定義樣式

通過 BEM、CSS Modules、 CSS in JS 等手段來獲得與其他應用不同的選擇器名,來規避樣式沖突。

圖片

或者直接使用 postcss 的插件,在編譯階段給所有樣式添加 prefix selector。

https://github.com/RadValentin/postcss-prefix-selector

主應用在運行時統一轉換樣式

如果不想或者無法干涉子應用的打包配置時,我們也可以通過主應用在運行時給所有樣式規則添加 prefix selector,來提升樣式優先級。

比如 A 應用的類選擇器 .title,在轉換后變成 #garfish_app_id_xxx .title,#garfish_app_id_xxx 是子應用最外層元素的 id,故保證該應用下的樣式優先級變高,并讓其只作用在當前應用下。

圖片

當然, 僅使用以上手段并不能解決子應用 html、 body 標簽給主應用帶來的樣式影響,細心的你可能已經發現,garfish 會為每個子應用創建一個假的 html 與 body 元素,然后對應子元素的 html 、body 樣式都會應用到這個假的 html、 body元素上。

圖片

Garfish

在 garfish 中是以插件來支持運行時轉換樣式的:

import { GarfishCssScope } from '@garfish/css-scope';
Garfish.run({
plugins: [
GarfishCssScope({
fixBodyGetter: true,
excludes: ['appName'],
}),
],
})

具體的源碼實現在這里:

https://github.com/modern-js-dev/garfish/tree/1f83e8fb35fd2ac12785fc7410015c3cd23c3bd2/packages/css-scope

優點

支持大部分樣式隔離需求,能夠同時處理 UI 組件庫的全局樣式與自定義樣式,比較省心。

缺點

運行時處理樣式,會有一定性能損耗

如果其他子應用或者主應用中使用了 !important ...

宿主環境隔離

Shadow DOM

附加并隱藏在常規 DOM 下的節點叫做 Shadow DOM —— 它以 Shadow root 節點為起始根節點,在這個根節點的下方,可以是任意元素,就和普通的 DOM 元素一樣,它可以通過方法添加子節點、設置屬性,以及為節點添加自己的樣式,隱藏的 DOM 樣式和其余 DOM 是完全隔離的,類似于 iframe 的樣式隔離效果。

圖片

如何創建

可以使用 shadowHostElement.attachShadow() 方法來將一個 shadow root 附加到調用方法的元素上。它接受一個配置對象作為參數,該對象有一個 mode 屬性,值可以是 open 或者 closed:

let shadowRoot = shadowHostElement.attachShadow({mode: 'open'});
let shadowRoot = shadowHostElement.attachShadow({mode: 'closed'});

open 表示可以通過頁面內的 JavaScript 來獲取 Shadow DOM,例如使用 Element.shadowRoot 屬性:

let shadowRoot = shadowHostElement.shadowRoot;

如果將 mode 設置為 closed,那么elementRef.shadowRoot 將會返回 null。

瀏覽器中的某些內置元素就是如此,例如<video>,就包含了不可訪問的 Shadow DOM。

為 shadow DOM 添加樣式

我們可以通過創建<style> 元素為 Shadow DOM 添加樣式,也可以通過創建<link> 元素引用外部樣式表。

// 使用 style 元素為 shadow DOM 添加樣式
var style = document.createElement('style');
style.textContent = `
.title {
color: blue;
}
`;
shadow.appendChild(style);

// 使用 link 標簽為 Shadow DOM 添加樣式
const linkElem = document.createElement('link');
linkElem.setAttribute('rel', 'stylesheet');
linkElem.setAttribute('href', 'style.css');
shadow.appendChild(linkElem);

Shadow DOM 的事件模型

當一個事件從 Shadow DOM 中冒泡出來時,事件的 target 屬性就會調整為 shadow DOM 的宿主。

有些事件甚至不會冒泡到 Shadow DOM 之外。

以下這些事件是會冒泡出去的:

  • Focus Events:blur, focus, focusin, focusout
  • Mouse Events:click, dblclick, mousedown, mouseenter, mousemove, etc.
  • Wheel Events:wheel
  • Input Events:beforeinput, input
  • Keyboard Events:keydown, keyup
  • Composition Events:compositionstart, compositionupdate, compositionend
  • DragEvent:dragstart, drag, dragend, drop, etc.

如果 shadow dom 的模式為 open,調用event.composedPath()就會返回一個數組——包含事件冒泡經過的所有元素。

Garfish

在 garfish 中使用也非常簡單,只需要一行配置即可開啟:

https://github.com/modern-js-dev/garfish/blob/main/packages/utils/src/container.ts#L37

Garfish.run({
sandbox: {
strictIsolation: true,
},
});

圖片

優點

完全隔離 CSS 樣式。

缺點

  • 在使用一些 antd Select 組件的時候(很多情況下都是將 open 后的元素默認添加到了 document.body 上 )這個時候它就跳過了陰影邊界,逃逸到主應用里面,導致樣式丟失,這時候就需要去子應用中手動修正該彈出元素的掛載節點(例如使用 antd select 的 getPopupContainer)。

圖片

圖片

  • 會與 react v17 之前的事件代理機制產生沖突[3]

React v16 會各種事件處理函數代理到 document ,但是根據 Shadow DOM 的事件模型,從 Shadow DOM 中冒泡出來的事件 target 都會被調整成 shadow host, 導致 react v16 無法通過 event.target 找到對應的元素并觸發事件。

garfish 源碼中的這部分就是在做 retarget:

https://github.com/modern-js-dev/garfish/blob/main/packages/utils/src/container.ts#L42

https://github.com/modern-js-dev/garfish/blob/1f83e8fb35fd2ac12785fc7410015c3cd23c3bd2/packages/utils/src/dispatchEvents.ts#L74

React v17 不再將事件代理到 document 上,而是將事件代理到了 root Element 上,從而規避了這個問題( root element 也還在 shadow tree 中 )。

圖片

關于 react v17 事件代理的更多內容可以看看下面的文章:

https://reactjs.org/blog/2020/08/10/react-v17-rc.html#changes-to-event-delegation

  • 兼容性[4]還行,需要考慮
  • Iconfont fontface
  • ..

總結

樣式隔離實現起來不復雜,各種方案都有其局限性。目前比較穩定的方案還是使用 css Modules 之類的工具配合團隊之間協商好樣式前綴,從樣式命名 & 優先級上解決問題。

(主應用的樣式依然可以影響到子應用,優先級也可能會被 !important 等操作被破壞,不過大多數場景下足夠了)

但從長期來看,通過 Shadow DOM 完全隔離樣式還是很香的,也希望 Shadow DOM 與其他框架、組件庫結合使用的暗坑早日被填補完畢。

責任編輯:武曉燕 來源: ELab團隊
相關推薦

2024-05-28 09:05:31

2019-01-17 10:58:37

2015-03-18 11:44:28

微信營銷

2024-03-13 13:07:36

移動端樣式適配

2022-08-03 09:11:31

React性能優化

2022-08-29 08:08:58

SQLOracleCPU

2024-01-04 08:49:03

Vuescope限制

2015-07-30 11:21:16

代碼審查

2012-03-12 16:42:54

測試

2022-02-17 13:18:58

定價模型營銷AHP

2023-12-29 10:04:47

數據分析

2012-05-07 08:49:57

Clojure

2021-04-25 09:19:22

騰訊Code Reviewleader

2023-11-06 07:33:01

推薦策略數據分析

2013-07-24 10:01:24

產品設計產品經理新手做產品

2018-05-15 15:33:07

Leader前端團隊

2014-04-15 13:16:00

Code Review

2025-02-21 08:20:33

2018-07-18 14:39:29

2013-11-29 10:15:48

國產虛擬化
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 在线成人一区 | 视频一区二区三区中文字幕 | 欧美日韩国产在线观看 | 一区精品视频 | 欧美第一页 | 国产精品免费av | 免费网站国产 | 亚洲综合五月天婷婷 | 欧美高清一区 | 日韩在线免费视频 | 国精日本亚洲欧州国产中文久久 | 日韩一区二区在线免费观看 | 黑人巨大精品欧美一区二区免费 | 区一区二区三在线观看 | 久久av一区二区三区 | 毛片a级 | 成人在线免费电影 | 羞羞视频在线观免费观看 | 国产精品一区二区精品 | 亚洲欧美另类在线观看 | 国产精品久久久久久久久久久久久久 | 成人精品在线 | 国产一级片一区二区三区 | 国产美女久久 | 亚洲欧美日韩在线不卡 | 国产精品视频久久 | 男女免费视频网站 | 久久99蜜桃综合影院免费观看 | 久久久久久91 | 日本在线免费看最新的电影 | 亚洲精品九九 | 黄色在线观看网址 | 男女视频在线观看网站 | 日本久久一区二区三区 | 九九视频在线观看视频6 | 在线视频一区二区 | 丝袜 亚洲 另类 欧美 综合 | 7799精品视频天天看 | 日韩一级电影免费观看 | 欧美一级免费看 | 中文字幕日韩一区 |