随着React、Vue等支持组件化的MVVM前端框架越来越流行,在js中直接编写css的技术方案也越来越被大家所接受。
为什么前端开发者们更青睐于这些css-in-js的方案呢?我觉得关键原因有以下几点:
-
css在设计之初对“组件化”的考虑是不完全的,css直接作用于全局,无法直接作用于某个组件内部的样式。
-
在我们的前端组件中有很多“组件样式随着数据变化”的场景,但传统css应对这种场景很无力。
-
虽然我们可以通过一些规范来规避问题,但是真正用起来太繁琐了,也不利于跨团队的写作。
比如一个遵循BEM规范的表单组件要写成这个样子:
实在是太繁琐了!如果这是一段业务代码(注意,是业务代码),那团队中的其他人去读这段代码的时候内心一定是比较崩溃的。当然,如果是维护基础组件的话,遵守BEM规范「块(block)、元素(element)、修饰符(modifier)」还是非常重要的。
二、React中编写css的几种方式
2-1、有规范约束的className
使用一些命名规范(比如BEM规范)来约束className,比如下面这种:
// style.css
.form {
background-color: white;
}
.form__input {
color: black;
}
import ‘./stype.css’
const App = props => {
return (
)
}
这种方式比较适合基础组件库的开发,主要原因是:
-
使用class开发的组件库,业务方可以很方便地由组件样式的覆盖。
-
基础组件库一般由专门的团队开发,命名规范能统一。
-
使用最基础的class,能有效降低组件库的大小。
2-2、inline styling
const App = props => {
return (
)
}
这种方式是JSX语法自带的设置style的方法,会渲染出来内联样式,它有一个好处是可以在style中使用一些全局变量(但实际上,less等css预处理语言也是支持的)。另外,如果你只是要调一下组件的margin,这种写法也是代码量最小的写法。
2-3、css-loader(CSS Module)
使用webpack的css-loader可以在打包项目的时候指定该样式的scope,比如我们可以这样打包:
// webpack config
module.exports = {
module: {
loaders: [
{
test: /.css$/,
loader: ‘css-loader?modules&importLoaders=1&localIdentName=[name][local]_[hash:base64:5]’
},
]
},
…
}
// App.css
.app {
background-color: red;
}
.form-item{
color: red;
}
import styles from ‘./App.css’;
const App = props => {
return (
)
}
这样.app
就会被编译为.App__app___hash
这样的格式了。这种方式是借助webpack实现将组件内的css只作用于组件内样式,相比于直接写inline styling也是一个不错的解决方案。
但使用style['form-item']
这种形式去className的值(并且我们单独编写css文件时一般也都会使用“-
”这个符号),我觉得不少开发者会觉得很尴尬……
另外虽然webpack支持“-
”和驼峰互相转换,但是在实际开发中,如果面对一个样式比较多的组件,在css文件中使用“-
”然后在js组件中使用驼峰也是有一定的理解成本的。
2-4、css-in-js
顾名思义,css-in-js是在js中直接编写css的技术,也是react官方推荐的编写css的方案,在 https://github.com/MicheleBertoli/css-in-js 这个代码仓库中我们可以看到css-in-js相关的package已经有60多个了。
下面以emotion为例,介绍一下css-in-js的方案:
import { css, jsx } from ‘@emotion/core’
const color = ‘white’
// 下面这种写法是带标签的模板字符串
// 该表达式通常是一个函数,它会在模板字符串处理后被调用,在输出最终结果前
// 我们可以通过该函数来对模板字符串进行操作处理
// 详细链接 —— https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
const App = props => {
return (
className={css`
padding: 32px;
background-color: hotpink;
font-size: 24px;
border-radius: 4px;
`}
This is test.
)
}
在开发业务代码的时候,由于维护人员较多且不固定,且代码规模会逐渐增大,不能保证 css 不会交叉影响,所以我们不能只通过规范来约束,而是要通过 css-in-js 这样的方案来解决 css 交叉影响问题。
三、css-in-js方案比较
我们选取了 https://github.com/MicheleBertoli/css-in-js 仓库中支持功能全面且月下载量较多的几个css-in-js方案进行一下比较(其实它们在使用的时候都差距不大,主要是实现原理以及支持的特性有一些不太一样)
从体积来看:emotion的体积是最小的。
从技术生态环境(以及流行程度):styled-components的star最多,文档相对来讲也是最完善的。
从支持的特性来看:emotion、aphrodite、jss支持的特性是最多的。
所以新人可以尝试接触styled-components,综合来看emotion是一个相当不错的选择。
我们团队其实很早就开始使用React + emotion进行前端开发了。当时选择emotion主要的考虑就是它拥有最全面的功能,以及在当时的css-in-js方案中相对最小的体积。
而且emotion是为数不多的支持source-map的css-in-js框架之一。
四、emotion实现原理简介
4-1、emotion效果
首先让我们来看一下emotion做了什么,这是一个使用了emotion的React组件:
import React from ‘react’;
import { css } from ‘emotion’
const color = ‘white’
function App() {
return (
padding: 32px;
background-color: hotpink;
font-size: 24px;
border-radius: 4px;
&:hover {
color: ${color};
}
`}>
This is emotion test
);
}
export default App;
这是渲染出的html:
我们可以看到emotion实际上是做了以下三个事情:
-
将样式写入模板字符串,并将其作为参数传入
css
方法。 -
根据模板字符串生成class名,并填入组件的
class="xxxx"
中。 -
将生成的class名以及class内容放到
<style>
标签中,然后放到html文件的head中。
4-2、emotion初始化
首先我们可以看到,在emotion实例化的时候(也就是我们在组件中import { css } from 'emotion'
的时候),首先调用了create-emotion
包中的createEmotion
方法,这个方法的主要作用是初始化emotion的cache(用于生成样式并将生成的样式放入<head>
中,后面会有详细介绍),以及初始化一些常用的方法,其中就有我们最常使用的css
方法。
import createEmotion from ‘create-emotion’
export const {
injectGlobal,
keyframes,
css,
cache,
//…
} = createEmotion()
let createEmotion = (options: *): Emotion => {
// 生成emotion cache
let cache = createCache(options)
// 用于普通css
let css = (…args) => {
let serialized = serializeStyles(args, cache.registered, undefined)
insertStyles(cache, serialized, false)
return ${cache.key}-${serialized.name}
}
// 用于css animation
let keyframes = (…args) => {
let serialized = serializeStyles(args, cache.registered)
let animation = animation-${serialized.name}
insertWithoutScoping(cache, {
name: serialized.name,
styles: @keyframes ${animation}{${serialized.styles}}
})
return animation
}
// 注册全局变量
let injectGlobal = (…args) => {
let serialized = serializeStyles(args, cache.registered)
insertWithoutScoping(cache, serialized)
}
return {
css,
injectGlobal,
keyframes,
cache,
//…
}
}
4-3、emotion cache
emotion的cache用于缓存已经注册的样式,也就是已经放入head中的样式。在生成cache的时候,使用一款名为Stylis的CSS预编译器对我们传入的序列化的样式进行编译,同时它还生成了插入样式方法(insert)。
let createCache = (options?: Options): EmotionCache => {
if (options === undefined) options = {}
let key = options.key || ‘css’
let stylisOptions
if (options.prefix !== undefined) {
stylisOptions = {
prefix: options.prefix
}
}
let stylis = new Stylis(stylisOptions)
let inserted = {}
let container: HTMLElement
if (isBrowser) {
container = options.container || document.head
}
let insert: (
selector: string,
serialized: SerializedStyles,
sheet: StyleSheet,
shouldCache: boolean
) => string | void
if (isBrowser) {
stylis.use(options.stylisPlugins)(ruleSheet)
insert = (
selector: string,
serialized: SerializedStyles,
sheet: StyleSheet,
shouldCache: boolean
): void => {
let name = serialized.name
Sheet.current = sheet
stylis(selector, serialized.styles) // 该方法会在对应的selector中添加对应的styles
if (shouldCache) {
cache.inserted[name] = true
}
}
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
最后
本人分享一下这次字节跳动、美团、头条等大厂的面试真题涉及到的知识点,以及我个人的学习方法、学习路线等,当然也整理了一些学习文档资料出来是附赠给大家的。知识点涉及比较全面,包括但不限于前端基础,HTML,CSS,JavaScript,Vue,ES6,HTTP,浏览器,算法等等
详细大厂面试题答案、学习笔记、学习视频等资料领取,点击资料领取直通车免费领取!
前端视频资料:
助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中…(img-Nrf793dN-1713552217731)]
[外链图片转存中…(img-iIG2KLJJ-1713552217732)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
[外链图片转存中…(img-08aLqJzX-1713552217732)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)
[外链图片转存中…(img-f2OqMkCe-1713552217732)]
最后
本人分享一下这次字节跳动、美团、头条等大厂的面试真题涉及到的知识点,以及我个人的学习方法、学习路线等,当然也整理了一些学习文档资料出来是附赠给大家的。知识点涉及比较全面,包括但不限于前端基础,HTML,CSS,JavaScript,Vue,ES6,HTTP,浏览器,算法等等
详细大厂面试题答案、学习笔记、学习视频等资料领取,点击资料领取直通车免费领取!
[外链图片转存中…(img-EblhqHiO-1713552217733)]
前端视频资料: