参考:
copy form
CSS之路:css->less->moudleCss->styleComponet


CSS 方案演进史 - 图1

OOCSS

面向对象的 CSS ,独立于应用逻辑书写,鼓励 htmlcss 分离和代码的复用

OOCSS遵循两个原则:

  • 独立的结构和样式
  • 独立的容器和内容

具体实现上可以是拆分为基类和修饰类,将尽可能多的公共属性抽象到基础类,然后将每个变体的样式归类为修饰符类。

  1. <div class="container">
  2. <div class="row">
  3. <div class="col-4">
  4. <button type="button" class="btn btn-success hidden">Success</button>
  5. </div>
  6. <div class="col-8">
  7. <button type="button" class="btn btn-primary">Primary</button>
  8. </div>
  9. </div>
  10. </div>

优点:
  • css 复用率高,扩展性好;
  • 具备良好的可读性和可维护性;
  • 代码量比较少,天然 Atomic CSS

缺点:
  • 多人协作模式下,class 选择器容易冲突,没有解决命名空间的问题
  • 基础类库开发周期时间偏长

SMACSS

通过结构化命名,将类名模组分组,达到可扩展的目的。与OOCSS相比,SMACSS 更像是一套模板指导 CSS 的书写。

  1. <div id="section">
  2. <div class="l-flex">
  3. <div class="l-col l-col-4">
  4. <button type="button" class="btn-success is-hidden">Success</button>
  5. </div>
  6. <div class="l-col l-col-8">
  7. <!-- 扩展btn样式,创建子模块 -->
  8. <button type="button" class="btn-primary">Primary</button>
  9. </div>
  10. </div>
  11. </div>

分类:
  • Base:重置元素的基本样式,保证元素在任意浏览器中,视觉样式具有一致性。实现如:reset.css、normalize.css;
  • Layout:布局规则将页面分块,将一个或多个模块组合在一起,命名以 l- 开头;
  • Module:模块是设计中可重用的模块化部分,也是 SMACSS 里最重要的一部分,不强制以 m- 开头;
  • State:状态规则是描述我们的模块或布局在特定状态下的外观的方式。比如 Tab 是否选中,命名一般以 is- 开头;
  • Theme:主题规则与状态规则相似,它们描述了模块或布局的外观;

优缺点:同 OOCSS

BEM

Block__Element—Modifier
使用 BlockElementModifier 描述页面结构

  • Block 即为区块,可以独立存在;
  • Element 可以理解为元素,它依附于 Block 存在,与 Block 使用 __连接;
  • Modifier 用于描述 BlockElement 的外观或状态,与他们使用 -- 连接;
  1. <form class="form form--theme-xmas form--simple">
  2. <input class="form__input" type="text" />
  3. <input class="form__submit form__submit--disabled" type="submit" />
  4. </form>

优点:
  • 制定了一套易与理解的CSS编码规范;
  • 可读性高,通过 class 的命名,可以得知元素的结构;
  • 很大程度解决了命名冲突的问题;

缺点:
  • 重新定义 BlockElement 的含义,容易造成混淆;
  • 需要与页面结构并行维护一套CSS 逻辑;
  • 命名过长,产出的 size 偏大;

Shadow DOM

Web components 标准的一部分,将封装的 Showdow DOM 附加到元素并控制其关联的功能。
Shadow DOM 中定义的 CSS 样式只会在 ShadowRoot 下生效,很好的实现了代码的隔离。

  1. // js
  2. let tmp1 = document.createElement('template');
  3. tmp1.innerHTML = `
  4. <style>
  5. .btn {
  6. display: inline-block;
  7. font-weight: 400;
  8. line-height: 1.5;
  9. text-align: center;
  10. text-decoration: none;
  11. vertical-align: middle;
  12. user-select: none;
  13. border: 1px solid transparent;
  14. padding: .375rem .75rem;
  15. font-size: 1rem;
  16. border-radius: .25rem;
  17. transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
  18. }
  19. .btn-primary {
  20. color: #fff;
  21. background-color: #0d6efd;
  22. border-color: #0d6efd;
  23. }
  24. </style>
  25. <button type="button" class="btn btn-primary">I'm in shadow dom!</button>
  26. <slot></slot>
  27. `;
  28. customElements.define('x-button', class extends HTMLElement {
  29. constructor() {
  30. super();
  31. let shadowRoot = this.attachShadow({ mode: 'open' });
  32. shadowRoot.appendChild(tmp1.content.cloneNode(true));
  33. }
  34. });
  35. // html
  36. <x-button></x-button>


