7
To define is to limit.
定义一样东西,就意味着限制了它。

——王尔德 《道林·格雷的画像》

React系列

React系列 --- 简单模拟语法(一)
React系列 --- Jsx, 合成事件与Refs(二)
React系列 --- virtualdom diff算法实现分析(三)
React系列 --- 从Mixin到HOC再到HOOKS(四)
React系列 --- createElement, ReactElement与Component部分源码解析(五)
React系列 --- 从使用React了解Css的各种使用方案(六)
React系列 --- 从零构建状态管理及Redux源码解析(七)
React系列 --- 扩展状态管理功能及Redux源码解析(八)

前言

这里主要总结一下CSS是怎么一步步演化呈现在百花齐放的局面,从用上React之后就更加多选择了,我就举例一下我的理解和用过的方案,还有一些了解过觉得还不错的方案.本身没有推荐或贬低哪些的意思,只是觉得可以满足需求的都是可以使用的方案.

CSS

用官方口吻来说就是

CSS 能够对网页中元素位置的排版进行像素级精确控制,支持几乎所有的字体字号样式,拥有对网页对象和模型样式编辑的能力。

具有以下特点:

  1. 丰富的样式定义

    CSS提供了丰富的包括但不限于样式外观,文本字体,背景属性,边距形状等等的能力,还有一些浏览器特有的属性定制.

  2. 易于使用修改

    可以将样式定义在HTML元素的style属性中,HTML文档的header部分,也可以将样式声明在一个专门的CSS文件中。

    可以将相同样式的元素进行归类使用同一个样式进行定义,也可以将某个样式应用到所有同名的HTML标签中。

  3. 多页面应用

    CSS样式表理论上不属于任何页面文件,在任何页面文件中都可以将其引用。这样就可以实现多个页面风格的统一。

  4. 层叠

    简单的说,层叠就是对一个元素多次设置同一个样式,后来定义的样式将对前面的样式设置进行重写。

  5. 体积减少

    在使用HTML定义页面效果的网站中,往往需要大量或重复的表格和font元素形成各种规格的文字样式,而将样式的声明单独放到CSS样式表中,可以大大的减小页面的体积,这样在加载页面时使用的时间也会大大的减少。另外,CSS样式表的复用更大程序的缩减了页面的体积,减少下载的时间。

然后随着用户审美水平日益提高的标准和需求,普通的样式已经不足以支撑起来了,特别有些简单要求还会让开发绞尽脑汁用尽各种奇淫巧技来实现,这种情况甚至会大大违背结构和样式分离的原则.

CSS3

作为CSS的升级版,主要包括盒子模型、列表模块、超链接方式、语言模块、背景和边框、文字特效、多栏布局等模块.

CSS3规范的一个新特点是被分为若干个相互独立的模块。一方面分成若干较小的模块较利于规范及时更新和发布,及时调整模块的内容,这些模块独立实现和发布,也为日后CSS的扩展奠定了基础。另外一方面,由于受支持设备和浏览器厂商的限制,没备或者厂商可以有选择的支持一部分模块,支持CSS3的一个子集,这样有利于CSS3的推广,但不同浏览器在不同时段支持不同特性,这也让跨浏览器开发变得复杂,当然截止到2019,主流浏览器已经基本支持了.低端版本也能通过各种辅助插件完成.

提升优势:

  1. 减少开发成本与维护成本

    例如以前需要使用图片定位模拟圆角,绝对定位+定时器模拟动画,元素伪造滚动条样式等各种繁琐工作都可以省略掉了.

  2. 提高页面性能

    减少多余的标签嵌套以及图片的使用数量,意味着用户要下载的内容将会更少,页面加载也会更快,能够减少用户访问Web站点时的HTTP请求数,这是提升页面加载速度的最佳方法之一

CSS3的推广一方面增强了样式的展示,一方面免去开发实现的额外工程,但是本质上并没有减少开发的工作量,因为有很多类似而不相同的样式还是需要手写出来.现在的网页越来越大,画面越来越丰富,于是就应运而生出了------

预处理器

因为我都是使用SCSS,就以此举例,但是其实语法和实现功能大致相同的.

