Lovable生成的React DOM组件到React Native的完整映射表,包括div→View、span→Text、Tailwind→NativeWind、shadcn/ui→gluestack-ui等具体替代方案。
你在 Lovable 上花了一个周末做了个东西。能跑。现在你想把它搬到手机上,然后你发现了营销页不会告诉你的事:Lovable 构建的是 React Web 应用,不是 React Native 应用。
常见的建议是"用 Capacitor 套一层"。这个建议有个失败模式,我后面会说。先说实际的工作,因为如果你要移植这个东西,你需要知道移植意味着什么。
Lovable 输出的是 React DOM:div、span、input、Tailwind class、shadcn/ui、react-router
这些在 React Native 里都不存在。不是"行为不同",是不存在。
你的 Supabase 层基本不需要改动就能移植过去,这省掉了大部分后端工作。
用 webview 包裹存在被 App Store 以 4.2 准则拒绝的风险。
把移植的预算花在 UI 层,其他地方不要动。
这才是你真正需要的部分。Lovable 生成的每个组件都有对应的映射目标,或者没有。
有两个条目比看起来的更复杂。
react-router-dom 到 expo-router 不是简单替换,是一次重构。Web 路由是组件树。Expo 路由是基于文件的,底层是原生 stack 和 tab 语义。先做这个,再去碰各个页面,否则你会做两遍。
shadcn/ui 无法迁移。它建立在 Radix 之上,而 Radix 建立在 DOM 原语之上。你的 Lovable 项目里每个 shadcn 组件都需要重写。这通常是整个迁移工作中最大的一项。
下面是一个登录表单在 Lovable 中的写法:
// Lovable output
export function LoginForm() {
const [email, setEmail] = useState('');
const navigate = useNavigate();
return (
<div className="flex flex-col gap-4 p-6">
<h1 className="text-2xl font-bold">Sign in</h1>
<input
className="border rounded px-3 py-2"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<button
className="bg-blue-600 text-white rounded py-2 hover:bg-blue-700"
onClick={() => navigate('/home')}
>
Continue
</button>
</div>
);
}
同一个登录表单在 React Native 中的写法:
// React Native
import { View, Text, TextInput, Pressable, StyleSheet } from 'react-native';
import { useRouter } from 'expo-router';
export function LoginForm() {
const [email, setEmail] = useState('');
const router = useRouter();
return (
<View style={styles.container}>
<Text style={styles.heading}>Sign in</Text>
<TextInput
style={styles.input}
value={email}
onChangeText={setEmail}
placeholder="Email"
autoCapitalize="none"
keyboardType="email-address"
/>
<Pressable style={styles.button} onPress={() => router.push('/home')}>
<Text style={styles.buttonText}>Continue</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: { flexDirection: 'column', gap: 16, padding: 24 },
heading: { fontSize: 24, fontWeight: 'bold' },
input: { borderWidth: 1, borderRadius: 6, paddingHorizontal: 12, paddingVertical: 8 },
button: { backgroundColor: '#2563eb', borderRadius: 6, paddingVertical: 8 },
buttonText: { color: '#fff', textAlign: 'center' },
});
有四件事是看完对照表也不会马上明白的:
按钮文字需要自己的 <Text>。你不能在 Pressable 里放裸字符串。屏幕上每段文字都必须放在 Text 节点里,永远都是。
hover:bg-blue-700 没了。没有鼠标指针。如果想要按下反馈,你得自己写,通常是通过 Pressable 的 style 回调。
autoCapitalize 和 keyboardType 出现了。移动端键盘是可配置的,而且默认设置对邮箱来说是错的。Web 从来不需要你考虑这个。
textAlign: 'center' 移到了文字上,不是容器上。样式继承在 React Native 里不会级联。Text 样式必须活在 Text 节点上。
第四点是"为什么这个看起来不对"调试工作中真正愚蠢的大量来源。
这些能编译、能运行、但没有任何效果。没有警告,没有报错。
// all of this is ignored
<View style={{
position: 'fixed', // only absolute and relative exist
display: 'grid', // flexbox only
boxShadow: '0 2px 4px', // use shadowColor/shadowOffset, or elevation on Android
cursor: 'pointer', // no cursor
overflow: 'scroll', // you need ScrollView, this won't do it
}} />
最后一条咬人很狠。在 Web 上,超出视口的内容默认会滚动。在 React Native 里,超出 View 的内容直接被裁掉没了。如果一个 Lovable 页面有个很长的表单,它本来会自由滚动,移植后就不会了。用 ScrollView 包裹它,并且记住 KeyboardAvoidingView,因为屏幕键盘会遮挡大约一半的显示区域,用户看不到他们正在输入的字段。
如果你的 Lovable 应用使用了 Supabase(很可能用了),那一整层搬过去几乎不需要改动。同样的 supabase-js、同样的查询、同样的 auth 调用、同样的 RLS 策略。
// works identically in both
const { data, error } = await supabase
.from('projects')
.select('*')
.eq('user_id', user.id);
你需要为原生端配置 storage:
import AsyncStorage from '@react-native-async-storage/async-storage';
export const supabase = createClient(url, anonKey, {
auth: {
storage: AsyncStorage,
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: false, // required on native
},
});
detectSessionInUrl: false 阻止客户端尝试解析一个不存在的浏览器 URL。漏掉它,你会花一个小时去追一个 null session。
所以移植的真正范围是:整个 UI 层,全部路由,没有数据模型。这在第一天听起来比实际情况好,第三天听起来比实际情况差。
每个人想到的捷径是 Capacitor 或 webview 外壳。这确实能用——在你能得到一个二进制文件的意义上。
问题是 App Store 审核指南第 4.2 条,最低功能要求。苹果拒绝本质上是一个重新打包的网站且没有原生能力的应用。没有一条明确的界线,这是最糟糕的一种规则——你没法围绕它制定发布计划。有真正离线行为、推送通知和设备集成的应用能通过。打包的 CRUD 仪表盘经常不能,而你会发现它在构建完其他所有东西之后。
如果这确实是一个 Web 产品而且你只想要分发,PWA 是更诚实和便宜的选择。如果你想要一个能活下来的 App Store 列表,你需要原生组件在底下。
上面的移植是机械性的,这正是值得外包的工作。RapidNative 将 Lovable 项目转换为 React Native 和 Expo 应用,并将其提交到应用市场:签名、证书、截图、元数据、隐私标签、数据安全表格。你能得到源代码,所以这不是一个被困住的黑箱。如果任一商店拒绝,他们修复并重新提交,不额外收费,直到上线,这才是真正降低上面 4.2 问题风险的部分。典型周转时间是一到两周。
无论你是外包还是手工完成,上面这个翻译对照表是实际发生的事情。值得去理解,因为你会需要调试它。
Lovable 是一个面向 Web 的好工具。React Native 是一个不同的渲染目标,不是同一个东西的不同风味。移植工作集中在 UI 层,Supabase 代码可以免费带过来,而套壳的捷径是用一周的移植时间换取审核时无限的风险。
在 Web 到原生的移植中,什么最让你头疼?我在收集静默失败清单,而 overflow: scroll 只能排第二。