我作为一名 Web 开发人员选择了 React Native,以下是我学到的东西
过去几周,我一直在工作中开发一个 React Native 应用。它是一个新闻阅读器(废话),功能有点庞然大物,具有文件系统访问、后台下载和推送通知等功能。
这不是我第一次用 React,但是!!这是我第一次用 RN。这很吓人,因为新事物总是让人害怕。不过,这是一次很棒的体验,我有点期待着某个“哦,该死”的时刻,某个意想不到的事情会变得非常糟糕——但到目前为止,它非常有趣。
为什么选择 React Native?嗯,我的团队原本想要一个 Web 应用!(PWA,现在很火)我们改变方向主要有三个原因:
- 尽管网络是一个“不错的选择”,但我们的第一个市场是应用商店
- 我们希望它拥有非常精细的离线和后台功能。这在网页端还处于早期阶段,且处于实验阶段,但在移动端应用上,这个问题从第一天起就得到了解决。
- 我们想要提供类似原生应用的体验。想想 60fps 的动画、多个堆叠视图等等。这些问题在应用领域也已经解决了,但在网页端,我们只能靠自己了。
- 如果需要的话,我们
react-native-web
有办法将其重新转变为 PWA
这不是网络
在 Web 上,简单的 React 最终会生成一个基于 HTML 的网站。这样你就可以使用 CSS 并直接在组件上调用 DOM 函数。
原生应用则截然不同。尽管使用了 React 的语法(与 Cordova 等库不同),RN 却不会提供 HTML、DOM 元素或 CSS,而是直接在移动操作系统上编排原生视图。这非常棒,因为它意味着你的 UI 是真正的原生 UI。当然,它是使用 JavaScript 动态组装的,但它使用的块与其他应用使用的块相同。
这和标准的 React 有什么区别?说实话区别不大。基本操作都差不多!
/*react web*/
const Counter () => (
<div className='row'>
<button onClick={setCount(c=>c+1)}>Add number</button>
<span>{count}</span>
</div>
)
/*react native*/
const Counter () => (
<View style={styles.row}>
<Button onClick={setCount(c=>c+1)}>Add number</Button>
<Text>{count}</Text>
</View>
)
使用原生 UI 不仅能让你的应用更出色,而且速度也更快。如果你习惯了在网页上费力地实现 60 fps 的动画,那么这里就是一个全新的世界,你可以轻松实现。而且完全免费!即使在老旧的设备上也是如此!(更多性能信息请见第二部分)
顺便说一句!你在这里也无法获得 HTML5 中所有语义元素的精髓。RN 中几乎所有元素都是View
。这意味着,为了全方面的目的,标记视图的语义目的至关重要accessibilityRole
。你可以使用来做到这一点。如果你需要 alt 文本,accessibilityLabel
它已经为你准备好了。
入门
很久以前,我通过制作原型获得了一些非常基本的 Xcode 经验(那时候 xcode 看起来像 iTunes?那是一个奇怪的时代)但无论如何,与网络相比,我知道会发生什么——更快的应用程序,但更慢的开发周期和更难使用的开发工具。
我 错 了
首先,如果你只是想尝试一下原生应用,你不需要这些,你可以使用expo来运行你的 JavaScript 并处理所有与应用相关的部分。这会显著减少你对应用部分的控制,但很酷的是,你所有的代码仍然是原生的 React。如果你需要这种控制,你可以随时expo eject
获取你的原始 Xcode 和 Android Studio 项目。
即使弹出后,您仍然不会在大多数情况下使用 Xcode 或 Android Studio(除非您想)。react-native run-ios
将启动模拟的 iPhone X 并运行您的应用程序,并将react-native run-android
其直接安装到您只想充电的手机上,但没关系,我想现在您的手机上已经有了一个应用程序。
React文档中关于设置 Android Studio 的说明非常详细。对于 iOS 应用来说,代码签名有点麻烦——你需要在 iOS 设备上运行之前完成。你不需要成为 Apple 开发者计划的付费会员,但需要登录 Xcode。我通常会尝试编译,点击所有红色标记,然后点击“修复问题”按钮,直到问题解决。
最后,运行应用时,摇晃设备或模拟器即可获得非常酷炫的调试菜单。您可以像在网页端一样热重载代码,运行 Chrome DevTools 查找错误,甚至可以打开世界上最可爱的小检查器:
造型
你可能需要给你的应用添加一些样式。除非你正在制作待办事项列表或其他什么,否则你很可能需要大量地修改应用样式。
React Native 自带一个内置StyleSheet
模块,可以帮你处理样式。这很棒,因为你再也不用纠结该用哪个 CSS-in-JS 方案了。但它也有缺点,因为StyleSheet
它和 CSS太相似了,你可能会以为自己在写 CSS,但其实这种相似之处只是表面现象。
const styles = StyleSheet.create({
button: {
borderRadius: 999,
backgroundColor: 'tomato',
padding: 10,
paddingHorizontal: 10,
},
text: {
textTransform: 'uppercase',
},
})
const Button = ({ children, ...props }) => {
return (
<Touchable {...props}>
<View style={styles.button}>
<Text style={styles.text}>{children}</Text>
</View>
</Touchable>
)
}
关于如何设计事物的内置文档非常好,但我想先了解一下大的变化
它很像 css-in-js
你的样式是一个带有驼峰式属性的 JavaScript 对象。如果你已经使用过emotion
,或者styled-components
你对这种工作方式感到很熟悉
粗糙像素
大多数手机屏幕都比较密集,而且会放大 UI,因此,作为一个整体,屏幕尺寸1px
会比较大,而且边框看起来也比较大。您可以使用它StyleSheet.hairlineWidth
来获取跨设备 1 个屏幕像素的大小。
但一切都是弹性盒子
由于所有StyleSheet
操作都与底层操作系统交互,因此与 CSS 相比,布局方式会受到限制。如果你想让某些内容浮动(例如,将图片包裹在文本的侧面),那就完全没办法了。使用 CSS 网格也一样!
你有一个神奇的flex
属性,可以将flexGrow
、flexShrink
和flexBasis
合并成一个数字。我不知道怎么用。@NikkitaFTW称之为“向后弯曲”。她也不知道怎么用。
所以你不能让东西漂浮起来
我们的情况比较特殊,但由于我们的应用需要渲染大量文本类型的文章,为了解决这个问题,我们决定将文章正文渲染到 WebView 中,并将其放入 React Native 应用中。这感觉不对,而且违反直觉,因为“反正都是 JavaScript”,但重要的是始终使用最适合的工具,而且 Web 就是为渲染文档而构建的!
或者调试布局😰
还记得以前为了检查布局哪里有问题,不得不把 div 涂成红色吗?准备好怀旧吧。RN 确实提供了内置的检查器,但由于它位于模拟器(或手机内部)中,使用起来有点麻烦。
而且没有级联或选择器
你可以直接将样式应用到组件上。你不能根据子组件的类型或hover
状态disabled
或:before / :after
别名来设置样式。
这听起来非常有限,但实际上,拥有一个具有小组件且结构良好、模块化的应用程序将为您解决很多问题。
所有样式都无法级联,这虽然能让你的 CSS 更可预测,但也带来了一些麻烦。为了解决这个问题,我们引入了 React context 来封装我们想要级联的样式属性,例如主题颜色。Context 非常适合这种情况,因为你可以在同一个屏幕中为不同的节点创建多个 context,几乎像 CSS 变量一样工作。
这有点过于简单(我们有一个 useAppearance() 钩子可以直接返回值)但你明白了:
/*
in your appearance file
*/
export const appearances = {
dark: {
backgroundColor:'#000',
color: '#fff',
},
light: {
backgroundColor:'#fff',
color: '#000',
},
}
export const AppearanceContext = createContext('light') // <- that's the default!
/*
in your view
*/
<AppearanceContext.Provider value={'dark'}>
<Button>I'm dark!</Button>
</AppearanceContext.Provider>
<AppearanceContext.Provider value={'light'}>
<Button>I'm light!</Button>
</AppearanceContext.Provider>
/*
in your component
*/
(...) => {
const { backgroundColor, color } = appearances[useContext(AppearanceContext)]
return (
<View style={{backgroundColor, color}}>{children}</View>
)
}
级联的丢失并不像看起来那么严重,除了一个非常重要的用例:
文本
您想要在 React native 中呈现的所有文本都必须<Text>Wrapped in a text tag</Text>
以 16px 的系统字体显示。
当然,你可以将文本样式设置为任何你想要的字体和大小,但文本的形状和大小千差万别,你应该做好应对各种变化的准备。在我们的应用中,我们最终将所有样式化的文本元素放在一个文件中,但我不确定这是否是最佳结构。
说到字体,你肯定想用自定义字体!尤其是现在所有应用都是黑底白字,上面还有一堆线条,除了打字之外,几乎没有其他方法可以区分它们。好消息是,你不用再受@font-face
规则的束缚了,这真是太棒了!
可惜的是,其他一切都很麻烦。你的字体会在 Android 和 iOS 项目中重复出现,而棘手的地方就在这里:要在 Android 中使用字体,你需要引用它的文件名;要在 iOS 上使用,你需要引用它的 Postscript 名称。不知道那是什么?别担心,我也不知道。就是这个:
图像和图标
如果你遵循现代设计趋势,那么现在你的大多数图像都会是平面矢量图像,很可能是内联 SVG,但有个坏消息要告诉你:React Native 中无法使用普通的 SVG。元素不支持它们<Image/>
。这对于图标等元素来说尤其糟糕。那么如何加载图像呢?有几种策略:
对于复杂的形状等,您可以将它们转换为 90 年代风格的位图。您可能需要设置一个构建管道来为您批量处理它们。应用中的所有资源都会预先下载,因此文件大小不像在网页上那样重要(但不要太过分!)为了确保位图清晰,您需要按照@3x
屏幕上的预期大小导出它们。
如果你想远程导入 SVG,这有点棘手,但并非不可能!有几个库可以帮你实现这一点,本质上就是把它们扔进 webview 里。
对于其他所有东西(我正在做这个!),你可以react-native svg
在代码中使用 SVG。它的工作原理是,它会将所有内容的 React Native 版本导出为 SVG,你可以使用这些 SVG,它会为你绘制合适的视图。
SVG 是 React 中的一等公民,具有道具和动画,一切都改变了我看待所有 SVG 的方式。我一直知道它们是标记,但现在必须自己直接调整它们,这给了我很多可以用它们做很酷的事情的想法。
说到底,react-native svg
这是一个非常精妙的 hack,它能提供视图,因此它也可以用作一个底层绘图库,用于绘制线条、圆圈等等!你的想象力才是极限!
评估图片加载策略的一个好方法是问问自己,如果图片加载不出来,会有多乱?例如,你可能希望图标是内联 SVG,而大图则需要远程下载。但要注意,有些东西总是会乱,而且有些用户可能永远都看不到图片,因为他们使用屏幕阅读器,或者视力不好,或者他们根本无法理解圆圈里一个方框里伸出的箭头是什么意思。
务必确保所有图片都有合适的可访问描述符!如果图片无法加载,请提供合理的回退方案(例如,在英雄页面中,使用代码设置背景颜色,使文本具有足够的对比度)。
导航
react-navigation
听起来有点像react-router
这片土地。你可能已经注意到,移动应用的导航类型比网页更高级。你不能只是把东西替换到位,然后把它叫做一个div,如果你看看任何移动应用,你会发现所有的屏幕都是滑进滑出的。react-navigation
有一个与这些过渡高度关联的数据模型。
每个导航器都是一个扁平的屏幕列表,每个屏幕都有一个入口点,并定义了其屏幕之间的过渡。例如,您可以为所有应用使用一个导航器,其中的所有屏幕都会按照从左到右逐渐堆叠的方式进行。
export const RootNavigator = createAppContainer(
createStackNavigator({
Main: HomeScreen,
Downloads: DownloadScreen,
Settings: SettingsScreen,
})
)
但是假设你正在开发一个音乐播放器,并且想要添加一张卡片,可以滑动到任何带有“正在播放”信息的视图上。你可以创建一个新的顶级导航器,其中包含你原来的导航器和那张单独的卡片。你甚至可以直接使用{mode: 'modal'}
它来获取预先制作好的动画,瞧,现在,如果你导航到正在播放的视图,它就会滑过应用的其余部分!
export const RootNavigator = createAppContainer(
createStackNavigator({
Main: createStackNavigator({
Main: HomeScreen,
Downloads: DownloadScreen,
Settings: SettingsScreen,
}),
NowPlaying: NowPlayingScreen,
},
{
mode: 'modal'
}
)
非常酷的是,即使导航器处于层级结构中,路由名称却并非如此。您可以从任何路由导航到任何路由,而无需担心到达顶层。一切就这么简单™。
出于可访问性的原因,你可能想要使用<Link />
这样的方法。如果你用以下方法创建网站,这将使事情变得整洁react-native-web
好消息! react-navigation
它赋予你很多控制权,但代价是重建了很多平台原生的导航视图。如果你的需求比较简单,不妨考虑一下react-native-navigation
哪个版本实现了平台原生的导航栏,但牺牲了灵活性。
总结
关于 React Native,我唯一能说的缺点就是它太好了吧?就像我一开始说的,我还在等待一个重大的“哦不”时刻,那个时刻我错误地假设了太久,导致应用程序的一半都坏了或者出现其他问题。
有趣的是,我的第一个 React(Web)应用就遇到了这种情况!我们临时接到一个要求,要让它在低端手机的三星互联网上运行,而它却是一个 Redux 和 websocket 驱动的庞然大物,我们能做的最好的就是让它在登录界面崩溃,而不是在启动页崩溃。
在我看来,RN 相当不错,但有时它可能会受到一些不公平的批评。Web 开发者害怕它,因为它不是 Web;应用开发者害怕它,因为它是一种不必要的抽象。我个人对它作为编写跨平台应用的解决方案的优雅程度印象深刻,这些应用感觉适合每个平台。我也非常期待最终使用它react-native-web
来构建一个完整的 PWA!
🥳
祈祷这篇文章读起来有趣!我觉得其中有些部分可以写成一本完整的书!我很想听听你对 React Native 中奇怪或有趣的地方的看法,希望这篇文章能激励你开始开发应用程序!
你喜欢这篇文章吗?请告诉我!我想发布一篇后续文章,包含更多动画和性能之类的内容,但我不想让大家对我的 React Native 胡言乱语感到厌烦。
嘘。你可以在推特上关注我@freezydorito
文章来源:https://dev.to/walaura/i-picked-up-react-native-as-a-web-developer-and-here-s-what-i-ve-learned-59h6