Styled-Components

它是通过JavaScript改变CSS编写方式的解决方案之一,从根本上解决常规CSS编写的一些弊端。
通过JavaScript来为CSS赋能,我们能达到常规CSS所不好处理的逻辑复杂、函数方法、复用、避免干扰。
尽管像SASS、LESS这种预处理语言添加了很多用用的特性,但是他们依旧没有对改变CSS的混乱有太大的帮助。因此组织工作交给了像 BEM这样的方法,虽然比较有用,但是它完全是自选方案,不能被强制应用在语言或者工具层面。
他搭配React可能将模块化走向一个更高的高度,样式书写将直接依附在JSX上面,HTML、CSS、JS三者再次内聚。

基本

安装

npm install --save styled-components

除了npm安装使用模块化加载包之外,也支持UMD格式直接加载脚本文件。

<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>

入门

styled-components使用标签模板来对组件进行样式化。

它移除了组件和样式之间的映射。这意味着,当你定义你的样式时,你实际上创造了一个正常的React组件,你的样式也附在它上面。

这个例子创建了两个简单的组件,一个容器和一个标题,并附加了一些样式。

// Create a Title component that'll render an <h1> tag with some styles
const Title = styled.h1`font-size: 1.5em;text-align: center;color: palevioletred;
`;// Create a Wrapper component that'll render a <section> tag with some styles
const Wrapper = styled.section`padding: 4em;background: papayawhip;
`;// Use Title and Wrapper like any other React component – except they're styled!
render(<Wrapper><Title>Hello World, this is my first styled component!</Title></Wrapper>
);

注意
CSS规则会自动添加浏览器厂商前缀,我们不必考虑它。

透传props

styled-components会透传所有的props属性。

// Create an Input component that'll render an <input> tag with some styles
const Input = styled.input`padding: 0.5em;margin: 0.5em;color: palevioletred;background: papayawhip;border: none;border-radius: 3px;
`;// Render a styled text input with a placeholder of "@mxstbr", and one with a value of "@geelen"
render(<div><Input placeholder="@mxstbr" type="text" /><Input value="@geelen" type="text" /></div>
);

基于props做样式判断

模板标签的函数插值能拿到样式组件的props,可以据此调整我们的样式规则。

