美文网首页
css modules & styled-component

css modules & styled-component

作者: liuniansilence | 来源:发表于2019-11-06 17:07 被阅读0次

为什么要用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';

<div className={styles.container}></div>

编译后:

<div class="src-biz-AccountManage-AccountInfo-AccountInfo__container--1Fy_N"></div>

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(
  <div>
    <Button>Normal</Button>
    <Button primary>Primary</Button>
  </div>
)
/* 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 = (
    <Provider store={store}>
        <ThemeProvider theme={theme}>
            <App />
        </ThemeProvider>
    </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!
<MyComponent className="red-bg" />

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

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 值,让 <Button /> 组件变大一些,像这样:

<Button primary />

当你使用 styled-components 传入一个插值函数,我们其实就向组件传入了一个 props,使用它就可以进行组件样式调整。

const Button = styled.button`
  font-size: ${props => props.primary ? '2em' : '1em'};
`;

现在如果 Button 是个基本按钮(primary),就有 2em 大小的字体,否则为 1em。

// font-size: 2em;
<Button primary />

// font-size: 1em;
<Button />

再看一下logArgs 函数。我们传入一个字符串。

logArgs(`Test ${() => console.log('test')}`)
// -> Test () => console.log('test')

logArgs`Test ${() => console.log('test')}`
// -> ["Test", ""] () => console.log('test')

我们在调用模版字符串时候,确实可以拿到函数了。为了测试,我们来创建一个新的函数来执行所有入参的函数:

const execFuncArgs = (...args) => args.forEach(arg => {
  if (typeof arg === 'function') {
    arg()
  }
})

测试调用一波:

execFuncArgs('a', 'b')
// -> undefined

execFuncArgs(() => { console.log('this is a function') })
// -> "this is a function"

execFuncArgs('a', () => { console.log('another one') })
// -> "another one"

// 用模版字符串测试一波
execFuncArgs`Hi, ${() => { console.log('Executed!') }}`
// -> "Executed!"

execFuncArgs 的第二个参数其实就是一个函数,并且可以执行这个函数。

styled-components的底层就是这么实现的。在渲染时向插值函数中传入 props,我们就可以基于 props 来定制样式。

标签模板字符串使得 styled-components API 得以实现,没有这个特性 styled-compnents 就不可能出现。

相关文章

  • css modules & styled-component

    为什么要用css modules? 代码只改动一处; 只应用在特定的组件,不影响别的地方; 默认情况下,所有类名和...

  • CSS文章整理

    CSS Module CSS Modules 详解及 React 中实践CSS Modules 用法教程

  • CSS 的进化

    CSS 的进化: CSS Evolution: From CSS, SASS, BEM, CSS Modules ...

  • CSS Modules

    CSS Modules CSS Modules是一个流行的,用于模块化和组合 CSS 的系统。vue-loader...

  • #4 react-css-modules

    CSS Modules 在React中写样式有多种方式,比较常见的有 CSS modules,这种方法将css样式...

  • React CSS Modules(译文)

    原文地址:react-css-modules阅读本文前建议了解 CSS Modules 的知识。墙裂推荐阅读 Ca...

  • CSS Modules

    待续

  • CSS Modules

    原文地址 前段时间在阅读 ant-design-pro 的源码时,发现了一种没见过的 CSS 书写方法,它能有效地...

  • CSS Modules

    调研:http://www.ruanyifeng.com/blog/2016/06/css_modules.htm...

  • CSS Modules

    1. 什么是CSS Modules? Official definition[https://github.com...

网友评论

      本文标题:css modules & styled-component

      本文链接:https://www.haomeiwen.com/subject/enzubctx.html