预处理器是一种强化 CSS 的辅助工具,它在 CSS 语法的基础上增加了变量 (variables)、嵌套 (nested rules)、混合 (mixins)、导入 (inline imports) 等高级功能,这些拓展令 CSS 更加强大与优雅,有助于更好地组织管理样式文件,以及更高效地开发项目.

简单举例常用而强大的特性:

  1. 嵌套规则 (Nested Rules)

    允许将一套 CSS 样式嵌套进另一套样式中,内层的样式将它外层的选择器作为父选择器,还有属性嵌套,占位符选择器等等

  2. SassScript

    可以通过声明变量,函数,运算等抽取重复样式代码减少大量重复样板代码.

  3. 指令

    @import导入 SCSS 或 Sass 文件 ,@media 嵌套在 CSS 规则内,@extend继承样式等

  4. 控制指令

    运用各种判断循环实现控制样式

CSS的瓶颈

上面都是前端开发的标配技能,懂得自然都懂,就不说太多了,但是该碰到的坑自然都会遇到.

主要这么几个:

  1. 全局污染

    好的规范自然能够极大方便重用样式减少代码,但是别说多人协作开发,即使个人维护都可能碰到多种样式覆盖,权重优先级混乱,冗余重复属性过多,于是各种内联样式,!important强制最先等写法,虽然也有其他方案如功能属性class拆分避免这种窘境,但是增大开发的参与难度,很难形成一套适用标准,起码我觉得太繁琐了.

  2. 命名烦恼

    元素嵌套越深,如果想命名语义化明确点就可能导致越来越长越复杂的命中规则,而有一些一次性的样式也不得不遵循这种规则,否则用内联样式替代

  3. 样式压缩

    因为样式对应到具体的引用属性id,class,选择器等,这一块是没办法省略的,嵌套越深入选择器前缀可能就越长,例如#A .B .C .D p{}这种

预处理器只是简化开发写法,实际编译完成也就这个样,本质上的问题没有得到解决.

CSS-in-JS

React本身没有定义样式的主张,而是用第三方库提供的CSS-in-JS.这里是一些实现的对比,之所以会出现种类繁多的第三方库是因为它们实现功能的支持有所区别,主要在

  1. Automatic Vendor Prefixing (自动添加前缀)
  2. Pseudo Classes (伪类)
  3. Media Queries (媒体查询)
  4. Styles As Object Literals (对象字面量样式)
  5. Extract CSS File (提取CSS文件)

例如React的官方示例原生style属性使用

style 属性接受具有驼峰命名属性的 JavaScript 对象,而不是 CSS 字符串。 这与 JavaScript DOM 的 style 属性一致,但是更高效,并且防止XSS安全漏洞。 例如:
const divStyle = {
  color: 'blue',
  backgroundImage: 'url(' + imgUrl + ')',
};

function HelloWorldComponent() {
  return <div style={divStyle}>Hello World!</div>;
}

注意:

  1. 样式不自动进行兼容。 要支持旧版本的浏览器,您需要提供相应的样式属性
  2. React 会自动为某些内联样式的数字属性值附加一个 “px” 后缀。 如果你想使用 “px” 以外的单位,请明确指定单位,并将该值指定为字符串
  3. 并不支持所有的 css,例如媒体查询,:before:nth-child等 pseudo selectors

然后我们也能用一些其他的CSS-in-JS库解决,我就拿比较多人使用支持功能比较完善的来举例一下,下面方案都属于这种,但是倾向和用法上有一定区别:

aphrodite

Framework-agnostic CSS-in-JS with support for server-side rendering, browser prefixing, and minimum CSS generation.

Support for colocating your styles with your JavaScript component.

支持功能

  • 不强制和React搭配才能使用
  • 支持媒体查询无需window.matchMedia
  • 支持伪选择器像:hover, :active等等,不需要在组件中存储悬停或活动状态。 :visited也能良好支持
  • 支持自动全局@font-face检测和插入
  • 在指定多个样式时,尊重优先顺序
  • 不需要AST转换
  • 只向DOM中注入渲染所需的确切样式。
  • 可以用于服务器渲染
  • 低依赖,小(20k,压缩后6k)
  • 没有外部的Css文件生成
  • 自动添加前缀

