javascript

css in js 学习随笔

2020-01-10  本文已影响0人  在宇宙Debugger

写在前面

食用此文档需要你至少具备

  • 基础的css使用
  • 基础的JavaScript使用
  • 基础的react的使用

在React还未出现的时候, 网页开发有一个原则叫做关注点分离(正交原则)

关注点分离

首先看一下百度百科的解释

关注点分离(Separation of concerns,SOC)是对只与“特定概念、目标”(关注点)相关联的软件组成部分进行“标识、封装和操纵”的能力,即标识、封装和操纵关注点的能力。是处理复杂性的一个原则。由于关注点混杂在一起会导致复杂性大大增加,所以能够把不同的关注点分离开来,分别处理就是处理复杂性的一个原则,一种方法。

通俗易懂的说就是, HTML, css, JavaScript 分离, 不要混到一起用, 不要写 行内样式行内脚本, 比如下面的代码就很糟糕

<h1 style="color:red;font-size:46px;"  onclick="alert('Hi')">
  Hello World
</h1>

正确的做法是分为三个部分, 分别写html/css/javaScript

<h1 calss="title">
  Hello World
</h1>
.title {
  color: red;
  font-size: 46px;
}
const titleDom = document.querySelector('.title');
titleDom.addEventListener('click', sayHi);

function sayHi() {
  alert('Hi');
}

React 的出现

React 出现以后, 这个原则不再适用了。 因为React是组件结构, 强制要求把 HTML、CSS、JavaScript 写在一起。

上面的例子使用 React 改写如下

const style = {
  'color': 'red',
  'fontSize': '46px'
};

const clickHandler = () => alert('hi');

ReactDOM.render(
  <h1 style={style} onclick={clickHandler}>
     Hello, world!
  </h1>,
  document.getElementById('example')
);

上面代码在一个文件里面,封装了结构、样式和逻辑,完全违背了"关注点分离"的原则。

但是,这有利于组件的隔离。每个组件包含了所有需要用到的代码,不依赖外部,组件之间没有耦合,很方便复用。所以,随着 React 的走红和组件模式深入人心,这种关注点混合的新写法逐渐成为主流。

css in js

由于React的出现, 使得关注点混合的新写法逐渐成为主流, 但由于React对css的封装非常弱, 导致了一系列第三方库, 用来加强React 的 css操作, 也就是说css in js 技术仅仅适用于关注点混合的结构

由于只要是加强JavaScript 对 css 的操作就可以称为css in js, 因此css in js并没有统一的实现方式, 如果你要挑选css in js类库, 可以在MicheleBertoli/css-in-js中找你喜欢的库, 这里收集了大量使用较多的css in js

下面介绍一款css in js类库的使用

Aphrodite

目前排名第一的是 Aphrodite 这个类库,这里我写了一个Aphrodite的在线模板, 可以直接在上面练习

先来看一下基础用法

import React from "react";
import ReactDOM from "react-dom";
import { StyleSheet, css } from "aphrodite";

// 创建样式
const styles = StyleSheet.create({
  red: { color: "red" }
});

ReactDOM.render(
  // 载入样式
  <div className={css(styles.red)}>
    <h1>Hello aphrodite!</h1>
  </div>,
  document.getElementById("root")
);

在线示例

既然是用JS来控制CSS, 就可以用条件判断来容易的控制样式

import React, { Component } from "react";
import ReactDOM from "react-dom";
import { StyleSheet, css } from "aphrodite";

// 创建样式
const styles = StyleSheet.create({
  red: { color: "red" },
  green: { color: 'green' }
});

class App extends Component {
  constructor(props) {
    super(props);
    this.state = { isGreen: false };
    this.handleClick = this.handleClick.bind(this);
  }

  /**
   * 点击事件, 用来切换红色与绿色
   */
  handleClick() {
    this.setState({
      isGreen: !this.state.isGreen,
    });
  }

  /**
   * 获取组件样式表
   */
  getClassName() {
    const { isGreen } = this.state;
    const titleClassName = css(isGreen ? styles.green : styles.red);
    return { titleClassName };
  }

  render() {
    const { handleClick } = this;
    const { titleClassName } = this.getClassName();
    return (
      <div className={titleClassName}>
        <h1>Hello aphrodite!</h1>
        <button onClick={handleClick}>
          change
        </button>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));

在线示例

Aphrodite默认所有的样式都增加了 !important 如果不想要这个特性, 可以引入aphrodite/no-important

import { StyleSheet, css } from 'aphrodite/no-important';

除了以上基础用法, Aphrodite还提供了minify函数, 用来压缩css

import { StyleSheet, minify } from 'aphrodite';

minify(true);

Aphrodite还可以在非react的环境下使用, 但不推荐这样做, 这里不做示例了

另外使用Aphrodite还要注意, 在获取元素属性时, 需要做一下延时, 因为Aphrodite不能确保设置的元素样式会立即生效

Style injection and buffering
Aphrodite will automatically attempt to create a <style> tag in the document's <head> element to put its generated styles in. Aphrodite will only generate one <style> tag and will add new styles to this over time. If you want to control which style tag Aphrodite uses, create a style tag yourself with the data-aphrodite attribute and Aphrodite will use that instead of creating one for you.

To speed up injection of styles, Aphrodite will automatically try to buffer writes to this <style> tag so that minimum number of DOM modifications happen.

Aphrodite uses asap to schedule buffer flushing. If you measure DOM elements' dimensions in componentDidMount or componentDidUpdate, you can use setTimeout or flushToStyleTag to ensure all styles are injected.

自己封装一个css in js 库

看了一些示例之后我自己也试着封装了一个css in js库, 有兴趣的同学可以看源码
在线示例


大致思路参考

首先将传入的data, 例如

const data = {
  app: {
    fontFamily: "sans-serif",
    textAlign: "center"
  }
}

解析成

{
  className: 'app',
  id: 'cssInJs0',
  style: { ... },
}

将解析后的数据转成style标签并置入head标签中, 解析完成如下

<style>.cssInJs0.app{font-family:sans-serif;text-align:center;}</style>

将类名暴露出去, 并暴露一个方法css, 作用就是自动拼接className, 并去重

<div className={css(sty.app)}></div>

使用css解析出的className

<div class="cssInJs0 app"></div>

以上 ~

参考文章:

上一篇 下一篇

猜你喜欢

热点阅读