image.png

一、BEM

1.1 简介

BEM是一种css命名方法论,意思是块(Block)、元素(Element)、修饰符(Modifier)的简写
这种命名方法让CSS便于统一团队开发规范和方便维护
以 .blockelement—modifier或者说block-nameelement-name—modifier-name形式命名,命名有含义,也就是模块名 + 元素名 + 修饰器名
如.dropdown-menu__item—active
社区里面对BEM命名的褒贬不一,但是对其的思想基本上还是认同的,所以可以用它的思想,不一定要用它的命名方式

1.2 应用场景

BEM思想通常用于组件库,业务代码中结合less等预处理器

1.3 优缺点分析

优点:

  • 人为严格遵守BEM规范,可以解决无作用域样式污染问题
  • 可读性好,一目了然是那个dom节点,对于无用css删除,删除了相应dom节点后,对应的css也能比较放心的删除,不会影响到其他元素样式

    缺点

  • 人为依赖约定,容易出错

  • 命名相对复杂

    二、CSS modules

    2.1 简介

    css-modules 将 css 代码模块化,可以避免本模块样式被污染,并且可以很方便的复用 css 代码

    所有的类名和动画名称默认都有各自的作用域的CSS文件。

CSS Modules既不是官方标准,也不是浏览器的特性,而是在构建步骤(例如使用Webpack,记住css-loader)中对CSS类名和选择器限定作用域的一种方式(类似于命名空间)

依赖webpack css-loader,配置如下,现在webpack已经默认开启CSS modules功能了

  1. {
  2. test: /.css$/,
  3. loader: "style-loader!css-loader?modules"
  4. }

我们先看一个示例:
将CSS文件style.css引入为style对象后,通过style.title的方式使用title class:

  1. import style from './style.css';
  2. export default () => {
  3. return (
  4. <p className={style.title}>
  5. I am KaSong.
  6. </p>
  7. );
  8. };

对应style.css:

  1. .title {
  2. color: red;
  3. }

打包工具会将style.title编译为带哈希的字符串

  1. <h1 class="_3zyde4l1yATCOkgn-DBWEL">
  2. Hello World
  3. </h1>

同时style.css也会编译:

  1. ._3zyde4l1yATCOkgn-DBWEL {
  2. color: red;
  3. }

这样,就产生了独一无二的class,解决了CSS模块化的问题
使用了 CSS Modules 后,就相当于给每个 class 名外加加了一个 :local,以此来实现样式的局部化,如果你想切换到全局模式,使用对应的 :global。
:local 与 :global 的区别是 CSS Modules 只会对 :local 块的 class 样式做 localIdentName 规则处理,:global 的样式编译后不变

  1. .title {
  2. color: red;
  3. }
  4. :global(.title) {
  5. color: green;
  6. }

可以看到,依旧使用CSS,但使用JS来管理样式依赖, 最大化地结合现有 CSS 生态和 JS 模块化能力,发布时依旧编译出单独的 JS 和 CSS

2.2 优缺点分析

