基本概念

glamorous的简要介绍

启发

为什么会有glaomrous这个项目

The Problem

你喜欢CSS in JS的概念,但是你不喜欢只是为了给组件加样式就给每个组件都写一个包装的函数。你也不喜欢给一个没有任何逻辑的简单组件起个名字。而且,创建样式(这里指代表样式的类名),className的传递。属性的转发等等的琐碎之事也很让人头疼。

比如说,下面就是你用纯glamor(或者aphrodite或者有类似功能的库)需要写的代码:

const styles = glamor.css({
  fontSize: 20,
  textAlign: 'center',
})
function MyStyledDiv({className = '', ...rest}) {
  return (
    <div
      className={`${styles} ${className}`}
      {...rest}
    />
  )
}

This Solution

通过使用glamorous,上面的例子就可以写成下面这样简单的形式:

const MyStyledDiv = glamorous.div({
  fontSize: 20,
  textAlign: 'center',
})

实际上,这种方案更好,因为很多用来组合这些组件的特性都棒极了! 恩...如果你觉得给不给MyStyledDiv起名字并不重要,用个div标签给点样式就行了。那么可以直接这么做:

const { Div } = glamorous

function App() {
  return (
    <Div
      fontSize={20}
      textAlign="center"
    >
      Hello world!
    </Div>
  )
}

点击这里在你的浏览器中尝试一下。

所以,这就是这种解决方案的基本部分了!接下来让我们了解更多的细节吧!

Installation

这个模块是通过node打包然后用npm发布的,使用时作为你的项目的依赖之一安装。

npm install --save glamorous

glamorous依赖reactglamor,所以如果之前没有安装这些模块的话,你还得在你的项目里面安装他们。

npm install --save react glamor

备注:如果你使用的React版本在15.5或者更高,你还需要安装prop-typesnpm install --save prop-types

你可以使用下面任何一种模块格式

  • main: dist/glamorous.cjs.js - 通过CommonJS模块的形式导出
  • global: dist/glamorous.umd.js and dist/glamorous.umd.min.js - 通过umd模块的形式导出,可以在几种环境中使用,最常见的是作为全局变量使用
  • jsnext:main and module: dist/glamorous.es.js - exports itself using the ES modules specification, you'll need to configure webpack to make use of this file do this using the resolve.mainFields property.
  • jsnext:main and module: dist/glamorous.es.js - 通过ES模块规范导出,如果要通过这种形式使用的话你需要在webpack中配置resolve.mainFields属性

不过最常见的场景还是通过CommonJS使用此模块:

const glamorous = require('glamorous')
const {ThemeProvider} = glamorous

如果你在使用转换工具(同时/或者使用jsnext:main)

import glamorous, {ThemeProvider} from 'glamorous'

// 你还可以导入特定的Glamorous组件(详见"内置"组件部分)
import {Div, H2} from 'glamorous'

// 和JavaScript内置对象名字相同的标签可以通过Tag后缀导入
// 包含短横线-的标签会被转换成驼峰风格
import {MapTag, ColorProfile} from 'glamorous'

如果你想以全局变量的形式使用:

<!-- 加载资源 -->
<script src="https://unpkg.com/react/dist/react.js"></script>
<script src="https://unpkg.com/prop-types/prop-types.js"></script>
<script src="https://unpkg.com/glamor/umd/index.js"></script>
<!-- 加载glamorous-->
<script src="https://unpkg.com/glamorous/dist/glamorous.umd.js"></script>
<script>
// 在这里使用window.glamorous
const glamorous = window.glamorous
const {ThemeProvider} = glamorous
</script>

起步

开始使用glamorous

Video Screenshot

这是一个最简单的例子,用来给你展示如何用css-in-js的魔法编写组件。

const { Div } = glamorous

function App() {
	return (
		<Div
			fontSize={20}
			textAlign="center"
		>
			Hello Glamorous!
		</Div>
	)
}

render(<App/>)
Hello Glamorous!

核心概念

glamorous

glamorous函数是默认的导出。它可以创建一个glamorous组件,该组件可以把样式渲染到你所给的React组件。这是通过把一个className属性转发给你传入的React组件完成的。 在我们学习如何包装自定义组件之前,我们先来聊一聊内置的DOM元素。

内置的DOM组件工厂

对于每一个DOM元素,都有一个对应的glamorous组件工厂绑定在glamorous函数上。如上所述,你可以通过诸如:glamorous.divglamorous.aglamorous.article之类的形式调用这些工厂函数。

const MyStyledSection = glamorous.section({ margin: 1 })

<MyStyledSection>content</MyStyledSection>
// 渲染输出:<section class="<glamor-generated-class>">content</section>
// 应用的样式: {margin: 1}

GlamorousComponent

GlamorousCompoennt就是从glamorousComponentFactory返回的东西。他的工作就是收集所有的样式然后从glamor)产生一个className并把这个className转交给你的React组件。

内置的 GlamorousComponents

有时候你只是单纯的想写点样式而不关心组件叫什么名字(因为起名字是一件麻烦事)。所以glamorous给所有类型的DOM节点都提供了一个GlamorousComponent,这样会让你更方便。

const { Div, Span, A, Img } = glamorous

