css-in-js 探究(一)

230 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第15天,点击查看活动详情

主要讲当前的几种css场景, css-in-js 概括、优缺点、使用场景、常见库(emotion)里面源码的大致实现。

css命名方法

  • BEM、模块名__元素名--修饰器名
  • OOCSS、面对对象,将元素样式抽象分成多个独立的小型样式类、
  • ITCSS,
  • SMACSS

css modules

一个css文件就是一个独立的模块,通过组件名的唯一性代表选择器的唯一,保证样式不会污染到组件外

每一个类名都是该模块导出对象的一个属性,打包时自动将id和class混淆成全局唯一的hash避免冲突

import styles from './xx.module.less'
<p className={styles.p}>

css-in-js

因为组件化概念,html、js、css写在了一起

react对于html,出现了jsx,即html-in-js

对于css,同样出现了css-in-js

最流行的库有

styled-components、emotion

  • 案例 material-ui

  • 优点

1、让css拥有独立的作用域,实现局部样式,防止样式冲突

2、避免无用的css样式堆积

3、重要的css放头style标签里,其他的异步加载减少渲染阻塞

4、通过组件状态动态生成样式

5、让组件能更好的移植和复用

  • 缺点

1、陡峭的学习曲线

2、运行时消耗

可读性差

没有统一的业界标准

特点连贯总结:

有独立作用域,不会样式冲突,但是hash生成的类名可读性差

由于跟着组件异步加载,可以避免无用的css样式堆积,可以把重要的css样式放在头style标签里,其他的通过异步加载减少渲染堵塞,根据组件状态动态加载样式,但会运行时消耗。

让组件有更好的移植和复用,但有陡峭的学习曲线,也暂时没有统一的业界标准(istf制定中)。

  • 选型

功能简单,重视代码可读性和维护性不用

逻辑复杂,重视封装和移植可以用。

emotion/css

通过生成唯一的css选择器来达到css局部作用域的效果

安装:npm i @emotion/css -save

使用:

import {css} from '@emotion/css'
const className = css`
 color:red;
`
function App(){
	return <div className={className}>app</div>
}

渲染:

<style data-emotion='css' data-s>.css-lqvvwil{color:red;}</style>
<div class='css-lqvvwil'>
    app
</div>

模板字符串的特性复习

es6.ruanyifeng.com/?search=Ref…

let a = 5;
let b = 10;

tag`Hello ${ a + b } world ${ a * b }`;
// 等同于
tag(['Hello ', ' world ', ''], 15, 50);

tag函数的第一个参数是一个数组,该数组的成员是模板字符串中那些没有变量替换的部分。其他参数都是模板字符串各个变量被替换后的值

基础实现

以上面的例子为例

css模板字符串需要实现的是:

1.拿到color:red;,生成hash

2.在head新加style标签,注入hash类和color:red;

3.返回hash

代码

// args[0] = ['color:red;]
function css(...args) {
    // 用 模板字符串传的东西转成 classname和styles
  const serialized = serializeStyles(args);//{name:'名字',styles:'color:red;'}
    // 将东西插入head作为style标签
  insertStyles(serialized);
    //返回  classname 给元素安排上
  return 'css-' + serialized.name;
}
function serializeStyles(args, props) {
  const strings = args[0];//color:red;
    let styles = strings
  const name = hashString(styles);
  return {
    name,
    styles
  };
}
// 转16进制,截前7位当hash
function hashString(keys) {
  let val = 10000000;
  for (let i = 0; i < keys.length; i++) {
    val += keys.charCodeAt(i);
  }
  return val.toString(16).slice(0, 7);
}
// 插入style标签
function insertStyles(serialized) {
  const className = 'css-' + serialized.name;//类名
  const rule = `.${className}{${serialized.styles}}`;
  const style = document.createElement('style');
  style.setAttribute('data-emotion', 'css');//没有什么用
  style.appendChild(document.createTextNode(rule));//style.innerHTML = rule;
  document.head.appendChild(style);
}

传入对象

css({
	color:'red'
})

实现过程:

需要在原先的基础上判断传入的是否是对象,可以通过判断是否有 strings.raw,有即代表是模板字符串

将属性和值拼起来

其他的步骤一样

代码

const strings = args[0];
let styles = ''
 if (strings.raw === undefined) {//说明传的是对象
    for (const key in strings) {
   	 string += (key + ":" + obj[key] + ";");
  	}
  }

还有种 emotion/react,等下一期说