背景

目前,社区比拟关注微前端利用款式的”封装性“,网上曾经有诸多文章介绍了微前端的款式隔离计划。然而,如果微利用只有“封装性”而没有“灵活性”,那么利用场景就会大打折扣:就如同一个不承受内部参数的函数,永远只做本人的事件,不承受调用者的管控和配置。

在这种不足灵活性的微利用架构下,宿主利用很难管控整个利用的款式主题。比方以下需要很难实现:

  • 主题色从橙色升级成浅蓝色
  • 甲方要求可能在线切换主题色
  • 微利用在多个宿主中被应用。在某些宿主中,体现为橙色;在另一些宿主中,则体现为蓝色。

根本原因在于,其中的微利用曾经将款式硬编码(并且实现了款式隔离),而无奈通过宿主的配置来扭转,不足信息传递与共享的渠道。

过来的实现形式须要消耗极大的老本:比方,对于每个微利用,为每种主题构建出一份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的值,体现为蓝色。