☰
NativeWind 起步实操:用 className 替换 StyleSheet,在 React Native 中写出 Utility-First 代码
2026/9/27 9:48:12 网站建设 项目流程
  • 移动开发
  • 跨平台
  • 前端

【免费下载链接】nativewind

The utility-first workflow you love from Tailwind CSS in your React Native applications.

项目地址:https://gitcode.com/gh_mirrors/na/nativewind
点击查看免费下载

导读

本文是 NativeWind(在 React Native 应用中提供 Tailwind CSS 式 utility-first 工作流的库)的“开始写代码”实战指南。核心目标只有一个:把项目里StyleSheet.create的旧式样式迁移为className写法的 Tailwind 类名,并让这套写法在你的 Expo / React Native 工程中真正跑起来。读完本文,你将掌握两种接入方式(Babel 转换自动模式与styled()高阶组件手动模式)的完整配置与迁移步骤,并能对照仓库源码理解className底层是如何被编译为原生样式的。

迁移前后对比:这就是 NativeWind 的日常用法

官方文档(apps/website/docs/_start-coding.md)用一个最小改动示例,直接展示了从“原生 StyleSheet 风格”到“Tailwind 类名风格”的全部差异:

import { StatusBar } from 'expo-status-bar'; import React from 'react'; -import { StyleSheet, Text, View } from 'react-native'; +import { Text, View } from 'react-native'; export default function App() { return ( - <View style={styles.container}> + <View className="flex-1 items-center justify-center bg-white"> <Text>Open up App.js to start working on your app!</Text> <StatusBar style="auto" /> </View> ); } -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#fff', - alignItems: 'center', - justifyContent: 'center', - }, -});

这个 diff 包含了三个关键信息点:

  1. className是 NativeWind 的入口属性:flex-1、items-center、justify-center、bg-white都是标准的 Tailwind 工具类,语义与 Web 端完全一致。
  2. StyleSheet.create的容器样式被整段删除:原本需要 6 行声明式代码描述的四条样式(flex、背景色、两条对齐规则),现在压缩进一行类名。
  3. Text、View依然来自react-native:不需要引入额外的“魔法组件”,NativeWind 通过编译/包装层让原生组件直接理解className。

仓库中配套的官方示例工程也遵循同样的模式,例如 examples/expo-router/app/(tabs)/index.tsx/index.tsx) 这类文件里都直接使用了className而不是style。

两种接入模式:Babel 自动转换 vsstyled()手动包装

上面这段代码能否直接运行,取决于你的工程采用了哪一种 NativeWind 接入方式。文档给出了两种等价路径:

模式一:开启 Babel 转换(推荐,零侵入)

这是当前主流的接入方式。只要在 Babel 配置中挂载nativewind/babel预设(并把 JSX 的导入源切到nativewind),那么Text、View等内置组件的className就会在编译期被自动处理,不需要在业务代码里做任何包装。

官方文档 apps/website/docs/getting-started/react-native.mdx 中的 Expo SDK 50+ 配置如下:

module.exports = function (api) { api.cache(true); return { presets: [ ["babel-preset-expo", { jsxImportSource: "nativewind" }], "nativewind/babel", ], }; };

要点说明:

  • jsxImportSource: "nativewind"让 Babel 在编译 JSX 时把jsx/jsxDEV的导入来源切到nativewind的 jsx-runtime 与 jsx-dev-runtime(这两个包内都有对应的index.js/index.d.ts)。这是className能被运行时识别的前提。
  • nativewind/babel预设则负责把类名静态地转换为 NativeWind 的样式运行时调用,对应仓库根目录的 packages/nativewind/babel.js。
  • 如果是 framework-less 的 React Native 工程,配置更简单,只需在现有 presets 末尾追加一项:
module.exports = { - presets: ['<existing presets>'], + presets: ['<existing presets>', 'nativewind/babel'], };

模式二:使用styled()高阶组件(不启用 Babel 转换时)

如果你出于某种原因没有接入 Babel 转换(例如旧项目、受限的构建管线),文档提供了替代方案:用styled()高阶组件手动包装目标组件。官方文档 apps/website/docs/_start-coding-components.md 给出了完整示例:

import { StatusBar } from 'expo-status-bar'; import React from 'react'; -import { StyleSheet, Text, View } from 'react-native'; +import { Text, View as RNView } from 'react-native'; +import { styled } from 'nativewind'; +const View = styled(RNView) export default function App() { return ( - <View style={styles.container}> + <View className="flex-1 items-center justify-center bg-white"> <Text>Open up App.js to start working on your app!</Text> <StatusBar style="auto" /> </View> ); } -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#fff', - alignItems: 'center', - justifyContent: 'center', - }, -});

这段代码有两点值得注意:

  • 因为View被styled()重新定义,原导入语句需要给原生View起别名(View as RNView),避免命名冲突。
  • styled(RNView)返回一个支持className的增强组件。从源码结构看,styled的实现在 packages/react-native-css-interop/src/runtime/native/render-component.tsx 及同层的 unwrap-components.ts 中负责将类名解析为原生样式树,并由 packages/react-native-css-interop/src/runtime/native/styles.ts 等模块完成样式计算与注入。