const Button = styled.button`/* Adapt the colours based on primary prop */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>
);

样式化任意组件

// This could be react-router's Link for example
const Link = ({ className, children }) => (<a className={className}>{children}</a>
)const StyledLink = styled(Link)`color: palevioletred;font-weight: bold;
`;render(<div><Link>Unstyled, boring Link</Link><br /><StyledLink>Styled, exciting Link</StyledLink></div>
);

扩展样式

我们有时候需要在我们的样式组件上做一点扩展,添加一些额外的样式:
需要注意的是.extend在对样式组件有效,如果是其他的React组件,需要用styled样式化一下。

// The Button from the last section without the interpolations
const Button = styled.button`color: palevioletred;font-size: 1em;margin: 1em;padding: 0.25em 1em;border: 2px solid palevioletred;border-radius: 3px;
`;// We're extending Button with some extra styles
const TomatoButton = Button.extend`color: tomato;border-color: tomato;
`;render(<div><Button>Normal Button</Button><TomatoButton>Tomato Button</TomatoButton></div>
);

在极少特殊情况下,我们可能需要更改样式组件的标签类型。我们有一个特别的API,withComponent可以扩展样式和替换标签:

const Button = styled.button`display: inline-block;color: palevioletred;font-size: 1em;margin: 1em;padding: 0.25em 1em;border: 2px solid palevioletred;border-radius: 3px;
`;// We're replacing the <button> tag with an <a> tag, but reuse all the same styles
const Link = Button.withComponent('a')// Use .withComponent together with .extend to both change the tag and use additional styles
const TomatoLink = Link.extend`color: tomato;border-color: tomato;
`;render(<div><Button>Normal Button</Button><Link>Normal Link</Link><TomatoLink>Tomato Link</TomatoLink></div>
);

添加attr

我们可以使用attrsAPI来为样式组件添加一些attr属性,它们也可以通过标签模板插值函数拿到props传值。

const Input = styled.input.attrs({// we can define static propstype: 'password',// or we can define dynamic onesmargin: props => props.size || '1em',padding: props => props.size || '1em'
})`color: palevioletred;font-size: 1em;border: 2px solid palevioletred;border-radius: 3px;/* here we use the dynamically computed props */margin: ${props => props.margin};padding: ${props => props.padding};
`;render(<div><Input placeholder="A small text input" size="1em" /><br /><Input placeholder="A bigger text input" size="2em" /></div>
);

动画

带有@keyframes的CSS animations,一般来说会产生复用。styled-components暴露了一个keyframes的API,我们使用它产生一个可以复用的变量。这样,我们在书写css样式的时候使用JavaScript的功能,为CSS附能,并且避免了名称冲突。

// keyframes returns a unique name based on a hash of the contents of the keyframes
const rotate360 = keyframes`from {transform: rotate(0deg);}to {transform: rotate(360deg);}
`;// Here we create a component that will rotate everything we pass in over two seconds
const Rotate = styled.div`display: inline-block;animation: ${rotate360} 2s linear infinite;padding: 2rem 1rem;font-size: 1.2rem;
`;render(<Rotate>&lt; ? &gt;</Rotate>
);

支持 React Native

高级特性

Theming

styled-components暴露了一个<ThemeProvider>容器组件,提供了设置默认主题样式的功能,他类似于react-rudux的顶层组件Provider,通过context实现了从顶层到底层所有样式组件的默认主题共用。

const Button = styled.button`font-size: 1em;margin: 1em;padding: 0.25em 1em;border-radius: 3px;/* Color the border and text with theme.main */color: ${props => props.theme.main};border: 2px solid ${props => props.theme.main};
`;Button.defaultProps = {theme: {main: 'palevioletred'}
}
// Define what props.theme will look like
const theme = {main: 'mediumseagreen'
};render(<div><Button>Normal</Button><ThemeProvider theme={theme}><Button>Themed</Button></ThemeProvider></div>
);

Refs

通常我们在给一个非原生样式组件添加ref属性的时候,其指向都是该组件实例的索引,我们通过用innerRef可以直接拿到里面的DOM节点。

const AutoFocusInput = styled.input`background: papayawhip;border: none;
`;class Form extends React.Component {render() {return (<AutoFocusInputplaceholder="Hover here..."innerRef={x => { this.input = x }}onMouseEnter={() => this.input.focus()}/>);}
}

Security

因为styled-components允许我们使用任意输入作为CSS属性值,一旦意识到这一点,我们马上明白要对输入做安全性校验了,因为使用用户外部的输入样式可以导致用户的浏览器被CSS注入攻击。CSS注入攻击可能不明显,但是我们还是得小心一点,某些IE浏览器版本甚至允许在URL声明中执行任意的JS。

这个例子告诉我们外部的输入甚至可能在CSS内调用一个API网络请求。

// Oh no! The user has given us a bad URL!
const userInput = '/api/withdraw-funds';const ArbitraryComponent = styled.div`background: url(${userInput});/* More styles here... */
`;

CSS.escape这个未来API标准可净化JS中的CSS的问题。但是浏览器兼容性目前还不是太好,所以我们建议在项目中使用polyfill by Mathias Bynens

CSS共存

如果我们打算把styled-components和现有的css共存的话,我们需要注意两个实现的细节问题:

styled-components也会生成真实的样式表,并通过className属性链接生成的样式表内容。在JS运行时,他会生成一份真实的style节点插入到document的head内。

注意的一个小地方:

// MyComponent.js
const MyComponent = styled.div`background-color: green;`;// my-component.css
.red-bg {background-color: red;
}// For some reason this component still has a green background,
// even though you're trying to override it with the "red-bg" class!
<MyComponent className="red-bg" />

我们styled-components生成的style样式表一般是在head头部的最底下,同等CSS优先级条件下是会覆盖默认前者css文件的样式的。这个插入顺序使用webpack来调整是比较难得。所以,我们一般都这样通过调整css优先级来改变显示:

/* my-component.css */
.red-bg.red-bg {background-color: red;
}

Media Templates

媒体查询是开发响应式web应用不可或缺的存在,这是一个简单的例子:

const Content = styled.div`background: papayawhip;height: 3em;width: 3em;@media (max-width: 700px) {background: palevioletred;}
`;render(<Content />
);

因为媒体查询语句很长,并且经常在整个应用程序中重复使用,所以为此创建一些模板来复用是很有必要的。

使用JS的功能特性,我们可以轻松定义一份可配置的语句,包装媒体查询和样式。

const sizes = {desktop: 992,tablet: 768,phone: 376
}// Iterate through the sizes and create a media template
const media = Object.keys(sizes).reduce((acc, label) => {acc[label] = (...args) => css`@media (max-width: ${sizes[label] / 16}em) {${css(...args)}}`return acc
}, {})const Content = styled.div`height: 3em;width: 3em;background: papayawhip;/* Now we have our methods on media and can use them instead of raw queries */${media.desktop`background: dodgerblue;`}${media.tablet`background: mediumseagreen;`}${media.phone`background: palevioletred;`}
`;render(<Content />
);

这太cool了,不是吗?

Tagged Template Literals

标签模板是ES6的一个新特性,这是我们styled-components创建样式组件的方式和规则。

const aVar = 'good';// These are equivalent:
fn`this is a ${aVar} day`;
fn([ 'this is a ', ' day' ], aVar);

这看起来有点麻烦,但是这意味着我们可以在styled-components生成样式组件中接受变量、函数、minxins,并将其变为纯css。

这篇文章可以了解更多:The magic behind ? styled-components

Server Side Rendering

styled-components很好地支持SSR。

一个例子:

import { renderToString } from 'react-dom/server'
import { ServerStyleSheet } from 'styled-components'const sheet = new ServerStyleSheet()
const html = renderToString(sheet.collectStyles(<YourApp />))
const styleTags = sheet.getStyleTags() // or sheet.getStyleElement()

也可以这样组件化包裹,只要在客户端不这么使用:

import { renderToString } from 'react-dom/server'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'const sheet = new ServerStyleSheet()
const html = renderToString(<StyleSheetManager sheet={sheet.instance}><YourApp /></StyleSheetManager>
)const styleTags = sheet.getStyleTags() // or sheet.getStyleElement()

sheet.getStyleTags()返回一个style标签数组。具体styled-components关于SSR更深入的操作,不在这里继续讨论了,还可以告知他兼容Next.js关于SSR的解决方案。

Referring to other components

styled-components提供了component selector组件选择器模式来代替我们以往对class名的依赖,解决得很干净。这下我们不必为命名和选择器冲突而苦恼了。

const Link = styled.a`display: flex;align-items: center;padding: 5px 10px;background: papayawhip;color: palevioletred;
`;const Icon = styled.svg`transition: fill 0.25s;width: 48px;height: 48px;${Link}:hover & {fill: rebeccapurple;}
`;const Label = styled.span`display: flex;align-items: center;line-height: 1.2;&::before {content: '◀';margin: 0 10px;}
`;render(<Link href="#"><Icon viewBox="0 0 20 20"><path d="M10 15h8c1 0 2-1 2-2V3c0-1-1-2-2-2H2C1 1 0 2 0 3v10c0 1 1 2 2 2h4v4l4-4zM5 7h2v2H5V7zm4 0h2v2H9V7zm4 0h2v2h-2V7z"/></Icon><Label>Hovering my parent changes my style!</Label></Link>
);

注意:

class A extends React.Component {render() {return <div />;}
}const B = styled.div`${A} {}
`;

这个例子是不可以的,因为A继承ReactComponent,不是被styled构造过的。我们的组件选择器只支持在Styled Components创建的样式组件。

class A extends React.Component {render() {return <div className={this.props.className} />;}
}const StyledA = styled(A)``;const B = styled.div`${StyledA} {}
`;

API文档

基本

  • styled
  • .attrs
  • ``字符模板
  • ThemeProvider

助手

  • css
  • keyframes
  • injectGlobal
  • isStyledComponent
  • withTheme

支持CSS

在样式组件中,我们支持所有CSS加嵌套。因为我们生成一个真实的stylesheet而不是内联样式,所以CSS中的任何工作都在样式组件中工作!

(&)被我们所生成的、唯一的类名替换给样式组件,使其具有复杂的逻辑变得容易。

支持flow和typescript

更多工具

Babel Plugin

Test Utilities

Jest Styled Components,基于jest,可对styled-components做单元测试

demo

Stylelint

使用stylelint 检查我们的styled-components样式书写规范。

Styled Theming 语法高亮显示

在模板文本中写入CSS时丢失的一个东西是语法高亮显示。我们正在努力在所有编辑器中实现正确的语法高亮显示。支持大部分编辑器包括Visual Studio Code、WebStorm。

总结

下面简单总结一下 styled-components 在开发中的表现:

  • 提出了 container 和 components 的概念,移除了组件和样式之间的映射关系,符合关注度分离的模式;
  • 可以在样式定义中直接引用到 js 变量,共享变量,非常便利,利用js的特性为css附能,帅毙了!
  • 支持组件之间继承,方便代码复用,提升可维护性;
  • 兼容现有的 className 方式,升级无痛;
  • 这下写CSS也乐趣十足了。
  • styled-components的最基本思想就是通过移除样式和组件之间的映射来执行最佳实践
  • 一个让styled-components很容易被接受的特性:当他被怀疑的时候,你同样可以使用你熟悉的方法去使用它!

当然,styled-components 还有一些优秀的特性,比如服务端渲染和 React Native 的支持。



题外:styled-components的魔法

如果你从来没看见过styled-components,下面是一个简单的样式组件的例子:

const Button = styled.button`background-color: papayawhip;border-radius: 3px;color: palevioletred;
`

现在可以像使用普通React组件一样渲染使用。

<Button>Hi Dad!</Button>

那么,这是怎么工作的呢?这个过程中到底发生了什么魔法?

标签模板

实际上, style.button` `是JavaScript的新语法特性,属于ES6的标签模板功能。

本质上, styled.buttonstyled.button()`是一样的。他们的差异只在传递参数时就变得可见了。

