样式
React 为内联样式提供了内置支持,但也有许多用于编写 React 组件样式的第三方库。你可能会喜欢这些方案:
Global CSS / CSS modules
CSS 工具库(
tailwindcss
,tachyons
,bootstrap
等)CSS-in-JS(
styled-components
,emotion
等)
ReScript 也能兼容这些库。下面几节,我们介绍了如何使用流行的样式库。如果你想要使用没提到的库,可以搜索 package index 或在 ReScript论坛 发帖询问。
内联样式
从90年代开始,内联样式就是样式的最基本形式。你能通过 ReactDOM.Style.make
API 来为任何 DOM 元素提供一个 style
属性:
RES<div style={ReactDOM.Style.make(~color="#444444", ~fontSize="68px", ())} />
它是一个使用 标签参数 的函数调用(因此是类型化的),参数映射到我们熟悉的 CSS 样式对象 {color: '#444444', fontSize: '68px'}
。对于 CSS 规范中的每个 CSS 属性,在 make
函数中都有一个驼峰命名的标签参数。
注意: make
返回一个不透明的 ReactDOM.Style.t
类型。我们还提供一个 ReactDOM.Style.combine
接口,来组合两个 style
。
逃生通道:unsafeAddProp
上面的 Style.make
API 会安全地检查每个样式字段的类型!但我们可能忽略了一些极少使用的字段。如果是这样,类型系统将会提示你尝试添加的字段不存在。为了提供补救措施,我们提供了 ReactDOM.Style.unsafeAddProp
来给样式添加字段,但是不是类型安全的。
RESlet style =
ReactDOM.Style.make(
~color="red",
~padding="10px",
(),
)->ReactDOM.Style.unsafeAddProp("-webkit-animation-name", "moveit")
Global CSS
使用 %%raw
表达式在 ReScript / React 组件代码中导入 CSS 文件:
RESCRIPT// in a CommonJS setup
%%raw("require('./styles/main.css')")
// or with ES6
%%raw("import './styles/main.css'")
CSS Modules
CSS modules 能像 JS 模块一样被导入。导入的是一个 JS 对象,属性名和 CSS 文件的类名相同。
例如,我们有一个像这样的 CSS module:
CSS/* styles.module.css */
.root {
color: red
}
为了将 CSS module 作为 JS 对象导入,我们需要新建一个 module 的绑定:
RES// {..} means we are handling a JS object with an unknown
// set of attributes
@module external styles: {..} = "./styles.module.css"
// Use the obj["key"] syntax to access any classname within our object
let app = <div className={styles["root"]} />
注意: {..}
是一个开放的 JS 对象类型,所以类型检查器不会检查是否正确使用类名。如果你想要强制让编译器报错,将 {..}
替换为具体的 JS 对象类型,例如 {"root": string}
。
CSS 工具库
Tailwind
TailwindCSS 类似的 CSS 工具库通常需要一些全局导入的 CSS。
首先,新建 Tailwind CSS 的主入口文件:
CSS/* main.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
然后,在你的 ReScript / React 应用中导入 main.css
文件:
RES// src/App.res
%%raw("import './main.css'")
利用 ReScript 的模式匹配和字符串插值来组合不同的 CSS 类名:
RES@react.component
let make = (~active: bool) => {
let activeClass = if active {
"text-green-600"
}
else {
"text-red-600"
}
<div className={`border-1 border-black ${activeClass}`}>
{React.string("Hello World")}
</div>
}
提示: rescript-lang.org
用了 Tailwind CSS!查看我们的 代码库 来获得一些使用灵感。
CSS-in-JS
我们无法推荐一个权威的 CSS-in-JS 工作流,因为绑定到 CSS-in-JS 库有很多不同的方法(从简单到非常高级)。
为了演示,让我们创建一些简单的 emotion
绑定 (像 这里 提到的):
RES// src/Emotion.res
@module("@emotion/css") external css: {..} => string = "css"
@module("@emotion/css") external rawCss: string => string = "css"
@module("@emotion/css") external keyframes: {..} => string = "css"
@module("@emotion/css") external cx: array<string> => string = "cx"
@module("@emotion/css") external injectGlobal: string => unit = "injectGlobal"
这会让你直接访问 emotion
的 API。下面是使用 emotion
的例子:
RESlet container = Emotion.css({
"color": "#fff",
"backgroundColor": "red"
})
let app = <div className={container} />
你也可以用子模块来更轻松地组织你的样式:
RESmodule Styles = {
open Emotion
let container = css({
"color": "#fff",
"backgroundColor": "red"
})
// your other declarations
}
let app = <div className={Styles.container} />
请注意,这种方法不会检查 CSS 属性名是否有效。如果你想要确保只传入有效的 CSS 属性,你可以像这样定义你的 css
函数:
RES@module("@emotion/css") external css: ReactDOM.Style.t => string = "css"
// Usage is slightly different (and probably less ergonomic)
let container = ReactDOM.Style.make(~padding="20px", ())->css;
let app = <div
className={container}
/>
这里我们使用现有的 React.Style.t
类型来强制确保只传入有效的 CSS 属性名。
最后但同样重要的是,你也能为直接使用原生 CSS 的函数创建绑定:
RESlet container = Emotion.rawCss(`
color: #fff;
background-color: red;
`)
let app = <div className={container} />
请记住,API 的类型安全程度有一个范围,需要在安全性和复杂度之间做权衡,所以请选择一个满足团队需求的解决方案。