两种模式最终达到的效果一致,区别只在“编译期自动做”还是“运行期手动包”。官方把前者作为默认推荐路径。

让className真正跑起来:完整前置配置清单

仅修改App.js还不够,className要生效,工程还必须具备以下四项基础配置(均可在仓库文档与示例工程中验证):

1. 安装 NativeWind 并初始化 Tailwind CSS

  • 通过npx tailwindcss init生成tailwind.config.js。
  • 必须把项目里所有用到className的源码文件路径写入content数组,否则 Tailwind 不会为这些文件生成对应的工具类。官方 quick-start 文档(apps/website/versioned_docs/version-v2/quick-starts/expo.md)中的示例:
// tailwind.config.js module.exports = { - content: [], + content: ["./App.{js,jsx,ts,tsx}", "./<custom directory>/**/*.{js,jsx,ts,tsx}"], theme: { extend: {}, }, plugins: [], }

其中<custom directory>需要替换为你的真实目录名(如screens、components)。

2. 配置 Metro,接入withNativeWind

在metro.config.js中引入nativewind/metro的withNativeWind,并指定全局 CSS 入口文件:

const { getDefaultConfig } = require("expo/metro-config"); const { withNativeWind } = require("nativewind/metro"); const config = getDefaultConfig(__dirname); module.exports = withNativeWind(config, { input: "./global.css" });

对应的实现位于 packages/nativewind/metro/index.ts,它会在 Metro 构建管线中注入 Tailwind 的处理逻辑,把global.css中的工具类编译成原生可消费的样式。

3. 创建并导入全局 CSS 文件

新建global.css(内容至少需要包含 Tailwind 指令,如@tailwind base;等),并在应用入口导入它:

import "./global.css" export default App() { /* Your App */ }

对于 Expo Router 工程,官方文档 apps/website/docs/getting-started/expo-router.mdx 要求在根布局app/_layout.js中导入:

import { Slot } from "expo-router"; // Import your global CSS file import "../global.css"; export default Slot;

4. Expo Web(可选)将 bundler 切换为 Metro

如果要在 Web 端使用,需要在app.json中显式声明使用 Metro bundler:

{ "expo": { "web": { "bundler": "metro" } } }

深入原理:className到原生样式的编译链

从仓库源码结构可以梳理出一条清晰的调用链,帮助你理解“一行类名”最终如何变成原生样式:

  1. 编译期(Babel):packages/react-native-css-interop/src/babel-plugin.ts 分析 JSX 中的className属性;而 packages/nativewind/babel.js 作为 NativeWind 侧的总入口负责将整个流程接入你的 Babel 管线。
  2. JSX 运行时:编译后的代码会引用 packages/nativewind/jsx-runtime / jsx-dev-runtime 提供的工厂函数,它们内部调用 packages/react-native-css-interop/src/runtime/wrap-jsx.ts 对组件进行包装。
  3. 运行时解析:包装层最终把类名字符串交给样式运行时解析为 React Native 的原生style对象——核心逻辑集中在 packages/react-native-css-interop/src/runtime/native 目录(styles.ts、stylesheet.ts、resolve-value.ts等),并最终通过 packages/nativewind/src/stylesheet.ts 对外暴露的NativeWindStyleSheet完成注册与输出。

仓库在 packages/nativewind/src/tests下提供了大量按 CSS 模块组织的快照测试(如flexbox-grid.tsx、sizing.tsx、spacing.tsx、typography.tsx等),它们直接以“在 JSX 中写className”的方式断言编译产物,是验证上述迁移写法正确性的第一手参考。

常见注意点

  • style与className可以共存:迁移阶段不必一次性删除所有style,NativeWind 允许在组件上同时保留两者,便于渐进式改造。
  • Web 端的额外要求:如文档所述,在 Web 上 NativeWind 是 Tailwind CSS 与 React Native 之间的兼容层,需要额外保证 NativeWind 被正确转译(例如通过@expo/webpack-config的dangerouslyAddModulePathsToTranspile: ["nativewind"])。
  • TypeScript 工程:如果需要完整的类型提示,官方建议参照 apps/website/docs/getting-started/typescript.md 配置nativewind-env.d.ts与tsconfig(仓库中的示例工程 examples/expo-router/nativewind-env.d.ts 可供对照)。

小结

从StyleSheet.create到className,表面上是书写方式的简化,底层则是 Babel 编译期 + JSX 运行时 + 样式运行时三段式架构的协同。按本文顺序完成安装、Tailwind 配置、Babel 预设、Metro 接入与全局 CSS 导入后,你便可以在任意组件里直接书写flex-1、bg-white、items-center这类类名,以 Tailwind 的 utility-first 心智模型编写 React Native 界面。

  • 移动开发
  • 跨平台
  • 前端

【免费下载链接】nativewind

The utility-first workflow you love from Tailwind CSS in your React Native applications.

项目地址:https://gitcode.com/gh_mirrors/na/nativewind
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询