ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

在 Next.js 中使用 React Native Web:构建跨平台 Web 应用的官方示例实战指南

在 Next.js 中使用 React Native Web:构建跨平台 Web 应用的官方示例实战指南 在 Next.js 中使用 React Native Web构建跨平台 Web 应用的官方示例实战指南【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js导读React Native 提供了一整套平台无关的组件与 API而react-native-web将这些能力带到 Web 端让开发者可以用同一份 React 代码编写原生应用与响应式 Web 界面。本篇文章以 examples/with-react-native-web 官方示例为核心完整剖析该示例在 Next.js 中的落地方式——包括项目结构与逐文件代码解读、Webpack 与 Turbopack 双引擎下的模块解析别名配置、页面间通用链接写法、以及基于AppRegistry的服务端样式注入原理。读完本文你将掌握在 Next.js 中接入 React Native Web 的完整可行方案并能自行搭建、运行与部署一个 RN 风格的 Web 项目。示例背景这个示例在解决什么问题在阅读源码之前先明确该示例想展示的核心能力。示例 README.md 明确总结了两大卖点高质量的用户界面React Native for Web 让你能在 JavaScript 中便捷地构建快速、自适应的 Web UI。它提供接近原生应用的交互体验、对多种输入模式触摸、鼠标、键盘的支持、经过厂商前缀优化的样式输出、开箱即用的 RTL从右向左布局、内置无障碍支持并能无缝集成 React DevTools。一次编写、随处渲染React Native for Web 可与现有 React DOM 组件互操作并兼容 React Native API 的绝大部分。你可以为原生端与 Web 端开发新组件而无需重写既有代码它还能借助 Node.js 在服务端渲染出 HTML 与关键 CSScritical CSS。简而言之这个示例是React Native 生态与 Next.js 工程化能力之间的桥梁演示用 RN 风格的StyleSheet、View、Text写页面同时享受 Next.js 的路由、SSR 与构建体系。为什么要由 Next.js 承担 Web 端渲染React Native Web 本身只解决组件与样式的跨端渲染并不自带路由、服务端渲染与静态导出工程。Next.js 恰好补齐这层能力目录文件即路由、next/head管理文档头部、next/document提供服务端 HTML 模板定制接口。二者的结合点正体现在示例对_document.js的重写上——RN Web 需要在此完成 SSR 样式采集这正是本文第 5 节要展开的重点。快速启动用 create-next-app 拉取示例仓库中每个examples/子目录都是一个可独立运行的 Next.js 应用。官方 README 提供了三种包管理器对应的引导命令目标项目名统一为with-react-native-web-appnpx create-next-app --example with-react-native-web with-react-native-web-appyarn create next-app --example with-react-native-web with-react-native-web-apppnpm create next-app --example with-react-native-web with-react-native-web-app命令执行后create-next-app会把 examples/with-react-native-web 下的完整模板复制到新目录并安装依赖。随后即可用示例自身定义的脚本运行。从 package.json 可见其 script 配置非常精简{ scripts: { dev: next, build: next build, start: next start } }即依次执行npm run dev开发服务器、npm run build生产构建、npm run start启动生产服务器即可。若想完整看到示例效果建议本地运行两个路由首页/与/alternate之间的链接跳转演示。示例依赖与项目结构全景先看依赖声明。示例 package.json 的 dependencies 如下{ private: true, scripts: { dev: next, build: next build, start: next start }, dependencies: { next: latest, react: ^18.2.0, react-dom: ^18.2.0, react-native-web: ^0.18.9 } }几个值得注意的事实依赖中没有react-native本体只有react-native-web。示例代码却统一import { ... } from react-native这正是通过模块别名alias把react-native解析到react-native-web实现的具体见第 6 节。React 版本锁定在^18.2.0next使用latest说明该示例面向当前 Next.js 稳定线。依赖数量极少没有任何 UI 库或路由库——RN Web 组件本身即是UI 库。示例完整目录结构为examples/with-react-native-web/ ├── README.md ├── app.json ├── next.config.js ├── package.json └── pages/ ├── _app.js ├── _document.js ├── alternate.js └── index.js它遵循 Pages Router 约定其中两个带下划线前缀的文件是 Next.js 的全局定制入口。此外还有一个非标准的app.json它是提供给 React NativeAppRegistry使用的应用注册信息第 7 节详解。页面代码用 RN 组件书写 Web 界面示例页面完全没有div、p、a等 Web 标签而是直接使用 RN 的View、Text并通过StyleSheet.create声明样式。首页 pages/index.js首页 pages/index.js 完整源码如下import { StyleSheet, Text, View } from react-native; const styles StyleSheet.create({ container: { alignItems: center, flexGrow: 1, justifyContent: center, }, link: { color: blue, }, textContainer: { alignItems: center, marginTop: 16, }, text: { alignItems: center, fontSize: 24, marginBottom: 24, }, }); export default function App(props) { return ( View style{styles.container} Text accessibilityRoleheader style{styles.text} React Native for Web Next.js /Text Text style{styles.link} accessibilityRolelink href{/alternate} A universal link /Text View style{styles.textContainer} Text accessibilityRoleheader aria-level2 style{styles.text} Subheader /Text /View /View ); }其中承载了三条示范性知识flex 布局即页面布局container用alignItems: center、justifyContent: center、flexGrow: 1完成垂直水平居中。RN Web 会把这套 flexbox 声明编译为带厂商前缀的 CSS这正是 README 所述优化过的 vendor-prefixed styles的实现途径。accessibilityRole 映射语义accessibilityRoleheader与accessibilityRolelink在 Web 端会分别映射为header、a等语义角色从而获得免费的无障碍与 SEO 语义。细看代码会发现aria-level2这类 ARIA 属性也可以直接透传说明 RN Web 组件天然支持与 React DOM 属性互操作。Text的href实现导航对Text href/alternate赋予了链接的语义与行为配合样式color: blue视觉上即是普通超链接。官方称之为 universal link即同一段代码在原生与 Web 端都表达跳转。子页面 pages/alternate.js对应的 pages/alternate.js 是一个对称的演示页展示如何从子页面返回首页import { StyleSheet, Text, View } from react-native; const styles StyleSheet.create({ container: { alignItems: center, flexGrow: 1, justifyContent: center, }, text: { alignItems: center, fontSize: 24, marginBottom: 24, }, link: { color: blue, }, }); export default function Alternate() { return ( View style{styles.container} Text accessibilityRoleheader style{styles.text} Alternate Page /Text Text style{styles.link} accessibilityRolelink href{/} Go Back /Text /View ); }两个页面组合在一起恰好演示了 Next.js Pages Router 对 RN 组件体系的完整支持页面即路由、RN 组件即 UI。只要在pages/下新增文件并默认导出一个用 RN 组件组装的 React 组件就自动获得一个新路由。全局定制_app.js 与 _document.js 的作用RN 组件要在 Next.js 中正确工作两个下划线文件承担了关键职责缺一不可。pages/_app.js设置移动端 viewport全局应用入口 pages/_app.js 非常简单但其价值很容易被低估import * as React from react; import Head from next/head; function MyApp({ Component, pageProps }) { return ( Head meta nameviewport contentwidthdevice-width, initial-scale1 / /Head Component {...pageProps} / / ); } export default MyApp;RN Web 面向多种输入模式与自适应布局因此必须让浏览器视口宽度等于设备宽度。若缺少这段meta nameviewport移动端页面会以桌面宽度渲染再缩放触摸体验与布局都会失真。该标签放在_app.js中意味着所有页面全局生效无需在每页重复声明。pages/_document.jsRN Web SSR 的关键一环服务端渲染文档定制文件 pages/_document.js 是整个示例中最具技术含量、也最需要理解的文件import { Children } from react; import Document, { Html, Head, Main, NextScript } from next/document; import { AppRegistry } from react-native; import config from ../app.json; // Force Next-generated DOM elements to fill their parents height const normalizeNextElements #__next { display: flex; flex-direction: column; height: 100%; } ; export default class MyDocument extends Document { static async getInitialProps({ renderPage }) { AppRegistry.registerComponent(config.name, () Main); const { getStyleElement } AppRegistry.getApplication(config.name); const page await renderPage(); const styles [ style dangerouslySetInnerHTML{{ __html: normalizeNextElements }} /, getStyleElement(), ]; return { ...page, styles: Children.toArray(styles) }; } render() { return ( Html style{{ height: 100% }} Head / body style{{ height: 100%, overflow: hidden }} Main / NextScript / /body /Html ); } }逐层拆解其工作原理注册组件根AppRegistry.registerComponent(config.name, () Main)把 Next.js 的Main /即当前路由页面注册为名为config.name的 RN 应用根组件。config来自 app.json其内容为{ name: with-react-native-web, displayName: with-react-native-web }采集样式元素AppRegistry.getApplication(config.name)返回一个包含getStyleElement()的对象。RN Web 内部把StyleSheet声明聚合成一张样式表调用该方法即可得到react-native-web的style元素其中内联着编译好的 critical CSS。合并进 Next 文档 styles把自定义的normalizeNextElements内联样式与getStyleElement()一起交给 Next.jsNext.js 会把这些style注入 HTML 的head。Children.toArray用于消除 React 对数组元素 key 的警告。打通 100% 高度链RN Web 布局常用flexGrow撑满容器因此需要html、body、#__next逐层具备height: 100%。示例的做法是在_document.js的render()中给Html、body直接内联height: 100%样式同时用#__next { display: flex; height: 100%; }的内联style强制 Next 生成的挂载节点铺满父级高度。这段内联 CSS 恰好解决了RN flex 布局在 Web 端高度塌陷的经典问题。body 细节overflow: hidden避免页面双滚动条Html、body上的 style 会被 React 序列化为合法的 HTML style 属性。从源码结构可以推断只要示例中页面使用了依赖父级高度的 flex 布局就离不开_document.js中这套注册根组件 注入样式 撑满高度的组合拳。如果你在自己的项目中复用此模式这四行左右的核心逻辑应当原样保留。构建配置react-native → react-native-web 的模块别名RN Web 最常见的接入痛点是现有代码从react-native导入而 Web 工程中并不存在该模块。示例 next.config.js 给出的解法是对 Next.js 支持的两种打包引擎同时配置别名/** type {import(next).NextConfig} */ module.exports { turbopack: { resolveAlias: { react-native: react-native-web, }, resolveExtensions: [ .web.js, .web.jsx, .web.ts, .web.tsx, .mdx, .tsx, .ts, .jsx, .js, .mjs, .json, ], }, webpack: (config) { config.resolve.alias { ...(config.resolve.alias || {}), // Transform all direct react-native imports to react-native-web react-native$: react-native-web, }; config.resolve.extensions [ .web.js, .web.jsx, .web.ts, .web.tsx, ...config.resolve.extensions, ]; return config; }, };这段配置包含三个可复用的要点Webpack 侧使用精确别名react-native$: react-native-web末尾的$表示精确匹配模块名只把直接import ... from react-native重定向而不误伤react-native/xxx子路径导入。Turbopack 侧平行配置next.config.js同时提供顶层turbopack字段其中resolveAlias同样把react-native映射到react-native-web。从 Next.js 近几个版本的能力看当以next dev --turbopack启动时该字段即生效。示例同时维护两套字段本质上是保证切换打包引擎时代码行为一致。.web.*平台后缀优先Webpack 与 Turbopack 两侧都把.web.js/.web.jsx/.web.ts/.web.tsx排到扩展名解析顺序的最前面。这是 React Native 生态的平台特定文件约定——当某文件存在.web.js与.js两份实现时Web 构建会优先取.web.js。示例把它前置到内置扩展名之前确保平台分流逻辑正确。可以推断之所以同时配置两个引擎是因为该示例面向使用next默认走 Turbopack 的开发路径以及显式启用 webpack 的两种运行环境均需保证import from react-native能解析成功。运行流程串联从启动到出页综合以上分析可把示例的完整运行链路归纳为nextdev读取 next.config.js按引擎建立模块别名令所有react-native导入指向react-native-webNext.js 依据 Pages Router 约定将 pages/index.js 与 pages/alternate.js 编译为/与/alternate两个路由每个请求进入 _app.js注入移动端 viewport meta_document.js 的getInitialProps在服务端把页面注册进AppRegistry、取出 RN Web 样式表并注入head同时铺满html/body/#__next高度链客户端 hydration 后页面上的View/Text/StyleSheet被渲染为带语义角色与优化样式的 Web DOMaccessibilityRolelink的Text即可像普通链接一样完成/与/alternate之间的客户端导航。这条链路中步骤 1、4 是最容易遗漏的两个工程开关也是把RN 组件直接搬到 Next.js的示例代码变为可用产品的关键。把它迁移到自有项目三条实操建议基于上述源码级分析如果你打算在自己的 Next.js 项目中引入 React Native Web可按下述清单对照落地均以当前仓库示例为参照依赖层面仅安装react-native-web示例使用^0.18.9无需安装react-native本体react 与 react-dom 使用项目既有版本即可。配置层面把 next.config.js 中webpack与turbopack两段别名逻辑原样合入你的配置文件——这是import from react-native能正常编译的前提如使用 monorepo 且多端共存还需自行评估平台后缀解析顺序对构建体积的影响。文档定制层面在pages/_document.js中复刻AppRegistry.registerComponentgetStyleElementheight: 100%三段逻辑并在pages/_app.js中补充 viewport meta若你使用的是 App Routerapp/目录需要将服务端样式注入迁移到 App Router 的文档处理能力上示例本身仍是 Pages Router 的实现直接照搬会失效。小结with-react-native-web 官方示例用不到十个文件讲清了RN 组件运行在 Next.js的全部要件RN Web 负责把平台无关的组件、flexbox 样式与无障碍语义翻译到 DOMNext.js 负责路由、SSR 与工程化而连接二者的胶水是配置文件里的模块别名与_document.js里的AppRegistry样式采集。对照 README.md 所述write once, render anywhere与高质量自适应 UI两大价值主张恰好能在 pages/index.js、pages/_document.js 与 next.config.js 三处源码中找到一一对应的工程实现。掌握了这三块拼图你就掌握了在 Next.js 中启用 React Native Web 的完整方案。【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表