在 React 中构建通用导航策略
由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!
当我加入STRV时,他们对我有一个具体的要求:构建一个适用于 iOS、Android 和 Web 的前端应用程序,并在所有平台之间共享组件和业务逻辑。
由于我是一名热爱探索新领域的前端开发人员,我无法拒绝,必须抓住这个机会。
我最终面临各种各样的挑战;从缺乏与 React Native Web 相关的真实场景内容,到热门项目缺乏意想不到的文档资料,再到难以构建一些特定于平台的模块。
这篇文章重点介绍这段旅程中非常重要的一部分:构建导航解决方案。
但首先……
一些背景信息
我之前只做过一个 React Native 示例应用(未编译也未发布)。老实说,在做这个项目的时候,我对 React Native 了解不多。
我最初听说Expo及其实验性的 Web 支持1,但我决定不采用它,主要是因为我喜欢控制项目堆栈并了解正在发生的事情;我希望能够自定义安装、安装自定义版本的模块并更好地控制项目依赖项。
随后,我了解到 GitHub 上还有另外两个项目:ReactNative for Web和ReactXP。两者目标相似,但实现方式不同。正如 ReactXP 的官方文档所述:
ReactXP 是位于 React Native 和 React 之上的一个层,而 React Native for Web 是 React Native 的一个并行实现——与 React Native for iOS 和 Android 同属一个体系。
本文不会着重介绍这两者之间的区别,但在阅读了一些技术博客文章和演讲后,我们最终选择了 ReactNative for Web。
在深入研究了一些文章并尝试在各自的领域中实现每个环境之后,我发现对我来说,最好的起点是一个名为react-native-web-monorepo 2的优秀模板,它借助Yarn Workspaces为通用应用程序提供了支持。
不过,在开始将这种方法应用到你的项目中之前,我建议你先回顾一下你的需求,并检查这些工具是否能满足你的所有需求。
我们拥有的东西
React.js 生态系统中一些流行的路由解决方案并非旨在同时支持 DOM 和原生环境; DOM<div>与原生环境不同<View>,<ul>原生环境与原生环境也不同<FlatList>,而且大多数 Web 底层机制与移动端底层机制也不同——这使得找到一个通用的解决方案变得困难。@reach/router就是一个选择回避同时支持这两种环境挑战的 Web 解决方案的例子。
截至目前(2020年1月),我们已经有一些现成的通用Web/原生应用方案。但它们最终都未能完全满足我们的需求:
- react-router对于 Web 来说是一个很好的选择,但在移动设备上,它缺少屏幕过渡、模态框、导航栏、后退按钮支持和其他必要的导航原语。
- react-navigation在移动端表现出色,但由于其Web支持仍处于实验阶段,尚未在生产环境中广泛应用,因此您很可能会遇到一些与历史记录和查询参数相关的问题。此外,它缺少TypeScript类型定义,这导致我不得不自行编写部分定义,因为 TypeScript 是该项目的必备工具。
接下来就是下一部分了!
思考解决方案
本文中的代码可在 GitHub 上找到:ythecombinator/react-native-web-monorepo-navigation
我承认,当我们深入研究这个问题时,最令人困惑的事情之一就是无法找到使用 React Native for Web 的流行应用程序(例如 Twitter、Uber Eats 和这里提到的所有其他应用程序)是如何进行导航的,以及它们是如何应对我之前提到的那些挑战的。
所以我们只能靠自己了!
我们的新方案基于对最新版 react-router-dom 4和 react-navigation 5的抽象。两者都经历了很大的发展,现在它们似乎有一些共同的目标,我认为这些目标是正确实现 React 导航/路由的关键决策:
- 钩子优先 API
- 以声明式方式实现导航
- TypeScript 中的一等类型
鉴于此,我们开发了一些实用工具和组件,旨在实现通用的导航策略:
utils/navigation
露出两个钩子:
useNavigation返回一个navigate函数,该函数以路由作为第一个参数,以参数作为其他参数。
它可以这样使用:
import { useNavigation } from "../utils/navigation";
// Our routes mapping – we'll be discussing about this one in a minute
import { routes } from "../utils/router";
const { navigate } = useNavigation();
// Using the `navigate` method from useNavigation to go to a certain route
navigate(routes.features.codeSharing.path);
它还提供了一些其他已知的路由实用程序,例如goBack和replace。
useRoute返回有关当前路由的一些数据(例如path,params传递给该路由的数据)。
以下是如何利用它获取当前值的方法path:
import { useRoute } from "../utils/navigation";
const { path } = useRoute();
console.log(path);
// This will log:
// '/features/code-sharing' on the web
// 'features_code-sharing' on mobile
utils/router
这基本上包含一个routes对象——其中包含每个平台的不同路径和实现——可用于:
- 导航
useNavigation - 根据当前路由切换逻辑
useRoute - 指定组件
path渲染的每个路由及其一些额外数据Router
components/Link
它提供应用程序内的声明式导航。它基于Web 端的Linkfrom和移动端的+ hook构建。react-router-dom TouchableOpacityuseNavigation
就像Link从……一样react-router-dom,它可以这样使用:
import { Text } from "react-native";
import { Link } from "../Link";
import { routes } from "../utils/router";
<Link path={routes.features.webSupport.path}>
<Text>Check "Web support via react-native-web"</Text>
</Link>
components/Router
这就是路由器本身。在网页上,它基本上是一个路由选择器BrowserRouter,用于Switch选择路线。在移动设备上,它是路由选择器Stack和BottomTab导航器的结合体。
综上所述,您将看到应用程序的每个屏幕,并了解如何使用useRoute(),无论您使用哪个平台。useNavigation()<Link />
如果有人问起我接下来的工作计划,我会提到以下几点:
1)添加更多实用工具——例如,一个Redirect旨在实现更声明式导航方法的组件6
2)处理两个平台上的极端情况
3)将导航库中的大部分内容重新组织起来,只留下主要Router组件,并utils/router在应用程序端编写。
结论
我的感觉是,Web、移动 Web 和原生应用程序环境都需要特定的设计和用户体验7 – 顺便说一句,这与 React Native 背后的“一次学习,到处编写” 理念相符。
虽然代码共享对 React 和 React Native 来说是一大优势,但我认为跨平台共享代码很可能应该:
- 业务逻辑
- 配置文件、翻译文件以及大多数常量数据——那些与渲染环境无关的数据。
- API/格式化;例如 API 调用、身份验证以及请求和响应数据的格式化
应用程序的其他一些层,例如路由,应该使用最适合该平台的库,例如react-router-domWeb 平台使用库,react-navigation或者原生平台使用类似的库。
或许将来我们可以拥有一个真正统一的代码库,但就目前而言,这项技术似乎还没有成熟,而这里分享的方法似乎是最合适的。
脚注
1) 今年在 Reactive Conf 大会上,Evan Bacon 就 Expo for Web 发表了一场精彩的演讲——如果你还没有看过,我强烈建议你去看一下。
2) 这个方案由DevHub的作者 Bruno Lemos 编写,并且他也在使用同样的方案。DevHub 是一款 GitHub 客户端,可在 Android、iOS、Web 和桌面平台运行,且这些平台之间的代码共享率超过 95%。如果您想了解他是如何想到这个解决方案的,请查看这里。
3)这些问题包括:
- 功能范围
- 开发者体验范围
- 由于缺少 TypeScript 类型定义(此处),我不得不自己编写部分定义。
4) React Router v5主要侧重于引入结构改进和一些新功能。但v5.1引入了一系列实用的 hooks,使我们能够将上述功能应用于 Web 端。
5) React Navigation v5也为引入现代化的、以 hooks 为先的 API 做了很多努力,使我们能够为移动设备实现上述功能。
<Redirect /> 6)这里有一篇非常好的文章,介绍了如何使用声明式和可组合式导航。
7) 如果您对这个话题感兴趣,在本次演讲中,我将分享一些在构建以代码共享为主要目标的应用程序时学到的经验教训——从项目设置、共享基础设施,一直到共享组件和样式——以及您如何实现同样的目标。
文章来源:https://dev.to/ythecombinator/drive-towards-a-universal-navigation-strategy-in-react-j60