CSS 方案演进史 - 图2

优点:
  • 面向未来的原生组件写法
  • 与页面的其他代码天然隔离
  • 可增强现有 HTML 标签,创建可重用的新标签(Custom Element)

缺点:
  • 对平台实现有强依赖,浏览器支持度一般
  • 目前只能使用 link 标签引入外部样式 (存在绘制闪烁的问题),其他形式的外部样式 (例如全局的CSS重置) 无法渗透进 shadowDOM 内部 (已有提案解决)

CSS-in-JS

React原生自带内联样式属于CSS-In-Js的一种,
同时还有其他几种方案

styled-componentsnts

借助 js 语言的灵活性,使用 js 样式化组件,在组件的运行时,将CSS 附加到 DOM 当中。

  1. import React from 'react';
  2. import styled, { css } from 'styled-components';
  3. const Button = styled.button`
  4. display: inline-block;
  5. font-weight: 400;
  6. line-height: 1.5;
  7. text-align: center;
  8. text-decoration: none;
  9. vertical-align: middle;
  10. user-select: none;
  11. border: 1px solid transparent;
  12. padding: .375rem .75rem;
  13. font-size: 1rem;
  14. border-radius: .25rem;
  15. transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
  16. ${props => props.primary && css`
  17. color: #fff;
  18. background-color: #0d6efd;
  19. border-color: #0d6efd;
  20. `}
  21. `
  22. // OR
  23. // A new component based on Button, but with some override styles
  24. const PrimaryButton = styled(Button)`
  25. color: #fff;
  26. background-color: #0d6efd;
  27. border-color: #0d6efd;
  28. `;
  29. export default ShowButton() {
  30. return (
  31. <div>
  32. <Button>Normal</Button>
  33. <Button primary>Primary</Button>
  34. <PrimaryButton>Primary</PrimaryButton>
  35. </div>
  36. )
  37. }

优点:
  • 很轻松的实现cssjs 变量共享,易于维护;
  • 代码中只存在 javaScript,具有跨平台优势;
  • 不需借助编译工具,即可提取 Critical CSS

缺点:

CSS modules

https://github.com/camsong/blog/issues/5
http://www.ruanyifeng.com/blog/2016/06/css_modules.html

利用 webpack 之类的工具,编译阶段将类名加上随机的 hash 值,以回避命名冲突的问题
比如在某个nextjs项目上,使用less+moudle,解决组件和页面类名冲突
image.png
:global 设置成全局class(不加hash值)
image.png

CSS Houdini

开放 css 底层 api,开发者可通过接口自行扩展 css

文章介绍了几种前端发展中出现的 css 方案,其实在解决的下面提及的几个问题:

  • 作用域(全局 -> 组件)
  • 模块化与代码复用(提高可维护性)
  • CSS Atomic 化(更小的代码尺寸)
  • 提取 Critical CSS(更快的首屏加载体验)
  • 跨平台(Write once,run every where)

方案可能会过时,但其中折射隐含的思想,会以新的形式流传进化,它值得每个前端开发人员深入学习与思考。总而言之,技术没有银弹,实际开发中亦需要不断权衡利弊,明确使用场景,选择合适的方法维护项目。

参考链接:

https://caniuse.com/
http://oocss.org/
http://smacss.com/
https://github.com/Polymer/polymer
https://www.cssinjsplayground.com/
https://styled-components.com/
https://github.com/webpack-contrib/css-loader#modules
https://github.com/WICG/webcomponents/blob/gh-pages/proposals/css-modules-v1-explainer.md
https://developer.mozilla.org/zh-CN/docs/Web/Houdini
https://www.smashingmagazine.com/2016/03/houdini-maybe-the-most-exciting-development-in-css-youve-never-heard-of/