function MyUserInterface({name, tagline, imageUrl, homepage, size}) {
  const nameSize = size
  const taglineSize = size * 0.5
  return (
    <Div display="flex" flexDirection="column" justifyContent="center">
      <A href={homepage} textDecoration="underline" color="#336479">
        <Img borderRadius="50%" height={180} src={imageUrl} />
        <Div fontSize={nameSize} fontWeight="bold">{name}</Div>
      </A>
      <Span fontSize={taglineSize} color="#767676">
        {tagline}
      </Span>
    </Div>
  )
}

点击这里在你的浏览器中尝试一下

给每个组件命名是一个枯燥乏味的事情,所以这些预置的组件是很有用的!另一个很方便的地方就是你可以把样式当做属性传入。从上面的例子可以注意到,glamorous可以区分传入的属性是样式还是有语义的(比如对于ImgA组件srchref属性就有特殊意义)。

css属性可以用来传入样式对象。

import glamorous, {withTheme} from 'glamorous'
const { Div, Span } = glamorous

const predefinedStyle = {
  color: '#767676',
  fontSize: 18,
}

const MyUserInterface = withTheme(function ({tagline, theme}) {
  return (
    <Div
      css={{
        display: 'flex',
        flexDirection: 'column',
        justifyContent: 'center',
        [theme.mq.tablet]: {
          flexDirection: 'row'
        }
      }}
     >
      <Span css={predefinedStyle}>
        {tagline}
      </Span>
    </Div>
  )
})

还有一个小技巧...下面的写法也是没问题的:

<glamorous.Div color="blue">
  JSX is pretty wild!
</glamorous.Div>

重写组件样式

使用属性最常见的场景就是重写现有的组件样式(通过glammorous或者其他的方式生成)。结合glamorous()函数使用className,css,和theme或者简单的组件组合来实现重写。

如果你对使用theme属性感兴趣的话可以看一看主题章节。在这个章节我们先来解释一下怎么使用className,css,和组件组合的方式来重写一个组件的样式。

让我们一起来看看下面的例子吧。

点击这里在你的浏览器中尝试一下

我们会用下面的这个组件作为演示用的GlamorousComponent

const MyStyledDiv = glamorous.div({margin: 1, fontSize: 1, padding: 1})
使用className

对于你提供的每个classNameGlamorousComponent都会检查它是否是一个由glamor生成的className(可以从glamor或者glamorous来,这并不重要)。如果是的话,就把产生这个className的原始样式和已经渲染的样式合并,冲突的属性会优先保留重写样式中的。

对于不是glamor生成的classNames,就会和glamor生成的连接起来。

const myCustomGlamorStyles = glamor.css({fontSize: 2})
<MyStyledDiv className={`${myCustomGlamorStyles} custom-class`} />
// 应用的样式
// {margin: 1, fontSize: 2, padding: 1}
// 和由类名custom-class提供的其他样式
使用css

这个预先设置的样式是一样的(比如在glamorous.div(...styles)中传入的),如果使用了该属性的话,传入的样式在合并是有最高的优先级。

const myCustomGlamorStyles = glamor.css({fontSize: 2, padding: 2})
<MyStyledDiv
  className={`${myCustomGlamorStyles} custom-class`}
  css={{padding: 3}}
/>
// 应用的样式
// {margin: 1, fontSize: 2, padding: 3}
// 和由类名custom-class提供的其他样式
使用glamorous()组合

如果我们想扩展现存的组件样式的话,可以通过glamorous()方法实现。

const MyComposedStyledDiv = glamorous(MyStyledDiv)({fontSize: 4, padding: 4})
<MyComposedStyledDiv />
// 应用的样式
// {margin: 1, fontSize: 4, padding: 4}

实际上,内置的DOM组件工厂只是提供了glamorous()函数的抽象,所以glamorous.divglamorous('div')两种写法的的效果是一样的。

动态样式和静态样式

glamorous的一个不错的好处就是他可以让你很明确的区分动态和静态样式,因为动态样式是用 函数的形式写的,静态样式是用对象字面量的形式写的。下面是一个同时使用动态样式和静态样式的例子:

const MyLink = glamorous.a(
  {
    color: 'blue',
    textDecoration: 'none',
  },
  ({size = 'small'}) => ({
    fontSize: size === 'big' ? 24 : 16,
  }),
  // 你可以继续提供任意数量的参数
  // `glamor`会把他们一起合并
  // 当样式冲突的时候,最后传入的会保留。
)

render(
  <div>
    <MyLink href="#">Default is small</MyLink>
    <br />
    <MyLink href="#" size="big">size="big"</MyLink>
  </div>
)

你可以在codesandbox中动态预览这个例子。

可以使用数组来合并样式

动画

要用glamorous做动画的话,简单的可以用CSS transition做,如果是复杂的工作你可以通过glamorcss.keyframesAPI用keyframes做。

// import * as glamor from 'glamor'

// 定义动画的样式
const animationStyles = props => {
	const bounce = glamor.css.keyframes({
		'0%': { transform: `scale(1.01)` },
		'100%': { transform: `scale(0.99)` }
	})
	return {animation: `${bounce} 0.2s infinite ease-in-out alternate`}
}

// 定义组件
const AnimatedDiv = glamorous.div(animationStyles)

render(
	<AnimatedDiv>
		Bounce.
	</AnimatedDiv>
)
Bounce.
CodeSandbox

glamorous-native

React Native

glamorous为React Native项目提供了一个特殊版本叫glamorous-native

npm install glamorous-native --save

你可以在glamorous-native项目了解更多。

贡献者:

liadbiz's GitHub avatar