前言
最近踏上了学习使用 React Native 进行客户端开发的征途,因为之前的技术栈一直是 Vue, 在大致看了一下React Native 的相关介绍后,感觉有必要首先学习一下ReactJS。再看过相关文档后,以 CNode 社区的 API 上手体验了一下 React 的组件化开发方式,见 cnode-react-App。之后在边看文档边 Google 的过程中,完成了人生中第一次客户端开发。下文中将给出以 CNode 社区为原型进行React Native 开发过程中遇到的问题以及相关思考。
踩坑之路
实践出真知,虽然官方文档已经给出了详细的API以及教程,没有刻骨铭心的踩坑之旅,对于一些问题的理解可能也并不那么透彻。
Github 开源地址
https://github.com/monster1935/cnode-rn-app
App 预览
以下是 App 的部分截图:
已完成功能
目前在现有 CNode Api 的支持下,已经完成以下功能:
- 分类的文章显示
- 文章详情以及文章对应的评论展示,评论包括精彩评论和评论
- 用户的收藏列表、消息列表页面
- 设置、关于、github登录页面(部分页面仅提供了导航占位,后期逐步完善)
- App 内部连接跳转至webview进行访问,点击更多可以刷新、跳转至浏览器访问
- 搜索功能,鉴于目前未提供搜索的接口,目前实现为前端按照检索词进行搜索
- 点击用户头像,跳转到用户信息详情页,展示该用户的最近发布以及最近回复
- 部分操作的权限控制,比如点赞、收藏、收藏列表、信息列表
- 扫码登录流程
- 登录后的个人信息展示以及退出功能
尚未完成以及待优化功能
- IOS 下的调试,目前仅仅测试了在 Android 下的表现
- 回复、发帖功能
- 添加代码区域的友好展示
- 导航的跳转,比如点赞或者收藏时,未登录状态下用户需要跳转至登录页面,登录完成后应定位至点赞、收藏
- 添加 App 启动页面
- 用户详情页的动画存在一定问题
- 异步接口请求的相关控制,组件销毁后,abort相关未完成的请求
技术栈
在开发过程中,用到了社区内较为优秀的一些开源贡献者提供的工具或者包,如下:
导航的实现 react-navigation
react-navigation 是 FaceBook 官方推荐使用的导航组件库,据称有着原生般的性能体验效果。使用起来也确实方便,在笔者进行开发的这段时间,react-navigation 的官方文档也进行了更新。官方文档还算详细,还有其他很多功能等待发掘。
图标 react-native-vector-icons
第三方的图标库,使用起来比较方便,项目中使用了 Ionicons的风格。
全局状态管理 react-redux & redux
使用了 react-redux 进行全局状态管理,主要涉及到登录后token, 用户信息以及文章列表的存储。相关需要登录才能使用的模块也需要获取到全局状态中的token,进行相应的判断。
html 转 view react-native-htmlview
涉及到 web 端 html 在 React Native 上的复用问题,因为两端在 布局、组件以及样式上的机制差异,html 完全的转换 view 不是很现实,但是在一定范围内实现转换还是可以的。react-native-htmlview 是目前做的较为优秀的,不过在笔者的使用过程中发现了一些问题,最终还是clone了其代码,又在本地修改才解决的,下文会详细解释。
持久化存储 react-native-storage
用于登录的token的持久化存储,每次启动 App 后,如果之前曾经登录不必重复登录。其不仅支持 React Native, 也提供了浏览器端的支持。
ActionSheet, 用于 webview 中的更多弹出面板 react-native-actionsheet
目前 app 中的链接均采用 app 内部的 webview 进行显示,显示的过程中添加了progress 进度条,并添加了刷新以及在浏览器中打开功能。
二维码扫描组件, 基于 react-native-camera的封装
基于 react-native-camera 封装的二维码扫码组件,思路借鉴了 react-native-qrcode的实现方法。
WebView 中的进度条显示 react-native-progress
一个更加友好的展示,在网页请求的过程中添加了顶部进度条
遇到的问题
1. react-navigation 中使用可滑动的tab view, 滑动不生效
初次使用react-navigation 后,在首页添加了react-native-scrollable-tab-view, 发现滚动根本不起作用,查阅了相关资料,设置 TabNavigator的 swipeEnabled: false
即可。
2. 使用 react-native-htmlview 转换 html 字符串至 react native View 的过程,发现图片显示较小
在使用的过程中发现图片显示异常,查看其 issue,发现有同样的开发者遇到这样的问题。查阅相关资料,React Native Text Inline Image, 这篇文章中解释了为什么内嵌于 Text 中的 Image 在 Android 上显示过小的问题。原因在于 React Native 的内部的处理问题,React Native 在这种情况下直接以 图片的原始尺寸进行显示,并未再该尺寸的基础上乘以 图片的 PixelRatio。作者给出了一种解决办法就是封装一个InlineImage 组件,在传入的尺寸属性上手动乘以 PixelRatio
。
3. Android 上 Inline Image 通过 Image.getSize()
后无法更新图片大小
一波未平,一波又起。刚弄明白 Inline Image 显示过小的问题,发现还是无法更加优雅的设置图片的尺寸。原始思路是通过 Image.getSize()后更新图片的尺寸。遗憾的是发现,图片不会更新大小,至今未发现原因。(持续跟进)
4. 动手修改 react-native-htmlview,完美解决 Image 显示问题
为了更加优雅的显示文章的图片,阅读了 react-native-htmlview 的源码,发现之所以会出现 Inline Image 的问题,主要原因是因为其 Image 是内嵌于 Text 节点,虽然其暴露了 NodeComponent 的 props, 不传的话默认为 Text。问题出现的原因,在于含有 img 标签的父标签被渲染为了 Text。因此在源码中添加了以下内容:
// 节点 后代中如果含有 img ,则该节点渲染为 View
function _contains(children) {
for(let i = 0, len = children.length; i < len; i++) {
if (children[i].type === 'tag' && children[i].name === 'img' || _contains(children[i].children || [])) {
return true;
}
}
return false;
}
...
// 标签如果是div 或者 含有img标签的父标签均渲染为View
if (node.type === 'tag') {
if (node.name === 'div' || _contains(node.children)) {
opts.NodeComponent = View;
} else {
opts.NodeComponent = Text;
}
}
完美解决了 Image 的显示问题。
总结
React Native 的开发如果有 React 的开发经验,上手还是比较快的。主要是熟悉其组件的使用方式以及不同组件之间的区别。目前仅仅实现了一个 Demo 版本,渲染效率以及内存消耗等问题还未优化。后期对React Native 有深入的理解后,会进行相关方面的优化。
最后
技术学习的路上已经使用 CNodejs 的 Api 做过了好几个 demo,包括:
感谢 CNode社区提供的 Api,给广大开发者提供了学习练手的机会。