css modules & styled-component

为什么要用css modules?

  1. 代码只改动一处;
  2. 只应用在特定的组件,不影响别的地方;

默认情况下,所有类名和动画名都在本地范围内的css文件。可以通过webpack或者Browserify来改变类名和选择器名,以保证作用域。

这可以很好的解决css中的全局作用域问题。

// AccountInfo.less
.container {
    padding: 20px 0 0 40px;

    :global {
        .ant-col {
            font-weight: 700;
            text-align: left;

            &-5 > label {
                margin-left: 7px;
            }
        }

        .ant-form-item {
            margin-bottom: 14px;

            &-required::before,
            &-required::after,
            &-label > label::after {
                display: none;
            }
        }
    }
}
// 调用
import styles from './AccountInfo.less';

编译后:

composes 关键字

// colors.css

.display {
  color: red;
  font-size: 30px;
  line-height: 35px;
}

// element.css

.element {
    composes: display from "./colors.css"
}

composes关键字指明.normal包含所有来自.common的样式,类似于Sass里的@extends。但是Sass通过重写CSS选择器来实现,而CSS模块通过选择哪个类输出到JavaScript进行了改变。

不再需要BEM

BEM 是 Block、Element、Modifier 的缩写,利用不同的区块,功能以及样式来给元素命名。这三个部分使用 与 连接(这里用两个而不是一个是为了留下用于块的命名)。命名约定如下:

.block {}

.block__element {}

.block--modifier {}

.block__element--modifier {}

BEM 的原则很简单:一个 Block 代表一个对象(一个人、一个登录表单、一个菜单);一个 Element 是一个块中作为特定功能的组件(一个帮助按钮、一个登录按钮、一个菜单项);一个 Modifier 是我们如何表示块或元素的不同变化(一个女人、一个带有隐藏标签的迷你登录框、 footer 中一个不同的菜单)。

BEM优点:

通过这种命名方式,HTML 层级结构一目了然,组件功能清晰明朗,而且不必使用过多的层级选择器,在一定程度上能够提高 CSS 的渲染速度。

  1. 代码结构更加清晰;
  2. 规范化
  3. 利于团队协作
BEM缺点:
  1. 命名过长问。

在 DOM 层级过深的情况下,会导致 CSS Class 冗长,难以阅读,所以使用 BEM 命名的层级一般不超过4层。

  1. 无法根治样式污染问题

在某些情况下,比如命名稍不注意导致重名,就可能会出现样式污染问题,使用第三方库的情况下也有可能会产生命名冲突,导致样式污染。

在构建CSS Modules时,有两个好处:

  1. 易解析,类似type.display这样的代码,对于开发者来说就像BEM-y的.font-size__serif--large。当BEM选择器变长时,可能更容易被理解。

  2. 本地作用域。我们在一个模块中运用.big中的font-size属性,也可以在另一个类中,去同时增加padding和font-size。因为本地作用域的不用,他们不会存在冲突,甚至可以在一个module中引入2个样式表。本地作用域的原因,构建工具会给class加上不同的前缀作区分。这可以很好的解决css样式中的特殊性问题。

styled-components

简易上手的几个Demo

const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;

/* Adapt the colors based on primary prop */
const Button = styled.button`
  background: ${props => props.primary ? "palevioletred" : "white"};
  color: ${props => props.primary ? "white" : "palevioletred"};
  
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

render(
  
)
/* Extending Styles */
const Button = styled.button`
  color: palevioletred;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

/* A new component based on Button, but with some override styles */
const TomatoButton = styled(Button)`
  color: tomato;
  border-color: tomato;
`;

vs 「css in js」的好处

相比于React的 css in js, styled-components 有一个好处,就是对伪元素的定义。

const Thing = styled.button`
  color: blue;

  ::before {
    content: '';
  }

  :hover {
    color: red;
  }
`

css in js的写法,伪元素只能通过onMouseOver这样的js方法去控制,增加代码量。

Theming in styled-components

styled-components提供Context这样的机制,有一套主题的上下文组件可供使用ThemeProvider

import {ThemeProvider} from 'styled-components';

const provider = (
    
        
            
        
    
);

ReactDOM.render(
    provider,
    document.getElementById('root')
);

styled-components还提供React hook,可以结合useContext 来获取theme。

import { useContext } from 'react';
import { ThemeContext } from 'styled-components';

const MyComponent = () => {
  const themeContext = useContext(ThemeContext);

  console.log('Current theme: ', themeContext);
  // ...
}

Issues with specificity

// MyComponent.js
const MyComponent = styled.div`background-color: green;`;

// my-component.css
.red-bg {
  background-color: red;
}

// For some reason this component still has a green background,
// even though you're trying to override it with the "red-bg" class!

上述例子中,styled-components可以取得在全局class之上的优先权。因为styled-components是在运行时加载到标签的尾部。因此它的样式优先级要高于类选择器。

Tips: 如果想要强行覆盖组件内的样式,可以通过加大选择器的权重来达到覆盖样式的效果。

/* my-component.css */
.red-bg.red-bg {
  background-color: red;
}

/* 或者 */
&& {
    background-color: red;
}

Tagged Template Literals(标签模板字符串)

标签模板字符串,是ES6的新特性。他们让你可以自定义字符串差值规则,这也是我们可以使用styled-components的原因。

// These are equivalent:
fn`some string here`
fn(['some string here'])


const aVar = 'good'

// These are equivalent:
fn`this is a ${aVar} day`
fn(['this is a ', ' day'], aVar)

本质上来说,调用函数 styled.button() 和使用 styled.button``几乎是一回事!但是当你传入参数时就会看到不同之处了。

我们先创建一个简单的函数用于探索:

const logArgs = (...args) => console.log(...args)

const favoriteFood = 'pizza'

logArgs(`I like ${favoriteFood}.`)
// -> I like pizza.

logArgs`I like ${favoriteFood}.`
// -> ["I like ", "."] "pizza"


可以看到,我们不再仅仅是得到了一个内容为 "I like pizza" 的字符串。

传入参数的第一位仍然是数组,不过现在有了 2 个元素:

  • 位于插值左侧的 I like,作为数组第一个元素;
  • 位于插值的右侧的 .,是数组第二个元素。

插值内容 favoriteFoor 成为了第二个传入参数。

如果我们插入不止一个变量,

const favoriteFood = 'pizza'
const favoriteDrink = 'obi'

logArgs`I like ${favoriteFood} and ${favoriteDrink}.`
// -> ["I like ", " and ", "."] "pizza" "obi"
Why is this useful?

对于 React 组件,你希望使用 props 值调整他们的样式。比如我们通过传入一个 primary 的 prop 值,让

你可能感兴趣的:(css modules & styled-component)