优点

  • 能100%解决css无作用域样式污染问题
  • 学习成本低:API简洁到几乎零学习成本

    缺点

  • 需要频繁引入 CSS 文件 (有类似 react-css-modules 之类的模块解决)

  • 没有变量,需要结合预处理器
  • 编译后代码可读性差,hash 值不方便 debug

    三、CSS in JS

    3.1 简介

    核心思想是把CSS直接写到各自组件中,也就是说用JS去写CSS,而不是单独的样式文件里
    这跟传统的前端开发思维不一样,传统的原则是关注点分离,如常说的不写行内样式、不写行内脚本
    CSS-in-JS不是一种很新的技术,可是它在国内普及度好像并不是很高,它当初的出现是因为一些component-based的Web框架(例如 React,Vue 和 Angular)的逐渐流行,使得开发者也想将组件的CSS样式也一块封装到组件中去以解决原生CSS写法的一系列问题
    使用 React 改写如下 ```javascript const style = { ‘color’: ‘red’, ‘fontSize’: ‘46px’ };

const clickHandler = () => alert(‘hi’);

ReactDOM.render(

Hello, world!

, document.getElementById(‘example’) );

  1. 上面代码在一个文件里面,封装了**结构、样式和逻辑**,完全违背了"关注点分离"的原则<br />但是,这有利于组件的隔离。每个组件包含了所有需要用到的代码,不依赖外部,组件之间没有耦合,很方便复用。
  2. <a name="ZmKw1"></a>
  3. ### 3.2 CSS in JS 与"CSS 预处理器"(比如 Less 和 [Sass](https://link.juejin.cn/?target=https%3A%2F%2Fwww.ruanyifeng.com%2Fblog%2F2012%2F06%2Fsass.html),包括 PostCSS)有什么区别
  4. CSS in JS 使用 JavaScript 的语法,**是 JavaScript 脚本的一部分**,不用从头学习一套专用的 API,也不会多一道编译步骤,但是通常会在运行时动态生成CSS,造成一定运行时开销
  5. <a name="HNQoF"></a>
  6. ### 3.3 优缺点分析
  7. <a name="BxE1c"></a>
  8. #### 优点
  9. - **没有无作用域问题样式污染问题**通过唯一CSS选择器或者行内样式解决
  10. - **没有无用的CSS样式堆积问题**CSS-in-JS会把样式和组件绑定在一起,当这个组件要被删除掉的时候,直接把这些代码删除掉就好了,不用担心删掉的样式代码会对项目的其他组件样式产生影响。而且由于CSS是写在JavaScript里面的,我们还可以利用JS显式的变量定义,模块引用等语言特性来追踪样式的使用情况,这大大方便了我们对样式代码的更改或者重构
  11. - **更好的基于状态的样式定义**CSS-in-JS会直接将CSS样式写在JS文件里面,所以样式复用以及逻辑判断都十分方便
  12. <a name="AkNwk"></a>
  13. #### 缺点:
  14. - **一定的学习成本**
  15. - **代码可读性差**
  16. - **运行时消耗**
  17. - 不能结合成熟的CSS预处理器(或后处理器)Sass/Less/PostCSS,:hover :active 伪类处理起来复杂
  18. <a name="bmiUH"></a>
  19. ## 四、预处理器
  20. <a name="PsOlo"></a>
  21. ### 4.1 简介
  22. **CSS 预处理器**是一个能让你通过预处理器自己独有的语法的程序<br />市面上有很多CSS预处理器可供选择,且绝大多数CSS预处理器**会增加一些原生CSS不具备的特性**,例如
  23. - 代码混合
  24. - 嵌套选择器
  25. - 继承选择器
  26. 这些特性让CSS的结构更加具有可读性且易于维护<br />常见的预处理器:
  27. - [Sass](https://link.juejin.cn?target=https%3A%2F%2Fsass-lang.com%2F)
  28. - [LESS](https://link.juejin.cn?target=https%3A%2F%2Flesscss.org%2F)
  29. - [Stylus](https://link.juejin.cn?target=http%3A%2F%2Fstylus-lang.com%2F)
  30. - [PostCSS](https://link.juejin.cn?target=http%3A%2F%2Fpostcss.org%2F)
  31. <a name="pd3vK"></a>
  32. ### 4.2 优缺点分析
  33. <a name="ivYgw"></a>
  34. #### 优点:
  35. - 利用嵌套,人为严格遵守嵌套首类名不一致,可以解决无作用域样式污染问题
  36. - 可读性好,一目了然是那个dom节点,对于无用css删除,删除了相应dom节点后,对应的css也能比较放心的删除,不会影响到其他元素样式
  37. <a name="uL9hw"></a>
  38. #### 缺点
  39. - 需要编译工具处理
  40. <a name="PmbXG"></a>
  41. ## 五、Shadow DOM
  42. <a name="GGkuI"></a>
  43. ### 5.1 简介
  44. ![](https://cdn.nlark.com/yuque/0/2022/webp/1125546/1650943788028-f41c9a6d-ff03-46f5-8aaa-d8fbd5a4c679.webp#clientId=u1880087a-ee69-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u97d7f45c&margin=%5Bobject%20Object%5D&originHeight=939&originWidth=1304&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=ud79c2772-e413-4575-862d-57963aa1271&title=)
  45. <a name="X08Pl"></a>
  46. ### 5.2 优缺点分析
  47. <a name="vHrIG"></a>
  48. #### 优点
  49. - 浏览器原生支持
  50. - 严格意义上的样式隔离,如iframe一样
  51. <a name="bvffg"></a>
  52. #### 缺点
  53. - 浏览器兼容问题
  54. - 只对一定范围内的dom结构起作用,上面微前端场景已经说明
  55. > 普通业务开发我们还是用框架、如VueReactShadow DOM适用于特殊场景,如微前端
  56. <a name="ggOL6"></a>
  57. ## 六、vue scoped
  58. <style> 标签有 scoped 属性时,它的 CSS 只作用于当前组件中的元素<br />使用 scoped 后,**父组件的样式将不会渗透到子组件中**<br />不过一个子组件的根节点会同时受其父组件的 scoped CSS 和子组件的 scoped CSS 的影响。这样设计是为了让父组件可以从布局的角度出发,调整其子组件根元素的样式,父租价利用深度作用选择器影响子组件样式<br />可以使用 >>> 操作符:
  59. ```javascript
  60. <style scoped>
  61. .a >>> .b { /* ... */ }
  62. </style>

有些像 Sass 之类的预处理器无法正确解析 >>>。这种情况下你可以使用 /deep/ 或 ::v-deep 操作符取而代之——两者都是 >>> 的别名,同样可以正常工作

总结

社区通常的样式隔离方案,以下两种

  • BEM+预处理器
  • CSS Moduls + 预处理器