styled-components利用模板字符串的用处在于可以给内部props赋值。

const Button = styled.button`font-size: ${props => props.primary ? '2em' : '1em'};
`
// font-size: 2em;
<Button primary />

原文https://segmentfault.com/a/1190000014682665

Styled-Components相关推荐

  1. 我如何使用React,Redux-Saga和Styled Components构建NBA球员资料获取器

    by Jonathan Puc 乔纳森·普克(Jonathan Puc) 我如何使用React,Redux-Saga和Styled Components构建NBA球员资料获取器 (How I buil ...

  2. CSS Modules和Styled Components

    前言 由于 React的JSX语法,能在React中使用样式的方式有很多,本文主要介绍在React中经常使用CSS样式的五种方法: //1行内样式 // 2声明样式 // 3引入样式 // 4 CSS ...

  3. 使用 Styled Components 编写样式化组件

    本文已整理到 Github,地址

  4. CSS-in-JS的权衡

    by Oleg Isonen 由Oleg Isonen CSS-in-JS的权衡 (The tradeoffs of CSS-in-JS) Recently I wrote a higher leve ...

  5. material-ui_满足Material-UI —您最喜欢的新用户界面库

    material-ui by Code Realm 通过Code Realm 满足Material-UI -您最喜欢的新用户界面库 (Meet Material-UI - your new favor ...

  6. react 组件样式_如何使用样式化组件为React组件创建视觉变体

    react 组件样式 by Gilad Dayagi 通过吉拉德·达亚吉 如何使用样式化组件为React组件创建视觉变体 (How to create visual variants for Reac ...

  7. javascript_治愈JavaScript疲劳的研究计划

    javascript by Sacha Greif 由Sacha Greif 治愈JavaScript疲劳的研究计划 (A Study Plan To Cure JavaScript Fatigue) ...

  8. 技术天地 | CSS-in-JS:一个充满争议的技术方案

    导读 为了解决传统CSS在现代前端应用开发中遇到的痛点,FreeWheel评估了大量新一代的CSS框架/工具/方案.在本文中,作者以评估过程为线索,介绍了CSS-in-JS的背景.现状.开发特点和趋势 ...

  9. RN中布局样式的写法

    介绍原始写法 & 及其改进写法一 还有比较流行的 styled-components在RN中的使用 & 及其改进写法二 1.原写法 /*** 原写法*/ const styles1 = ...

  10. 中使用js修改变量值_谈一谈css-in-js在React项目中的使用

    一.什么是css-in-js 参考:[css in js 简介] 简单来说,传统的前端方案推崇"关注点分离"原则,HTML.CSS.JavaScript 应该各司其职,进行分离. ...

最新文章

  1. luogu P1231 教辅的组成(建图、拆点、最大流)
  2. 解决会声会影x7 x8打开即“已停止工作问题”
  3. 【Java基础】异常处理与输入输出流
  4. 论文浅尝 | AMUSE: 基于 RDF 数据的多语言问答语义解析方法
  5. 安卓获取手机路径方法
  6. ~~染色法判别二分图
  7. vxworks源码剖析- 数据结构篇一(双向链表)-转
  8. 【OpenCV】图像金字塔详解及编程实现
  9. Web UI设计师的CSS优化工具 25+
  10. SQL Server-【知识与实战IV】多表查询、个数计算、多重约束条件、两表间的交集、多表之间的关系分析
  11. python模块--hashlib
  12. leetcode记录-罗马数字转整数
  13. 用pod安装swiftyJson的一个实例
  14. iOS appicon 与 launchImage 尺寸一览
  15. Codeforces Round #622 (Div. 2) C2. Skyscrapers (hard version)(单调栈)
  16. 洛谷 P1427 小鱼的数字游戏
  17. Share:win10的日语输入法切换快捷键
  18. java 求正割_Java中AQS基本实现原理
  19. matlab仿真高尔顿正态分布源码,中心极限定理:从高尔顿板到麦克斯韦分布
  20. 惠普LaserJet M1216nfh MFP激光打印机开机提示“安装黑色碳粉盒”亮黄灯

热门文章

  1. C语言 最佳情侣身高差
  2. Openwrt Lede koolshare固件下屏蔽固定MAC地址以及屏蔽某些网站
  3. 【Verilog语法1】加载存储器$readmemh和$readmemb函数的使用
  4. Echarts 图表生成渐变色方法
  5. 丹佛斯变频器al13故障_丹佛斯变频器常见故障维修
  6. hbuildx打包 vue3项目成apk
  7. 极兔速递 一面面试题
  8. 【全网热点】打造全网最全爱心代码仓库【火速领取爱心】
  9. 定时脚本任务列子(crontab)
  10. TokenGazer 深度研究 | Synthetix:合成资产市场增长迅速 其死亡螺旋值得警惕