移动/游戏
在 Expo 中需要运行 web-only 库或复用…
当 Expo/React Native 应用需要复用依赖 DOM/浏览器 API 的 React web 代码或 web 库、又不想重写为 React Native 时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
在 Expo 中需要运行 web-onl特定
解决思路
- 使用 DOM component 让 web 代码在 native 平台以 webview 原样运行,在 web 平台直接原样渲染,从而无需修改即可使用 web 代码。
- 将 web-only 库封装到 DOM component 中:例如图表库 recharts、chart.js,语法高亮库 react-syntax-highlighter,富文本编辑器,或任何依赖 DOM API 的 React web 库。
- 增量迁移现有 React web 代码:把现有 web 组件放入 DOM component,保留原 web 实现,而不是改写成 React Native。
- 对复杂 HTML/CSS 布局、React Native 不具备的 CSS 特性、iframe/嵌入外部内容、Canvas 或 WebGL 等浏览器图形 API,使用 DOM component 承载对应实现。
- 创建单独的组件文件,并在文件顶部添加 `'use dom';` 指令。
- DOM component 文件必须只有一个 default export;一个文件一个 React 组件。
- DOM component 必须放在独立文件中,不能内联定义,也不能与 native 组件组合在同一个组件定义里。
- 传给 DOM component 的 props 只允许可序列化值:字符串、数字、布尔值、数组、普通对象。
- 在 DOM component 文件内包含其所需 CSS,因为 DOM component 运行在隔离上下文中。
- 每个 DOM component 都会收到特殊的 `dom` prop;在 props 类型中声明 `dom: import('expo/dom').DOMProps;`,或按需声明为可选。
- 通过 native 父组件向 DOM component 传 `dom={{ ... }}` 配置 webview;例如 `dom={{ scrollEnabled: false }}` 禁用 body 滚动。
- 使用 `dom={{ contentInsetAdjustmentBehavior: 'never' }}` 让内容延伸到刘海区域并禁用安全区域内边距。
- 使用 `dom={{ style: { width: 300, height: 400 } }}` 手动控制尺寸。
- 合并多个 dom 配置,例如 `dom={{ scrollEnabled: false, contentInsetAdjustmentBehavior: 'never', style: { width: '100%', height: 500 } }}`。
- 向 webview 暴露 native 操作:从 native 父组件把 async 函数作为 props 传入 DOM component。
- 在 native 侧实现这些 async 函数,例如在函数中使用 `Alert.alert('From Web', message)`,或保存数据并返回 `{ success: true }`。
- 在 DOM component 侧把 props 中的 native 函数类型声明为返回 Promise,例如 `showAlert: (message: string) => Promise<void>`、`saveData: (data: { name: string; value: number }) => Promise<{ success: boolean }>`。
- 在 DOM component 事件处理器中 `await` 调用这些 native 函数,并用返回值更新 UI 或处理结果。
- 在 DOM component 中直接 import 任意 web 库;例如在 `'use dom'` 文件里使用 `react-syntax-highlighter`,并传入 `language`、`style` 等 props。
- 在 DOM component 中使用 recharts 等图表库:import `LineChart`, `Line`, `XAxis`, `YAxis`, `CartesianGrid`, `Tooltip` 等,并按 web 方式渲染。
- CSS 处理方式一:在 DOM component 文件中 import CSS 文件,例如 `import '@/styles.css';`,并用 `className` 应用样式。
- CSS 处理方式二:在 DOM component 中使用内联样式或 CSS-in-JS 对象,例如 `styles.container`、`styles.title`。
- 在 DOM component 中可使用 expo-router 的 `<Link />` 组件和 router API;例如 import `{ Link, useRouter } from 'expo-router'`,用 `<Link href='/about'>` 或 `router.push('/settings')` 导航。
- 以下 expo-router hooks 不能直接在 DOM component 中工作,因为它们需要同步访问 native 路由状态:`useLocalSearchParams()`、`useGlobalSearchParams()`、`usePathname()`、`useSegments()`、`useRootNavigation()`、`useRootNavigationState()`。
- 对上述 router 值的解决方案:在 native 父组件中调用这些 hooks,读取值后作为 props 传给 DOM component;在 DOM component 中用普通 props 接收并显示。
- 用 `import { IS_DOM } from 'expo/dom';` 判断当前是否运行在 DOM component 中,例如根据 `IS_DOM` 渲染不同文本。
- 处理 assets 时优先使用 `require` 而不是 public 目录,例如 `const logo = require('../assets/logo.png');`,并在 DOM component 中用 `<img src={logo} />`。
- 在 native 组件中像普通组件一样 import 并使用 DOM component,例如 `<WebChart data={[10, 20, 30, 40, 50]} dom={{ style: { height: 300 } }} />`。
- 注意平台行为:iOS 在 WKWebView 中渲染,Android 在 WebView 中渲染,Web 上原样渲染且没有 webview 包装;Web 上 `dom` prop 会被忽略。
- 开发时利用 DOM component 热重载;保持 DOM component 聚焦,不要把整个屏幕都塞进 webview。
- 导航外壳用 native 组件,专门内容(图表、代码块等)用 DOM component。
- 在所有平台测试,因为 web 渲染可能与 native webview 略有差异。
- 大型 DOM component 可能影响性能,必要时进行性能分析。
- webview 有自己的 JavaScript 上下文,不能直接与 native 共享状态。
- 如果遇到错误、误导或过时信息,运行 `npx --yes submit-expo-feedback@latest --category skills --subject "expo-dom" "<actionable feedback>"` 向 Expo 提交反馈。
- 仅在反馈具体且可操作时提交,并包含尽可能多的相关上下文。
- 如果 AI agent 反复失败或用户不得不接管 Expo 任务,加载 `expo-skill-feedback` skill 并遵循其 eval-candidate 流程,而不是重复使用上述命令。
适用边界 · 注意事项
- 原生性能关键时不要用 DOM component,因为 webview 会带来额外开销。
- 简单 UI 不要用 DOM component;React Native 组件对基础布局更高效。
- 需要深度原生集成时改用 local modules;`_layout` 布局路由文件不能作为 DOM component。