样式

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 来给样式添加字段,但是不是类型安全的。

RES
let 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 的例子:

RES
let container = Emotion.css({ "color": "#fff", "backgroundColor": "red" }) let app = <div className={container} />

你也可以用子模块来更轻松地组织你的样式:

RES
module 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 的函数创建绑定:

RES
let container = Emotion.rawCss(` color: #fff; background-color: red; `) let app = <div className={container} />

请记住,API 的类型安全程度有一个范围,需要在安全性和复杂度之间做权衡,所以请选择一个满足团队需求的解决方案。