CSS in JS之styled-components

2023-11-02

代码已经关联到github: 链接地址 觉得不错可以顺手点个star,这里会持续分享自己的开发经验(:

我们都知道,JSXJS语法的扩展,增加了对HTML语法的支持,那距离all in js就只差一个CSS语法支持了,目前实现该功能的库比较出名的有60+,感兴趣的可以自己查看:css-in-js
下面要介绍的就是其中之一的styled-components

简介

styled-components应该可以说CSS-in-JS最热门的一个库了,到目前为止github的star数已经超过了34k。
通过styled-components,你可以使用ES6的 标签模板字符串 语法在Component中定义一系列CSS属性,当该组件的JS代码被解析执行的时候,styled-components会动态生成一个CSS选择器,并把对应的CSS样式通过style标签的形式插入到head标签里面。
动态生成的CSS选择器会有一小段哈希值来保证全局唯一性来避免样式发生冲突。

Hello World

下面我们简单来了解下其写法:

const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;


const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
`;

render(<Wrapper>
    <Title>Hello World</Title>
  </Wrapper>
}

编译后的样子:

在这里插入图片描述

其特点是什么?

  • 相当与定义了一个空的组件
  • 每一段样式必须绑定到一个组件上
  • 语法与CSS完全一样
  • 模板字符串编写

原理

基于5.3.1版本代码,下面简单解释其原理。

语法解释

从其写法分析,代码的可以拆解为styled.xx和后面的模板字符串。

前者可以理解成一个styled-components定义的函数,eg:

const styled = (tag) => {
	//...
};

domElements.forEach(domElement => {
  styled[domElement] = styled(domElement);
});

那后面的模板字符串,函数是怎么去解析的呢?这就不得不说模板字符串的一个功能带标签的模板字符串,该功能可以用函数解析模板字符串,eg:

function tag(strings,...keys){
  console.log('strings:', strings)
  console.log('keys:',keys)
}

tag`
	color:red;
	background:${props => props.bgcolor};
`

//strings: ["\n\tcolor:red;\n\tbackground:", ";\n", raw:["\n\tcolor:red;\n\tbackground:", ";\n"]]
//keys: [props => props.bgcolor]

得到原始字符串和占位符数据,这样就可以组装好css模板,渲染的时候根据传入值渲染即可。

组件创建过程

  1. 生成唯一id

根据参数和父组件生成,比如上面编译后的sc-jlyJG

const styledComponentId = generateId(options.displayName, options.parentComponentId)
  1. 对模板字符串解析得到样式,在head 中插入一个 style 节点并将样式注入,返回 className,比如之前编译后的eFyTid。下面简单描述这一过程:
// 构造className 
const generatedClassName = generateName(cssStatic >>> 0);

//构造style节点并获得
const style = document.createElement('style');
style.setAttribute(SC_ATTR, SC_ATTR_ACTIVE);
style.setAttribute(SC_ATTR_VERSION, SC_VERSION);
//...省略上一个style标签位置查找
// Avoid Edge bug where empty style elements don't create sheets
style.appendChild(document.createTextNode(''));
//根据传入的节点获得sheet
this.sheet = style.sheet //getSheet(style)

//最后插入css
this.sheet.insertRule(rule, index);

  1. 解析组件的其他propsattr得到新的元素参数propsForElement
  const computedProps = attrs !== props ? { ...props, ...attrs } : props;
  const propsForElement = {};

  for (const key in computedProps) {
    if (
      shouldForwardProp
        ? shouldForwardProp(key, validAttr, elementToBeCreated)
        : isTargetTag
        ? validAttr(key)
        : true
    ) {
      // Don't pass through non HTML tags through to HTML elements
      propsForElement[key] = computedProps[key];
    }
  }
  1. 最后根据propsForElementclassName生成组件
propsForElement.className = Array.prototype
  .concat(
  styledComponentId,
  generatedClassName !== styledComponentId ? generatedClassName : null,
  props.className,
  attrs.className
)
  .filter(Boolean)
  .join(' ');

// elementToBeCreated生成过程被省略了,该参数是一个元素标签或者是一个react组件
return createElement(elementToBeCreated, propsForElement);

基础使用指南

基于Props设置样式

styled定义的组件可以接受props参数,根据不同的参数设置样式。

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>
);

继承样式

可以在继承一个已有组件的基础上生成一个新的组件,可以覆盖和新增样式。

const Button = styled.button`
  background: white;
  color: palevioletred;
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

const PrimaryButton = styled(Button)`
	background: palevioletred;
  color: white;
`

render(
  <div>
    <Button>Normal</Button>
    <PrimaryButton>Primary</Button>
  </div>
);

传递HTML属性

我们都知道html元素有很多属性,比如input 的 type、readonly和onChange 等属性。

const Input = styled.input`
  padding: 0.5em;
  margin: 0.5em;
  background: papayawhip;
  border: none;
`;

const handleChanged = (e) => {
	console.log('input changed:',e.target.value)
}

render(
  <Input defaultValue="default val" type="email" onChange={handleChanged}/>
)

组件中维护其他HTML属性

styled-components 同时支持为组件传入 html 元素的其他属性,比如为 input 元素指定一个 type 属性,我们可以使用 attrs关键字来实现。

使用过程中,我们可以使用静态属性,也可以使用props参数设置:

const Password = styled.input.attrs({
  type: 'password',
  placeholder:${props=>props.placeholder?props.placeholder:''}
})`
  color: palevioletred;
  font-size: 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

在实际开发中,这个方法还有一个有用处,用来引用第三方类库的 css 样式:

const Input = styled.input.attrs({
  className: 'my-input',
})`
  color: palevioletred;
  font-size: 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;
`;

与className和css框架搭配使用

可以与组件的className搭配使用,当然前提className是不会混淆的命名

const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
	& > .my-h1{
		font-size: 1.5em;
    text-align: center;
    color: palevioletred;
	}
`;

render(
   <Wrapper>
    <h1 className={'my-h1'}>My H1</h1>
  </Wrapper>
)

同样的也兼容less等css框架使用。

import * as styles from './index.less'

const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
`;

render(
   <Wrapper>
    <h1 className={styles['my-h1']}>My H1</h1>
  </Wrapper>
)

& 关键字

less 中类似,& 在语法中代表自身选择器,我们上一节就使用了该用法。

const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
	& > .my-h1{
		font-size: 1.5em;
    text-align: center;
    color: palevioletred;
	}
`;

除了这种用法,他还可以用来增加样式的权重

const Example = styled.li`
    color: red; 
    & {
        color:blue;
    }
    
    && {
        color: green;
    }
`;

动画

styled-components 同样对 css 动画中的 @keyframe 做了很好的支持。

const rotate = styled.keyframes`
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
`;

const styles = styled.css`
  animation: ${rotate} 2s linear infinite;
`

const Rotate = styled.div`
	${styles};
  display: inline-block;
  padding: 2rem 1rem;
  font-size: 1.2rem;
`;

render(
  <Rotate>&lt; 									
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CSS in JS之styled-components 的相关文章

  • 在导航栏下方添加背景图片

    我想在导航栏下方添加背景图像 具有完整窗口大小的宽度 它不应覆盖整个页面长度 而是从导航栏菜单下方开始 一直向下直到特定的指定高度 但宽度是满的 我参考了以下内容 但仍然没有结果 在 Twitter Bootstrap 中的导航栏下方启动背
  • 具有最大高度和滚动的动态内容的对话框+页脚CSS

    我有一个dialog with 位置 绝对 and a 最大高度放 这最大高度财产是set从外面by a javascript框架 jQuery UI 对话框 所以我无法控制它 里面有 2 个 div 其中一个充满了动态内容 and a 静
  • 有 CSS 父选择器吗?

    我该如何选择 li 元素是锚元素的直接父元素 举个例子 我的 CSS 应该是这样的 li lt a active property value 显然 有多种方法可以使用 JavaScript 实现此目的 但我希望 CSS Level 2 本
  • 如何仅使用CSS设置某个角的边框半径

    如上所示 我可以只给顶部部分而不给底部提供半径 或者有时给底部而不是顶部提供半径吗 有没有办法只给一个角提供边界半径 Like border radius top left top right bottom right bottom lef
  • 为什么 JSON.stringify 对于似乎具有属性的对象返回空对象符号“{}”?

    下面的例子表明JSON stringify 返回字符串 对于 SpeechSynthesisVoice 对象 var voiceObject window speechSynthesis getVoices 0 JSON stringify
  • mouseover 和 mouseout 事件在子进程上触发

    代码 div div div div 如果我将鼠标悬停在Navigation the Drop Downdiv 向下滑动 如果我将鼠标移开 它会向上滑动 问题是如果我将鼠标悬停在孩子上Drop Downdiv它也向上滑 动 有谁知道我该如何
  • 覆盖函数(例如“警报”)并调用原始函数?

    我想用调用原始版本的新版本覆盖 Javascript 内置函数 类似于用调用的版本覆盖类上的方法 super有多种语言版本 我怎样才能做到这一点 例如 window alert function str do something addit
  • nodejs mocha suite 未定义错误

    我正在尝试使用摩卡运行一些测试 但似乎无法克服这个错误 E tdd nodejs cart gt mocha cart test js node js 201 throw e process nextTick error or err Re
  • 获取 JSON 中的 HTML 以在 React 组件中呈现为 HTML

    试图找出如何让链接实际呈现为链接 现在 在我从 Json 文件中读取这行文本后 React 将超链接渲染为文字文本 而不将其渲染为链接 一些数据 json about John has a blog you can read a href
  • 嵌套辅助函数和性能

    嵌套辅助函数对于使代码更易于理解非常有用 谷歌甚至建议在他们的应用程序中使用嵌套函数时尚指南 https google styleguide googlecode com svn trunk javascriptguide xml Nest
  • 如何在表格的 tbody/thead 部分周围创建边框?

    我正在尝试创建一个包含表格数据的页面 该页面必须显示为多个表格 然而 我有两个相互冲突的要求需要解决 每个表格周围都必须有边框 每个表格的列宽必须能够根据内容重新调整大小 但是 所有表中的列宽必须一致 即列的大小基于所有表中该列中最大的单元
  • 如何使用CSS将背景图像放入选择选项标签中

    我有一个从 json 响应接收的国家 地区下拉列表 并且想要在国家 地区名称旁边添加相应的国家 地区国旗图标 也从 json 响应获取图像 我已经尝试了不同的 jQuery 和 bootstrap 插件 但他们所做的是破坏我以前的选择框样式
  • 有没有好的 JQuery twitter 小部件可以循环推文?

    我想知道是否有任何 JQuery 小部件提供了循环加载推文的功能 例如在官方小部件中http twitter com about resources widgets widget profile http twitter com about
  • 水平平滑滚动 100px

    Heyjo problem 一周以来我一直在寻找 javascript 或 jQuery 代码 以便在我的网站上实现滚动按钮 我失败的那一刻是按钮应该多次工作的时候 他的任务不是滚动到专用元素 而是应该向左滚动 例如 100px 此外 滚动
  • D3v6 嵌套图 - 嵌套 join()?

    我想可视化每个节点的 孩子 洞察力 我猜 D3v6 join 函数可以嵌套 不幸的是我找不到任何例子 下面的代码片段包含一个具有 3 个节点和子节点作为属性的outerGraph 到目前为止 这些孩子还没有被使用 相反 innerGraph
  • javascript:window.print() 打印 2 页,而我有 1 页

    我有一个简单的 HTML 文档 其中仅包含图像标签 我想在文档加载后打印图像 我的代码 img src form1 jpg alt form1 style margin 0 auto display block 它可以工作 但问题是它打印图
  • 在角度控制器中监听文档事件

    如何捕获角度控制器中的事件 我有文档级事件 所以我需要在角度控制器中捕获事件 这可能吗 Update 我有独立的 js 文件来处理来自相机的一些操作 document addEventListener myCameraEvent handl
  • 如何按字母顺序排序并先小写排序

    如何获得以下排序的结果Food to Eat然后是 食物123 显然 第二个较低的 o 应该将 要吃的食物 带到排序后的第一个项目中 我很惊讶这个问题不容易通过谷歌找到答案 这个壮举没有包含在 javascript 标准中也让我感到惊讶 F
  • Jquery 两个字段的时间差(以小时为单位)

    我的表单中有两个字段 用户可以在其中选择输入时间 start time end time 我想在更改这些字段时重新计算另一个字段的值 我想做的是获取两次之间的小时数 例如 如果我的开始时间为 5 30 结束时间为 7 50 我想将结果 2
  • 如何使用引用该键的变量来获取对象键中的值?

    我有一个对象 我可以引用密钥a如下 var obj a A b B c C console log obj a return string A 我想通过使用变量引用对象键来获取值 如下所示 var name a console log ob

随机推荐

Powered by Hwhale