背景
目前,社区比拟关注微前端利用款式的”封装性“,网上曾经有诸多文章介绍了微前端的款式隔离计划。然而,如果微利用只有“封装性”而没有“灵活性”,那么利用场景就会大打折扣:就如同一个不承受内部参数的函数,永远只做本人的事件,不承受调用者的管控和配置。
在这种不足灵活性的微利用架构下,宿主利用很难管控整个利用的款式主题。比方以下需要很难实现:
- 主题色从橙色升级成浅蓝色
- 甲方要求可能在线切换主题色
- 微利用在多个宿主中被应用。在某些宿主中,体现为橙色;在另一些宿主中,则体现为蓝色。
根本原因在于,其中的微利用曾经将款式硬编码(并且实现了款式隔离),而无奈通过宿主的配置来扭转,不足信息传递与共享的渠道。
过来的实现形式须要消耗极大的老本:比方,对于每个微利用,为每种主题构建出一份 css,而后依据宿主以后的主题状态,用 js 来切换每个微利用的 css。并且,任何一个波及到主题的批改,都要求你批改、从新构建、公布所有微利用,极其繁琐。
计划简介
本文分享一个计划,通过 cssVar 来实现微利用的主题管控,能够做到:
- 微利用有本人的默认主题。开箱即用,无需任何配置就能应用。
- 宿主有掌控权。当宿主想要进行款式管控的时候,能够准确、按需地管制每个微利用的主题变量,笼罩微利用本人的默认主题,保障整个利用是和谐一致的。
在这套计划中,微利用的款式就如同一个有可选参数的函数:
function (cssVar1 = defaultVar1, cssVar2 = defaultVar2) {// render style with cssVar1, cssVar2...}
在封装了外部款式的同时,又对外提供了能够配置的 API。更灵便地满足各种场景的需要。
上面用一个简略的例子,来介绍这套计划的实现思路。
开箱即用的默认主题
这是微利用挂载的款式:
/* 微利用 css */
.widget-k7na5-root {--button-bg: orange;}
.widget-k7na5-btn {background-color: var(--button-bg);
}
其中,
widget-k7na5
是微利用的类名前缀,实现款式隔离。你能够用网上的各种形式来实现款式隔离(比方 css module、css-in-js),它们都能够与本计划组合应用。
它的 DOM 构造如下:
<div classname="widget-k7na5-root">
<button classname="widget-k7na5-btn">button</button>
</div>
因而,在无外界影响的状况下,这个微利用会展现默认的橙色主题。开箱即用,无需配置。
它的关键点在于,在微利用根元素上,定义一份默认的款式变量;而后在微利用外部,援用款式变量来实现款式,而不是将具体值硬编码在款式中。
宿主能够笼罩微利用的款式变量
如果宿主想要将整个站点的主题降级为蓝色,那么微利用就不应该持续体现为橙色。因而 宿主须要有笼罩微利用默认主题的能力。
如何做到呢?
首先,微利用要提供一个 API,容许宿主配置微利用根元素的类名。
/* 宿主 js */
// 宿主加载微利用的时候,能够定制微利用根元素的类名
function App() {return <LoadWidget id="widget-instance-list" className="theme-blue" />}
微利用有如下 DOM 构造:
<div classname="widget-k7na5-root theme-blue">
<button classname="widget-k7na5-btn">button</button>
</div>
微利用加载的款式与后面一样,无需扭转:
/* 微利用 css */
.widget-k7na5-root {--button-bg: orange;}
.widget-k7na5-btn {background-color: var(--button-bg);
}
因而,微利用的款式始终只用筹备一份,无需用 js 来做动静切换,实现与保护都很简略。
宿主 的款式蕴含如下主题变量定义:
/* 宿主 css */
/* 选择器权重高于 widget 本人的变量定义 */
.theme-blue.theme-blue {--button-bg: blue;}
实现!当初微利用中的 button 会展现为蓝色主题!
这里的关键点在于,宿主笼罩了微利用根元素上的 cssVar 款式变量定义。
宿主只须要用这种形式,给每个微利用都加上 .theme-blue
的类名,就能够让整个站点都对立变成蓝色主题。在这个主题降级过程中,各个微利用不须要做任何改变、公布。
留神到,宿主始终没有侵入微利用外部的实现,维持了微利用的封装性。宿主应用的仅仅是以下 API:
- 定制根元素类名的 API。
- 微利用在根元素上定义的 cssVar 变量名。它们就如同函数的具名参数,也是一种 API。
只有保障这两个 API 可能维持稳固,宿主与微利用就能各自独立迭代。封装性与灵活性兼得。
高级例子:准确控制能力
这种计划简略灵便,宿主甚至能够准确地管制每一个微利用要展示哪种主题。
宿主 js 如下,给每个微利用别离传入主题类名:
/* 宿主 js */
// 宿主加载微利用的时候,能够定制微利用根元素的类名
function App() {
return (
<>
<LoadWidget id="widget-instance-list" className="theme-blue" />
<LoadWidget id="widget-instance-list" className="theme-green" />
</>
);
}
宿主 的款式蕴含每个主题类名的变量定义:
/* 宿主 css */
/* 选择器权重高于 widget 本人的变量定义 */
.theme-blue.theme-blue {--button-bg: blue;}
.theme-green.theme-green {--button-bg: green;}
这样的话,前者会展现为蓝色主题,而后者会展现为绿色主题。两者互相不烦扰,并且齐全受宿主管制。
微利用默认继承宿主的款式变量
在下面的计划中,微利用默认展现本人的主题。宿主如果要笼罩微利用的主题,须要给每个微利用传入类名。即默认隔离 的计划。
cssVar 也能够实现 默认继承 的计划:微利用主动继承宿主的款式变量,宿主无需给微利用传入额定配置。
实际上,cssVar 本来就是默认继承的。
宿主 js:
/* 宿主 js */
function App() {return <LoadWidget id="widget-instance-list" />}
留神到,宿主不再须要定制微利用根元素的类名
宿主 css:
/* 宿主 css */
html {--button-bg: blue;}
留神到,宿主间接在全局范畴定义主题变量。
微利用的 DOM 构造与之前雷同:
<div classname="widget-k7na5-root">
<button classname="widget-k7na5-btn">button</button>
</div>
微利用挂载的款式:
/* 微利用 css */
.widget-k7na5-root {}
.widget-k7na5-btn {background-color: var(--button-bg, orange);
}
为了防止每次应用 var(–button-bg)的时候都要带上默认值,让代码变得反复、难以编写和批改,你还能够借助预处理器的变量性能(比方
$button-bg: var(--button-bg, orange);
)。
这样,微利用会主动从宿主环境读取到 --button-bg
的值,体现为蓝色。