示例

import React, { Component } from 'react';
import { StyleSheet, css } from 'aphrodite';

class App extends Component {
    render() {
        return <div>
            <span className={css(styles.red)}>
                This is red.
            </span>
            <span className={css(styles.hover)}>
                This turns red on hover.
            </span>
            <span className={css(styles.small)}>
                This turns red when the browser is less than 600px width.
            </span>
            <span className={css(styles.red, styles.blue)}>
                This is blue.
            </span>
            <span className={css(styles.blue, styles.small)}>
                This is blue and turns red when the browser is less than
                600px width.
            </span>
        </div>;
    }
}

const styles = StyleSheet.create({
    red: {
        backgroundColor: 'red'
    },

    blue: {
        backgroundColor: 'blue'
    },

    hover: {
        ':hover': {
            backgroundColor: 'red'
        }
    },

    small: {
        '@media (max-width: 600px)': {
            backgroundColor: 'red',
        }
    }
});

当然也很多人不太接受这种方式书写Css,还把html结构嵌套一堆className={css()}的写法.

styled-components

使用标记的模板文本和CSS的强大功能,样式组件允许您编写实际的CSS代码来样式化组件。它还删除了组件和样式之间的映射——将组件用作低级样式结构再简单不过了!

风格组件既兼容React(用于web),也兼容React Native——这意味着它甚至是真正通用的应用程序的完美选择!

示例

import React from 'react';

import styled from 'styled-components';

// Create a <Title> react component that renders an <h1> which is
// centered, palevioletred and sized at 1.5em
const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;

// Create a <Wrapper> react component that renders a <section> with
// some padding and a papayawhip background
const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
`;

// Use them like any other React component – except they're styled!
<Wrapper>
  <Title>Hello World, this is my first styled component!</Title>
</Wrapper>

优点

  • 直接将样式组件化,移除样式和组件的映射关系
  • 支持组件继承,方便复用
  • 和原有CSS方式无障碍搭配使用

styled-jsx

这是我查资料的时候了解到的一种方案,因为没有实际用过,所以我只贴上它的功能示例

主要功能

  • 完全的CSS支持, no tradeoffs in power
  • 运行时大小仅为3kb(从12kb压缩为gzip)
  • 完全隔离:选择器、动画、关键帧
  • 内置CSS浏览器前缀
  • 非常快速、最小和高效
  • 非服务器渲染时高效运行注入
  • 面向未来:相当于服务器可渲染的"Shadow CSS"
  • 支持源映射
  • 支持动态样式和主题
  • 通过插件进行CSS预处理

示例

export default () => (
  <div>
    <p>only this paragraph will get the style :)</p>

    { /* you can include <Component />s here that include
         other <p>s that don't get unexpected styles! */ }

    <style jsx>{`
      p {
        color: red;
      }
    `}</style>
  </div>
)

CSS Module

这是我在学习webpack的Css-loader的时候了解到的一种方案,现在在使用的方案

CSS Module是一个CSS文件,默认所有类和动画命名都限于局部范围.所有资源(url(...))@imports都在模块请求格式.

Css模块会被编译成底层交换格式像ICSS或者Interoperable CSS,但是书写格式和普通CSS文件一样.

当从JS模块导入CSS模块时,它导出一个具有从本地名称到全局名称的所有映射的对象。

CSS

/* style.css */
.className {
  color: green;
}

JSX

import { PureComponent } from 'react';
import styles from './index.scss';

class Page2 extends PureComponent {
  constructor(props) {
    super(props)
  }

  render() {
    return (
      <div className={styles.className}>
      </div>
    );
  }
}

当然这不是只限于React,使用webpack或者其他打包工具的方式都能支持使用.

优点

模块化和可重用性

  • 没有冲突
  • 显性依赖
  • 不会污染全局
  • 可以配合预处理器使用
  • API少,开发单独CSS文件写法基本一致

Afterward
621 声望62 粉丝

努力去做,对的坚持,静待结果