| @@ -40,19 +40,6 @@ npx tailwindcss init | |||
| 40 | 40 | ||
| 41 | ### **在你的 tailwind.config.js 文件中添加所有组件文件的路径。** | 41 | ### **在你的 tailwind.config.js 文件中添加所有组件文件的路径。** |
| 42 | 42 | ||
| 43 | -- V2.0.11 | ||
| 44 | - | ||
| 45 | -```diff | ||
| 46 | -module.exports = { | ||
| 47 | -- content: [], | ||
| 48 | -+ content: ["./App.{js,jsx,ts,tsx}", "./screens/**/*.{js,jsx,ts,tsx}"], | ||
| 49 | - theme: { | ||
| 50 | - extend: {}, | ||
| 51 | - }, | ||
| 52 | - plugins: [], | ||
| 53 | -} | ||
| 54 | -``` | ||
| 55 | - | ||
| 56 | - V4.1.24 | 43 | - V4.1.24 |
| 57 | 44 | ||
| 58 | ```diff | 45 | ```diff |
| @@ -69,15 +56,6 @@ module.exports = { | |||
| 69 | 56 | ||
| 70 | ### **修改你的 babel.config.js** | 57 | ### **修改你的 babel.config.js** |
| 71 | 58 | ||
| 72 | -- V2.0.11 | ||
| 73 | - | ||
| 74 | -```diff | ||
| 75 | -module.exports = { | ||
| 76 | -- plugins: [], | ||
| 77 | -+ plugins: ["@react-native-oh-tpl/nativewind/babel"], | ||
| 78 | -}; | ||
| 79 | -``` | ||
| 80 | - | ||
| 81 | - V4.1.24 | 59 | - V4.1.24 |
| 82 | 60 | ||
| 83 | ```diff | 61 | ```diff |
| @@ -124,28 +102,6 @@ const config = getDefaultConfig(__dirname) | |||
| 124 | 102 | ||
| 125 | > [!WARNING] 使用时 import 的库名不变。 | 103 | > [!WARNING] 使用时 import 的库名不变。 |
| 126 | 104 | ||
| 127 | -- V2.0.11 | ||
| 128 | - | ||
| 129 | -```jsx | ||
| 130 | -import React from 'react'; | ||
| 131 | -import { Text, View } from 'react-native'; | ||
| 132 | -import { styled } from 'nativewind'; | ||
| 133 | - | ||
| 134 | -const StyledView = styled(View) | ||
| 135 | -const StyledText = styled(Text) | ||
| 136 | -const NativewindDemo = () => { | ||
| 137 | - return ( | ||
| 138 | - <StyledView className="flex-1 items-center justify-center"> | ||
| 139 | - <StyledText className="font-bold"> | ||
| 140 | - Hello,World. | ||
| 141 | - </StyledText> | ||
| 142 | - </StyledView> | ||
| 143 | - ) | ||
| 144 | -} | ||
| 145 | - | ||
| 146 | -export default NativewindDemo | ||
| 147 | -``` | ||
| 148 | - | ||
| 149 | - V4.1.24<sup>+</sup> | 105 | - V4.1.24<sup>+</sup> |
| 150 | 106 | ||
| 151 | ```jsx | 107 | ```jsx |
| @@ -175,16 +131,16 @@ export default function App() { | |||
| 175 | 131 | ||
| 176 | | Name | Description | Type | Required | Platform | HarmonyOS Support | | 132 | | Name | Description | Type | Required | Platform | HarmonyOS Support | |
| 177 | | -------------------------------- | ------------------------------------------------------------ | --------- | -------- | -------- | ----------------- | | 133 | | -------------------------------- | ------------------------------------------------------------ | --------- | -------- | -------- | ----------------- | |
| 178 | -| withNativeWind<sup>4.1.24+</sup> | `withNativeWind` 是一个高阶组件,用于更新你的 Metro 配置文件,以提供 NativeWind 框架支持。 | component | no | All | yes | | 134 | +| withNativeWind | `withNativeWind` 是一个高阶组件,用于更新你的 Metro 配置文件,以提供 NativeWind 框架支持。 | component | no | All | yes | |
| 179 | -| remapProps<sup>4.1.24+</sup> | NativeWind 提供了 `remapProps` 工具函数,简化对带有多个 `style` 属性的第三方组件的开发操作。 | component | no | All | yes | | 135 | +| remapProps | NativeWind 提供了 `remapProps` 工具函数,简化对带有多个 `style` 属性的第三方组件的开发操作。 | component | no | All | yes | |
| 180 | -| cssInterop<sup>4.1.24+</sup> | 该函数会给组件打上标记,这样组件渲染时,运行时就能识别并将组件的 `className` 字符串转换为样式。 | component | no | All | yes | | 136 | +| cssInterop | 该函数会给组件打上标记,这样组件渲染时,运行时就能识别并将组件的 `className` 字符串转换为样式。 | component | no | All | yes | |
| 181 | 137 | ||
| 182 | ### API | 138 | ### API |
| 183 | 139 | ||
| 184 | | Name | Description | Type | Required | Platform | HarmonyOS Support | | 140 | | Name | Description | Type | Required | Platform | HarmonyOS Support | |
| 185 | | ----------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -------- | -------- | ----------------- | | 141 | | ----------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -------- | -------- | ----------------- | |
| 186 | | useColorScheme() | 用于获取设备的配色方案信息。 | () =>{colorScheme,setColorScheme} | no | All | yes | | 142 | | useColorScheme() | 用于获取设备的配色方案信息。 | () =>{colorScheme,setColorScheme} | no | All | yes | |
| 187 | -| var<sup>4.1.24+</sup> | `vars` 是一个函数,接收一组 CSS 变量键值对象作为参数,并返回一个可在 React Native 组件中使用的样式对象。 | function | no | All | yes | | 143 | +| vars | `vars` 是一个函数,接收一组 CSS 变量键值对象作为参数,并返回一个可在 React Native 组件中使用的样式对象。 | function | no | All | yes | |
| 188 | 144 | ||
| 189 | ## 遗留问题 | 145 | ## 遗留问题 |
| 190 | 146 | ||
| @@ -40,19 +40,6 @@ npx tailwindcss init | |||
| 40 | 40 | ||
| 41 | ### **Add the file paths of all components to your `tailwind.config.js` file** | 41 | ### **Add the file paths of all components to your `tailwind.config.js` file** |
| 42 | 42 | ||
| 43 | -- V2.0.11 | ||
| 44 | - | ||
| 45 | -```diff | ||
| 46 | -module.exports = { | ||
| 47 | -- content: [], | ||
| 48 | -+ content: ["./App.{js,jsx,ts,tsx}", "./screens/**/*.{js,jsx,ts,tsx}"], | ||
| 49 | - theme: { | ||
| 50 | - extend: {}, | ||
| 51 | - }, | ||
| 52 | - plugins: [], | ||
| 53 | -} | ||
| 54 | -``` | ||
| 55 | - | ||
| 56 | - V4.1.24 | 43 | - V4.1.24 |
| 57 | 44 | ||
| 58 | ```diff | 45 | ```diff |
| @@ -69,15 +56,6 @@ module.exports = { | |||
| 69 | 56 | ||
| 70 | ### **Modify your `babel.config.js`** | 57 | ### **Modify your `babel.config.js`** |
| 71 | 58 | ||
| 72 | -- V2.0.11 | ||
| 73 | - | ||
| 74 | -```diff | ||
| 75 | -module.exports = { | ||
| 76 | -- plugins: [], | ||
| 77 | -+ plugins: ["@react-native-oh-tpl/nativewind/babel"], | ||
| 78 | -}; | ||
| 79 | -``` | ||
| 80 | - | ||
| 81 | - V4.1.24 | 59 | - V4.1.24 |
| 82 | 60 | ||
| 83 | ```diff | 61 | ```diff |
| @@ -124,28 +102,6 @@ The following code shows the basic use scenario of the repository: | |||
| 124 | 102 | ||
| 125 | > [!WARNING] The name of the imported repository remains unchanged. | 103 | > [!WARNING] The name of the imported repository remains unchanged. |
| 126 | 104 | ||
| 127 | -- V2.0.11 | ||
| 128 | - | ||
| 129 | -```jsx | ||
| 130 | -import React from 'react'; | ||
| 131 | -import { Text, View } from 'react-native'; | ||
| 132 | -import { styled } from 'nativewind'; | ||
| 133 | - | ||
| 134 | -const StyledView = styled(View) | ||
| 135 | -const StyledText = styled(Text) | ||
| 136 | -const NativewindDemo = () => { | ||
| 137 | - return ( | ||
| 138 | - <StyledView className="flex-1 items-center justify-center"> | ||
| 139 | - <StyledText className="font-bold"> | ||
| 140 | - Hello,World. | ||
| 141 | - </StyledText> | ||
| 142 | - </StyledView> | ||
| 143 | - ) | ||
| 144 | -} | ||
| 145 | - | ||
| 146 | -export default NativewindDemo | ||
| 147 | -``` | ||
| 148 | - | ||
| 149 | - V4.1.24<sup>+</sup> | 105 | - V4.1.24<sup>+</sup> |
| 150 | 106 | ||
| 151 | ```jsx | 107 | ```jsx |
| @@ -175,16 +131,16 @@ export default function App() { | |||
| 175 | 131 | ||
| 176 | | Name | Description | Type | Required | Platform | HarmonyOS Support | | 132 | | Name | Description | Type | Required | Platform | HarmonyOS Support | |
| 177 | | -------------------------------- | ------------------------------------------------------------ | --------- | -------- | -------- | ----------------- | | 133 | | -------------------------------- | ------------------------------------------------------------ | --------- | -------- | -------- | ----------------- | |
| 178 | -| withNativeWind<sup>4.1.24+</sup> | withNativeWind is a higher order component that updates your Metro configuration to support NativeWind | component | no | All | yes | | 134 | +| withNativeWind | withNativeWind is a higher order component that updates your Metro configuration to support NativeWind | component | no | All | yes | |
| 179 | -| remapProps<sup>4.1.24+</sup> | Nativewind provides the remapProps utility to simplify working with third-party components with multiple "style" props. | component | no | All | yes | | 135 | +| remapProps | Nativewind provides the remapProps utility to simplify working with third-party components with multiple "style" props. | component | no | All | yes | |
| 180 | -| cssInterop<sup>4.1.24+</sup> | This function "tags" components so that when its rendered, the runtime will know to resolve component className strings into styles. | component | no | All | yes | | 136 | +| cssInterop | This function "tags" components so that when its rendered, the runtime will know to resolve component className strings into styles. | component | no | All | yes | |
| 181 | 137 | ||
| 182 | ### API | 138 | ### API |
| 183 | 139 | ||
| 184 | | Name | Description | Type | Required | Platform | HarmonyOS Support | | 140 | | Name | Description | Type | Required | Platform | HarmonyOS Support | |
| 185 | | ----------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -------- | -------- | ----------------- | | 141 | | ----------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -------- | -------- | ----------------- | |
| 186 | | useColorScheme() | provides access to the devices color scheme. | () =>{colorScheme,setColorScheme} | no | All | yes | | 142 | | useColorScheme() | provides access to the devices color scheme. | () =>{colorScheme,setColorScheme} | no | All | yes | |
| 187 | -| var<sup>4.1.24+</sup> | vars is a function that takes a dictionary of CSS variables and returns a style object that can be used in React Native components. | function | no | All | yes | | 143 | +| vars | vars is a function that takes a dictionary of CSS variables and returns a style object that can be used in React Native components. | function | no | All | yes | |
| 188 | 144 | ||
| 189 | ## Known Issues | 145 | ## Known Issues |
| 190 | 146 | ||
| @@ -13,13 +13,6 @@ | |||
| 13 | "deliveryWithInstall": true, | 13 | "deliveryWithInstall": true, |
| 14 | "installationFree": false, | 14 | "installationFree": false, |
| 15 | "pages": "$profile:main_pages", | 15 | "pages": "$profile:main_pages", |
| 16 | - // below property is supported from 5.0.0 - it is needed by bundleManager.canOpenLink to check if the app can open some url | ||
| 17 | - // "querySchemes": ["maps", "http", "https", "customDomain"], | ||
| 18 | - "requestPermissions": [ | ||
| 19 | - { | ||
| 20 | - "name": "ohos.permission.INTERNET" | ||
| 21 | - }, | ||
| 22 | - ], | ||
| 23 | "metadata": [ | 16 | "metadata": [ |
| 24 | { | 17 | { |
| 25 | "name": "OPTLazyForEach", | 18 | "name": "OPTLazyForEach", |
| @@ -0,0 +1,3 @@ | |||
| 1 | +{ | ||
| 2 | + "presets": ["@react-native-ohos/nativewind/babel"] | ||
【AI-Review】【建议】【软件设计】【冗余重复代码】.babelrc 与 babel.config.js 配置重复 ● 问题: example/src/.babelrc(本 PR 新增)配置 ● 影响: 建议。nativewind babel 插件(packages/react-native-css-interop/src/babel-plugin.ts)将 ● 建议: 删除 example/src/.babelrc,统一由 example/babel.config.js 管理构建配置。 ![]() ![]() | |||
| 3 | +} | ||
| @@ -1,18 +1,170 @@ | |||
| 1 | -/* | 1 | +import "../../../global.css" |
【AI-Review】【致命】【基础代码问题】【代码逻辑错误】global.css 导入路径错误导致构建失败 ● 问题: example/src/index.tsx 第1行 ● 影响: 致命。Metro 解析器(packages/react-native-css-interop/src/metro/index.ts 的 resolveRequest)先调用原始 resolver 解析导入路径,再比对 options.input。由于 ● 建议: 将导入路径改回
![]() ![]() | |||
| 2 | - * Copyright (c) 2025 Huawei Device Co., Ltd. All rights reserved | 2 | +import { Text, View, ScrollView, Button, TextInput, TouchableOpacity, StyleSheet } from 'react-native'; |
| 3 | - * Use of this source code is governed by a MIT license that can be | 3 | +import { vars, useColorScheme, remapProps, cssInterop } from 'nativewind'; |
| 4 | - * found in the LICENSE file. | 4 | +import { Tester, TestCase, TestSuite } from '@rnoh/testerino'; |
| 5 | - */ | ||
| 6 | 5 | ||
| 7 | -import "../global.css" | ||
| 8 | -import { Text, View } from "react-native"; | ||
| 9 | - | ||
| 10 | export default function App() { | 6 | export default function App() { |
| 11 | - return ( | 7 | + |
| 12 | - <View className="flex-1 items-center justify-center bg-white"> | 8 | + const { colorScheme, setColorScheme } = useColorScheme(); |
| 13 | - <Text className="text-xl font-bold text-blue-500"> | 9 | + const theme = vars({ |
| 14 | - Welcome to Nativewind! | 10 | + "--theme-fg": "green", |
| 15 | - </Text> | 11 | + }); |
| 16 | - </View> | 12 | + type ThirdPartyButtonProps = { |
| 13 | + buttonStyle?: any; | ||
| 14 | + labelStyle?: any; | ||
| 15 | + children?: React.ReactNode; | ||
| 16 | + [key: string]: any; | ||
| 17 | + }; | ||
| 18 | + const ThirdPartyButton = ({ buttonStyle, labelStyle, children, ...props }: ThirdPartyButtonProps) => ( | ||
| 19 | + <TouchableOpacity style={buttonStyle} {...props}> | ||
| 20 | + <Text style={labelStyle}>{children}</Text> | ||
| 21 | + </TouchableOpacity> | ||
| 17 | ); | 22 | ); |
| 18 | -} | 23 | + const CustomizedButton = remapProps(ThirdPartyButton, { |
| 24 | + buttonClass: "buttonStyle", | ||
| 25 | + labelClass: "labelStyle", | ||
| 26 | + }); | ||
| 27 | + const MyInput = (props) => <TextInput {...props} />; | ||
| 28 | + const OverridenTextInput = remapProps(MyInput, { className: "style" }); | ||
【AI-Review】【严重】【基础代码问题】【代码逻辑错误】组件与 cssInterop 在 App 函数体内定义导致重渲染时状态丢失 ● 问题: example/src/index.tsx 第18-28行将 ● 影响: 严重。用户在 ● 建议: 将组件定义、remapProps/cssInterop 调用移到模块作用域(App 函数外部):
![]() ![]() | |||
| 29 | + | ||
| 30 | + cssInterop(TextInput, { | ||
| 31 | + className: { | ||
| 32 | + target: "style", | ||
| 33 | + nativeStyleToProp: { | ||
| 34 | + textAlign: true, | ||
| 35 | + }, | ||
| 36 | + }, | ||
| 37 | + placeholderClassName: { | ||
| 38 | + target: false, | ||
| 39 | + nativeStyleToProp: { | ||
| 40 | + color: "placeholderTextColor", | ||
| 41 | + }, | ||
| 42 | + } | ||
| 43 | + }); | ||
| 44 | + | ||
| 45 | + cssInterop(Tester, { | ||
| 46 | + className: { | ||
| 47 | + target: "style", | ||
| 48 | + nativeStyleToProp: { | ||
| 49 | + textAlign: true, | ||
| 50 | + }, | ||
| 51 | + } | ||
| 52 | + }); | ||
| 53 | + return ( | ||
| 54 | + <Tester className="flex-1 bg-black pt-20"> | ||
| 55 | + <ScrollView> | ||
| 56 | + <Text className="text-4xl text-white font-bold">withNativeWind桥接</Text> | ||
| 57 | + <TestSuite name='hooks:withNativeWind()'> | ||
| 58 | + <TestCase itShould='withNativeWind()'> | ||
| 59 | + <View> | ||
| 60 | + <TextInput | ||
| 61 | + className="h-12 w-full p-2.5 border border-red-300 rounded-md | ||
| 62 | + text-base | ||
| 63 | + text-orange-700 | ||
| 64 | + bg-red-500 | ||
| 65 | + shadow | ||
| 66 | + " | ||
| 67 | + placeholderClassName="text-green-700" | ||
| 68 | + placeholder="placeholder" | ||
| 69 | + /> | ||
| 70 | + </View> | ||
| 71 | + </TestCase> | ||
| 72 | + </TestSuite> | ||
| 73 | + | ||
| 74 | + <Text className="text-4xl text-white font-bold">vars</Text> | ||
| 75 | + <TestSuite name='设置CSS 变量'> | ||
| 76 | + <View className="h-12"></View> | ||
| 77 | + <View style={vars({ '--brand-color': 'red' })}> | ||
| 78 | + <Text className="text-[--brand-color]" > | ||
| 79 | + Welcome to Nativewind! | ||
| 80 | + </Text> | ||
| 81 | + </View> | ||
| 82 | + <View className="h-12"></View> | ||
| 83 | + <View className="flex-1 justify-center items-center gap-10" style={theme}> | ||
| 84 | + <Text className="text-[--theme-fg]">Variables!!!</Text> | ||
| 85 | + </View> | ||
| 86 | + <View className="h-12"></View> | ||
| 87 | + </TestSuite> | ||
| 88 | + | ||
| 89 | + <Text className="text-4xl text-white font-bold">useColorScheme</Text> | ||
| 90 | + <TestSuite name='hooks:useColorScheme()'> | ||
| 91 | + <TestCase tags={['C_API']} itShould='useColorScheme()'> | ||
| 92 | + <View style={{ height: 120, alignItems: 'center', justifyContent: 'center' }}> | ||
| 93 | + <View className='dark:bg-black w-60 h-8 rounded-md'> | ||
| 94 | + <Text className='dark:text-white leading-6 text-center' >{'light:白底黑字;dark:黑底白字'}</Text> | ||
| 95 | + </View> | ||
| 96 | + <View className='w-48 h-8 rounded-md'> | ||
| 97 | + <Text className='leading-6 text-center' >{colorScheme}</Text> | ||
| 98 | + </View> | ||
| 99 | + <Button | ||
| 100 | + title='点击切换主题' | ||
| 101 | + onPress={() => { | ||
| 102 | + setColorScheme(colorScheme === 'light' ? 'dark' : 'light') | ||
| 103 | + }} | ||
| 104 | + /> | ||
| 105 | + </View> | ||
| 106 | + </TestCase> | ||
| 107 | + </TestSuite> | ||
| 108 | + <Text className="text-4xl text-white font-bold">remapProps</Text> | ||
| 109 | + <TestSuite name='hooks:remapProps()'> | ||
| 110 | + <TestCase itShould='remapProps()'> | ||
| 111 | + <View className="p-12"> | ||
| 112 | + <CustomizedButton buttonClass="bg-blue-500 p-2" labelClass="text-white font-bold"> | ||
| 113 | + 按钮 | ||
| 114 | + </CustomizedButton> | ||
| 115 | + </View> | ||
| 116 | + <View className="px-12"> | ||
| 117 | + <OverridenTextInput | ||
| 118 | + className=" | ||
| 119 | + h-12 | ||
| 120 | + w-full | ||
| 121 | + p-2 | ||
| 122 | + border | ||
| 123 | + border-gray-300 | ||
| 124 | + rounded-md | ||
| 125 | + text-base | ||
| 126 | + text-black | ||
| 127 | + bg-white | ||
| 128 | + shadow | ||
| 129 | + " | ||
| 130 | + placeholder="placeholder" keyboardType="numeric" /> | ||
| 131 | + </View> | ||
| 132 | + <View className="h-12"></View> | ||
| 133 | + </TestCase> | ||
| 134 | + </TestSuite> | ||
| 135 | + <Text className="text-4xl text-white font-bold">cssInterop</Text> | ||
| 136 | + <TestSuite name='hooks:cssInterop()'> | ||
| 137 | + <TestCase itShould='cssInterop()'> | ||
| 138 | + <View className="p-12"> | ||
| 139 | + <TextInput | ||
| 140 | + className="h-12 w-full p-2.5 border border-red-300 rounded-md | ||
| 141 | + text-base | ||
| 142 | + text-orange-700 | ||
| 143 | + bg-white | ||
| 144 | + shadow | ||
| 145 | + " | ||
| 146 | + placeholderClassName="text-green-700" | ||
| 147 | + placeholder="placeholder" | ||
| 148 | + /> | ||
| 149 | + </View> | ||
| 150 | + </TestCase> | ||
| 151 | + </TestSuite> | ||
| 152 | + </ScrollView> | ||
| 153 | + </Tester> | ||
| 154 | + ); | ||
| 155 | +} | ||
| 156 | + | ||
| 157 | +const styles = StyleSheet.create({ | ||
| 158 | + button: { | ||
| 159 | + backgroundColor: "#10B981", | ||
| 160 | + paddingVertical: 12, | ||
| 161 | + paddingHorizontal: 24, | ||
| 162 | + borderRadius: 6, | ||
| 163 | + alignItems: "center", | ||
| 164 | + }, | ||
| 165 | + buttonText: { | ||
| 166 | + color: "#ffffff", | ||
| 167 | + fontWeight: "600", | ||
| 168 | + fontSize: 16, | ||
| 169 | + }, | ||
| 170 | +}); | ||


【AI-Review】【严重】【基础代码问题】【代码逻辑错误】移除 ohos.permission.INTERNET 导致 Metro 开发模式无法加载 JS bundle
● 问题: example/harmony/entry/src/main/module.json5 本 PR 删除了
requestPermissions中的ohos.permission.INTERNET权限。但 example/harmony/entry/src/main/ets/pages/Index.ets 第101行jsBundleProvider将new MetroJSBundleProvider()作为首个 JS bundle 提供者,该提供者通过 HTTP(localhost:8081)从 Metro 开发服务器拉取 JS bundle。example/package.json 的start脚本hdc rport tcp:8081 tcp:8081 && react-native start即为该模式准备(hdc 反向端口转发 + Metro 服务)。HarmonyOS 应用访问网络(包括 localhost TCP 连接)需要ohos.permission.INTERNET权限。● 影响: 严重。移除该权限后,
MetroJSBundleProvider无法建立到 Metro 服务器的网络连接,npm start开发模式(带热重载/Fast Refresh)失效。开发者只能依赖npm run dev/npm run prod预打包的 bundle(FileJSBundleProvider/ResourceJSBundleProvider)才能运行应用,丧失开发期的实时调试能力。● 建议: 恢复
ohos.permission.INTERNET权限声明: