css隔离方案

254 阅读2分钟

当前比较流行的微前端架构方案,其中要解决的问题之一是处理子模块之间的冲突,其中包括js冲突、css冲突等,本周着重总结一下css冲突解决方案。

因为css并没有作用域这个说法,只要规则匹配上就会被应用到元素上面,所以如果系统中涉及到N个子系统的协同开发时,就难以避免的会出现样式冲突、覆盖的情况。 查询资料后,发现有以下几个方法处理。

image.png

下面分别从上图的六个方法来讲述

  1. BEM 简单解释一下BEM,block__element--modifiedmoment,举例: .select__options--active、.dropdown--menu__item--active等,就是这种方式可以请清晰的看出这个类名的用途,可读性较好,缺点是比较依赖约定,难免会出现不符合约定的命名方式。在编写组件过程中经常用到。

  2. vue scope
    我们很经常编写的vue 组件,用的是这种方式隔离,我们看个例子。 编译之前:

<style lang="scss" scoped>
.sidebar-logo-container {
  position: relative;
  width: 100%;
  height: 50px;
  line-height: 50px;
  background: #2b2f3a;
  text-align: center;
  overflow: hidden;
}
</style>

编译后样式:

// dom节点
<div data-v-1039e2f0="" class="sidebar-logo-container"><img data-v-1039e2f0="" src="https://km.woa.com/gkm/api/img/cos-file-url?url=https%3A%2F%2Fkm-pro-1258638997.cos.ap-guangzhou.myqcloud.com%2Ffiles%2Fphotos%2Fpictures%2Fmobile_for_extranet_img%2Fimage-1251917893.cos.ap-guangzhou.myqcloud.com%2Fpvp-admin%2F1619431192004.jpg&is_redirect=1" alt="logo"></div>
// 对应样式
.sidebar-logo-container[data-v-1039e2f0] {
    position: relative;
    width: 100%;
    height: 50px;
    line-height: 50px;
    background: #2b2f3a;
    text-align: center;
    overflow: hidden;
}

以上是vue style在编译过程中对每个样式加上了独一无二的属性选择,通过对属性选择参数的控制,保证样式的全局唯一性。

  1. 通过shadow dom来完成。

看下mdn上的定义

image.png

然后我们测试一下:

const shadowDomContainer = document.getElementById('shadow-dom');
const shadow = shadowDomContainer.attachShadow({ mode: 'open' });
shadow.innerHTML = `
  <span class="container">这个一个shadow dom</span>
  <style type="text/css">
  .container {
    background: red;
  }
  </style>
`

看下效果:

image.png

  1. css module

通过webpack工具结合css-loader来实现css样式隔离。看下例子

.title {
  color: green;
  composes: blue from './color.css';
  composes: red;
  border-bottom: 1px solid #ccc;
  padding-bottom: 20px;
}

编译后

// dom节点
<h2 class="src-styles-index__title--3XpLk src-styles-color__blue--3WlK8 src-styles-index__red--1ihPk">CSS Modules</h2>
// css样式
.src-styles-index__title--3XpLk {
    color: green;
    border-bottom: 1px solid #ccc;
    padding-bottom: 20px;
}

可以看出,这种方式就是把BEM通过工具化方式实现。开启方式如下:

{
        test: /\.css$/,
        use: [
          { loader: "style-loader" },
          {
            loader: "css-loader",
            options: {
              !!#ff0000 modules: true,!!
              // camelCase: true,
              localIdentName: '[path][name]__[local]--[hash:base64:5]'
            }
          }
        ]
      },
  1. 预处理器,通过sass、less、stylus等预处理器,开发者可以编写自己的应用规则,实现dom层级嵌套结构,这种方式的样式源码更加一目了然。这里就不多介绍。
  2. css in js. 这种方式,我理解是基于封装的思想,把相同职责的模块代码都放置在一起,在组件封装和代码可读性上有较大提升。跟传统的关注点分离的思想有所不同。 这里看个例子,有些过react、react-native代码的同学,对这个应该比较熟悉。
const style = {
  'color': 'green',
  'fontSize': '24px'
};
const handle = () => alert('hellow'); 
ReactDOM.render(
  <h1 style={style} onclick={handle}>
     Hello, world!
  </h1>,
  document.getElementById('example')
);

这样子就把组件所需要的信息都封装到内部,可以更加方便的复用组件内容。