Repository: cnsnake11/blog Branch: master Commit: b9f6f85f0f2a Files: 131 Total size: 23.0 MB Directory structure: gitextract_zekn0oua/ ├── HybirdAPI/ │ ├── AudioPlayer.md │ ├── AudioRecorder.md │ └── index.md ├── README.md ├── ReactNative开发指导/ │ ├── CodePush.md │ ├── ReactNative入门知识.md │ ├── ReactNative分享.md │ ├── ReactNative增量升级方案.md │ ├── ReactNative安卓首屏白屏优化.md │ ├── ReactNative导航设计与实现.md │ ├── ReactNative开发技巧总结.md │ ├── ReactNative打离线包-android篇.md │ ├── ReactNative打离线包-ios篇.md │ ├── ReactNative的ios崩溃整理分析.md │ ├── ReactNative的架构设计.md │ ├── ReactNative组件状态设计思考.md │ ├── ReactNative调试菜单.md │ ├── React的render优化框架.md │ ├── 已有工程集成ReactNaitve-IOS.md │ └── 淘宝d2分享-ReactNative变革无线前端.md ├── ReactNative开发规范/ │ ├── 目录与命名规范.md │ ├── 组件、页面结构规范.md │ └── 编码规范.md ├── ReactNative翻译/ │ ├── React Advanced Performance.md │ ├── React PureRenderMixin.md │ ├── React Shallow Compare.md │ ├── ReactNative : A warning from Apple.md │ ├── Reconciliation.md │ ├── react-native的定时器.md │ ├── react-native的性能.md │ └── 导航器比较.md ├── ReactNative问题汇总/ │ ├── Immutable.js的问题.md │ ├── ReactNative-0.14.2.md │ ├── ReactNative-0.15.0.md │ ├── ReactNative-0.16.0.md │ ├── ReactNative-0.17.0.md │ └── 总体说明.md ├── data.json ├── xmind/ │ ├── 20160125RN增量升级讨论会.xmind │ ├── 201602Todo.xmind │ ├── 2016规划.xmind │ ├── RN分享议题PPT大纲.xmind │ ├── RN安卓启动优化.xmind │ ├── RN阶段总结-里程碑2.xmind │ ├── react环境搭建.xmind │ ├── web性能监控方案.xmind │ ├── web资源缓存.xmind │ ├── 增量升级.xmind │ ├── 增量升级新方案.xmind │ ├── 孕育集成能不能吃-IOS.xmind │ ├── 已有App集成RN.xmind │ ├── 文档编写大纲.xmind │ └── 腾讯x5集成.xmind ├── 其它/ │ ├── 2 │ ├── Building the DOM faster: speculative parsing, async, defer and preload(part 1).md │ ├── Building the DOM faster: speculative parsing, async, defer and preload(part 2).md │ ├── Building the DOM faster: speculative parsing, async, defer and preload(part 3).md │ ├── Designing Websites for iPhone X.md │ ├── FlatList.md │ ├── How TensorFlow Lite Brings Machine Learning to Mobile Devices.md │ ├── PanResponder.md │ ├── RN分享提纲.md │ ├── Range.md │ ├── ReactNative分享议题.md │ ├── ReactNative分享议题PPT大纲.md │ ├── Scrollview.md │ ├── Selection.md │ ├── Understanding the WebView Viewport in iOS 11 ~ 1.md │ ├── Understanding the WebView Viewport in iOS 11 ~ 2.md │ ├── WebAssembly(1) A cartoon intro to WebAssembly.md │ ├── WebAssembly(2-1)A crash course in just-in-time (JIT) compilers.md │ ├── WebAssembly(2-2)A crash course in just-in-time (JIT) compilers.md │ ├── WebAssembly(3)A crash course in assembly.md │ ├── WebAssembly(4-1)Creating and working with WebAssembly modules.md │ ├── WebAssembly(4-2)Creating and working with WebAssembly modules.md │ ├── WebAssembly(5-1)What makes WebAssembly fast?.md │ ├── WebAssembly(5-2)What makes WebAssembly fast?.md │ ├── WebAssembly(6-1)Where is WebAssembly now and what’s next?.md │ ├── WebAssembly(6-2)Where is WebAssembly now and what’s next?.md │ ├── ios11和iPhone X中Webview的适配.md │ ├── node与shell的交互与通讯.md │ ├── node中文件操作的简单介绍.md │ ├── package.json中版本号的含义.md │ ├── performance.timing.md │ ├── rn分享5.md │ ├── 一个类似微信群聊程序的技术方案设计过程.md │ ├── 了解一个新概念---区块链.md │ ├── 前端和开闭原则.md │ ├── 前端应该掌握的一些app开发知识-android篇(1).md │ ├── 前端应该掌握的一些app开发知识-android篇(2).md │ ├── 前端应该掌握的一些app开发知识-ios篇(1).md │ ├── 前端应该掌握的一些app开发知识-ios篇(2).md │ ├── 前端监控平台.md │ ├── 前端资源打包设计.md │ ├── 富文本编辑器1~html转结构化数据.md │ ├── 富文本编辑器2~复制粘贴的处理.md │ ├── 富文本编辑器3~自定义元素.md │ ├── 富文本编辑器4~站外图片的粘贴.md │ ├── 富文本编辑器的命令执行.md │ ├── 开坑机器学习?.md │ ├── 弱特征广告方案.md │ ├── 微信二次分享.md │ ├── 数据结构--树型数据的处理(1).md │ ├── 数据结构--树形数据的处理2.md │ ├── 机器学习-入门篇(1).md │ ├── 机器学习-入门篇(2).md │ ├── 机器学习-入门篇(3).md │ ├── 机器学习-入门篇(4).md │ ├── 架构/ │ │ └── 通用组件规范.md │ └── 模式/ │ ├── 代理模式.md │ ├── 单例模式(1).md │ ├── 单例模式(2).md │ ├── 原型模式.md │ ├── 多个工厂方法模式.md │ ├── 建造者模式.md │ ├── 抽象工厂模式.md │ ├── 普通工厂模式.md │ ├── 桥接模式.md │ ├── 装饰器模式.md │ ├── 门面模式.md │ └── 静态工厂方法模式.md └── 日常记录与计划/ ├── 2015总结与2016畅想.md ├── 2016-09-13.md ├── todo list.md ├── week2015-11-23.md ├── week2015-11-30.md ├── week2015-12-07.md ├── week2015-12-14.md ├── week2016-01-04.md ├── 总体计划1.md └── 总体计划2.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: HybirdAPI/AudioPlayer.md ================================================ # AudioPlayer # 概述 音频播放对象,播放本地音频。 注:网络音频请使用h5的audio标签。 # 常量 ### TYPE_SPEAKER ### TYPE_EARPIECE # 方法 ### play ### pause ### stop ### setCurrentTime(long ms) ### (long)getDuration ### (long)getCurrentTime ### setType(int AudioPlayer.TYPE_*) # 事件 ### onPlay ### onPause ### onFail ### onEnded ### onTimeupdate ================================================ FILE: HybirdAPI/AudioRecorder.md ================================================ # AudioRecorder # 概述 调用麦克风进行录音操作 # 方法 ### record filePath samplerate format ### stop ### getFilePath ### clear # 事件 ### onRecord ### onFail ### onEnded ### onTimeupdate ================================================ FILE: HybirdAPI/index.md ================================================ # HybirdAPI # 概述 此系列文档是为了描述app中如何提供一套统一的原生接口,供js调用。 app中webview中的js可以调用。 app中reactnative模块可以调用。 ================================================ FILE: README.md ================================================ # 文章目录 大家有问题欢迎留言issues讨论。 ### [【翻译】理解ios11中的webview viewport(第一部分)](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/Understanding%20the%20WebView%20Viewport%20in%20iOS%2011%20~%201.md) ### [【翻译】让网站适配iphoneX](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/Designing%20Websites%20for%20iPhone%20X.md) ### [【原创】富文本编辑器4~站外图片的粘贴](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E5%AF%8C%E6%96%87%E6%9C%AC%E7%BC%96%E8%BE%91%E5%99%A84~%E7%AB%99%E5%A4%96%E5%9B%BE%E7%89%87%E7%9A%84%E7%B2%98%E8%B4%B4.md) ### [【原创】富文本编辑器3~自定义元素](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E5%AF%8C%E6%96%87%E6%9C%AC%E7%BC%96%E8%BE%91%E5%99%A83~%E8%87%AA%E5%AE%9A%E4%B9%89%E5%85%83%E7%B4%A0.md) ### [【原创】富文本编辑器2~复制粘贴的处理](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E5%AF%8C%E6%96%87%E6%9C%AC%E7%BC%96%E8%BE%91%E5%99%A82~%E5%A4%8D%E5%88%B6%E7%B2%98%E8%B4%B4%E7%9A%84%E5%A4%84%E7%90%86.md) ### [【原创】富文本编辑器1~html转结构化数据](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E5%AF%8C%E6%96%87%E6%9C%AC%E7%BC%96%E8%BE%91%E5%99%A81~html%E8%BD%AC%E7%BB%93%E6%9E%84%E5%8C%96%E6%95%B0%E6%8D%AE.md) ### [【翻译】ReactNative:关于苹果审核警告](https://github.com/cnsnake11/blog/blob/master/ReactNative%E7%BF%BB%E8%AF%91/ReactNative%20:%20A%20warning%20from%20Apple.md) ### [【翻译】Reconciliation React比对算法](https://github.com/cnsnake11/blog/blob/master/ReactNative%E7%BF%BB%E8%AF%91/Reconciliation.md) ### [【原创】数据结构--树形数据的处理(2)](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84--%E6%A0%91%E5%BD%A2%E6%95%B0%E6%8D%AE%E7%9A%84%E5%A4%84%E7%90%862.md) ### [【原创】数据结构--树形数据的处理(1)](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84--%E6%A0%91%E5%9E%8B%E6%95%B0%E6%8D%AE%E7%9A%84%E5%A4%84%E7%90%86(1).md) ### [【翻译】React Advanced Performance 性能进阶](https://github.com/cnsnake11/blog/blob/master/ReactNative%E7%BF%BB%E8%AF%91/React%20Advanced%20Performance.md) ### [【原创】弱特征广告方案](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E5%BC%B1%E7%89%B9%E5%BE%81%E5%B9%BF%E5%91%8A%E6%96%B9%E6%A1%88.md) ### [【原创】前端和开闭原则](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E5%89%8D%E7%AB%AF%E5%92%8C%E5%BC%80%E9%97%AD%E5%8E%9F%E5%88%99.md) ### [【原创】React的render优化通用解决方案](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/React%E7%9A%84render%E4%BC%98%E5%8C%96%E6%A1%86%E6%9E%B6.md) ### [【原创】一个类似微信群聊程序的技术方案设计过程](https://github.com/cnsnake11/blog/blob/master/%E5%85%B6%E5%AE%83/%E4%B8%80%E4%B8%AA%E7%B1%BB%E4%BC%BC%E5%BE%AE%E4%BF%A1%E7%BE%A4%E8%81%8A%E7%A8%8B%E5%BA%8F%E7%9A%84%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88%E8%AE%BE%E8%AE%A1%E8%BF%87%E7%A8%8B.md) ### [【原创】ReactNative的ios崩溃整理分析](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E7%9A%84ios%E5%B4%A9%E6%BA%83%E6%95%B4%E7%90%86%E5%88%86%E6%9E%90.md) ### [【原创】ReactNative团队分享提纲](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%88%86%E4%BA%AB.md) ### [【翻译】React PureRenderMixin](https://github.com/cnsnake11/blog/blob/master/ReactNative%E7%BF%BB%E8%AF%91/React%20PureRenderMixin.md) ### [【原创】ReactNative增量升级解决方案](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%A2%9E%E9%87%8F%E5%8D%87%E7%BA%A7%E6%96%B9%E6%A1%88.md) ### [【翻译】React 浅比对 Shallow Compare](https://github.com/cnsnake11/blog/blob/master/ReactNative%E7%BF%BB%E8%AF%91/React%20Shallow%20Compare.md) ### [【原创】ReactNative安卓首屏白屏优化](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%AE%89%E5%8D%93%E9%A6%96%E5%B1%8F%E7%99%BD%E5%B1%8F%E4%BC%98%E5%8C%96.md) ### [【原创】ReactNative导航设计与实现](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%AF%BC%E8%88%AA%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0.md) ### [【原创】ReactNative打离线包-android篇](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E6%89%93%E7%A6%BB%E7%BA%BF%E5%8C%85-android%E7%AF%87.md) ### [【原创】ReactNative打离线包-ios篇](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E6%89%93%E7%A6%BB%E7%BA%BF%E5%8C%85-ios%E7%AF%87.md) ### [【原创】ReactNative的组件架构设计](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E7%9A%84%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1.md) ### [【原创】ReactNative组件状态设计思考](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E7%BB%84%E4%BB%B6%E7%8A%B6%E6%80%81%E8%AE%BE%E8%AE%A1%E6%80%9D%E8%80%83.md) ### [【翻译】ReactNative的定时器Timers](https://github.com/cnsnake11/blog/blob/master/ReactNative%E7%BF%BB%E8%AF%91/react-native%E7%9A%84%E5%AE%9A%E6%97%B6%E5%99%A8.md) ### [【原创】已有工程集成ReactNative-IOS](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/%E5%B7%B2%E6%9C%89%E5%B7%A5%E7%A8%8B%E9%9B%86%E6%88%90ReactNaitve-IOS.md) ### [【翻译】ReactNative的性能](https://github.com/cnsnake11/blog/blob/master/ReactNative%E7%BF%BB%E8%AF%91/react-native%E7%9A%84%E6%80%A7%E8%83%BD.md) ### [【原创】ReactNative的组件架构设计](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E7%9A%84%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1.md) ### [【转载】ReactNative变革无线前端-淘宝d2分享](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/%E6%B7%98%E5%AE%9Dd2%E5%88%86%E4%BA%AB-ReactNative%E5%8F%98%E9%9D%A9%E6%97%A0%E7%BA%BF%E5%89%8D%E7%AB%AF.md) ### [【原创】CodePush](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/CodePush.md) ### [【原创】ReactNative开发技巧总结](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%BC%80%E5%8F%91%E6%8A%80%E5%B7%A7%E6%80%BB%E7%BB%93.md) ### [【原创】ReactNative入门知识](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%85%A5%E9%97%A8%E7%9F%A5%E8%AF%86.md) ================================================ FILE: ReactNative开发指导/CodePush.md ================================================ #CodePush https://github.com/Microsoft/react-native-code-push #重要说明 While the service is in beta, we don’t recommend using it for doing production deployments, since we don’t currently offer any specific SLA.因为现在是beta版本,不建议用做生产版本的发布。 确实不能用于生产,目前只在美国有服务器,大陆访问非常慢。 所以,增量升级只能自力更生了。 #工作方式 可以只升级Rn的js和图片,不用使用二进制包升级。 通过CodePush服务器进行升级。 服务端目前没有开源,不能用自己的服务器 原生代码的改动是不能通过codepush升级的。 # 支持平台 Android (asset support coming soon!) 安卓暂不支持asset #基本流程 1. 安装CodePush CLI 2. 创建codePush账户 3. 注册app到codepush服务器 4. app中加入codepush客户端代码,并开发升级相关代码。 5. 发布更新到codepush服务器 6. app收到了升级推送 具体配置过程请参考官网 网上资料参考 #下一步工作 因为codepush服务器端不开源,而且服务器还在国外的关系,不能保证服务端的稳定性,所以很遗憾不能应用于真正的项目中。 针对目前的情况,制定下一步工作如下: 1. 研读codepush客户端源码,理解android和ios在客户端下载资源文件后,如何切换到新资源的方法。 2. 设计服务端,服务端仅提供文件资源下载服务和升级信息描述服务即可,不需要较复杂的逻辑。 3. 设计自动化打包解决方案,包括升级补丁打包,完整app打包。 #源码阅读记录 1. js版本同时需要考虑对app版本的依赖问题,例如,js1.0版本必须运行在iosApp2.0,安卓app2.6上等。 2. 提供了一系列js接口,方便应用更细节的控制升级的过程。 3. 对于assets中的图片内容,可以自动计算与上一版本的差别,只下载增量部分。 4. 如果app升级了,js应自动使用app中版本。todo一个升级算法状态图。 4. 客户端应有2部分组件-codepush文档很详细,可以参考 1. native部分,安卓和ios分别提供启动时bundle资源位置的服务 2. js部分,提供对更新和下载的细粒度控制接口. ================================================ FILE: ReactNative开发指导/ReactNative入门知识.md ================================================ # ReactNative入门知识 本文也可以作为RN的入门知识范围,为入门RN提供了一个知识大纲。掌握这些知识帮助你快速入门RN,少走弯路。 #es6 let和const 箭头函数 类定义 解构赋值Destructuring 函数参数的默认值 函数的rest参数【形式为“...变量名”】;及其逆运算; import export 参考地址:http://facebook.github.io/react-native/docs/javascript-environment.html#content 参考地址:http://www.ghugo.com/react-native-es6/ #Flow #react jsx 生命周期事件 render方法 state相关 props相关 #react-naive常用组件 view text touch image textInput scrollview listview navigator #flexbox #Dimensions #fetch #console 断点 #定时任务 参考地址:https://github.com/cnsnake11/blog/blob/master/ReactNative翻译/react-native的定时器.md #交互管理器 参考地址:https://github.com/cnsnake11/blog/blob/master/ReactNative翻译/react-native的定时器.md #调试 console.log() 在chrome中调试js #Immutable.js ================================================ FILE: ReactNative开发指导/ReactNative分享.md ================================================ # ReactNative分享提纲 # 分享的目的 1. 了解rn是什么,特点和使用场景 2. 本次重点是学习架构(思想)而不是学习框架(用法) 3. 学习组件化的思想及其好处,目标是能运用到工作中 4. 希望随时打断进行交流讨论 # reactnative是个啥 用js写原生app的开源框架,目前官方支持ios和android。 facebook2015年发布。社区关注度非常高。版本迭代很快,基本上2周左右一个版本。 # 安卓demo包下载 [RnDemo.apk,9MB](http://pan.baidu.com/s/1mhX6VoO) # 优势 ## 开发体验 1. 改代码,即时编译,即时加载 1. 兼容性好,代码可预测,各系统的模拟器、各型号的真机基本无差异 1. 组件化支撑,代码更易复用 1. flexbox布局,自适应布局更容易 1. 手势操控系统,更易开发体验佳的触屏应用 1. 支持应用内更新,自由控制发版 1. es6新特性,各种语法黑魔法 1. 箭头函数 1. promise 1. 解构赋值 1. 类定义、继承 1. fetch 2. 等 ## 用户体验 1. 加载性能好,无白屏 2. 动画性能好,无卡顿 2. 顺畅的手势操作 # 劣势 1. 无web版,分享页等还要开发,react-web目前还不成熟 1. 无选择器的概念,定义样式很繁琐,通过开发规范部分解决 1. 尚未发布1.0 程序员首先要思考的是让用户爽,然后才是让自己爽。如果按此原则的话,劣势还是在我们接受范围内的。(用户体验和开发体验同样重要,最好是有更完美的解决方案。) # react基本概念 ## 组件化编程 ### 组件化定义 组件的定义:独立的可完成某些工作的部件。可能是一个button,或者一个util类。 组件化编程的目的:复用复用复用。站在别人肩膀上可以极大的提升开发测试效率。 遇到的困难:个性化定制。通过属性、事件等扩展。 举个web组件的例子,参考此文档的前半部分:[链接](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E7%BB%84%E4%BB%B6%E7%8A%B6%E6%80%81%E8%AE%BE%E8%AE%A1%E6%80%9D%E8%80%83.md) ### react做了什么 react是一个组件化编程的框架,提供了组件化编程的完整解决方案。[例子](http://facebook.github.io/react/) react组件的定义。 [链接](http://facebook.github.io/react/docs/component-specs.html) react组件生命周期及其生命周期事件。[链接](http://facebook.github.io/react/docs/component-specs.html#lifecycle-methods) ## 面向状态编程 页面中任何组件的state的改变,都会引起页面所有组件的render方法的执行。react会根据render执行的结果与当前的虚拟dom进行比对,只进行增量的更新。[链接](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E7%BB%84%E4%BB%B6%E7%8A%B6%E6%80%81%E8%AE%BE%E8%AE%A1%E6%80%9D%E8%80%83.md#rn设计思路) ## jsx js中的xml。[例子](http://facebook.github.io/react/) # reactnative基本概念 ## 和react的关系 1. rn使用react的组件化规范。 2. rn使用react的面向状态编程的机制。 3. rn使用react的jsx机制。 4. rn提供了一系列app开发所需要的组件。 ## 常用组件 1. div View 2. span Text 3. img Image 4. input TextInput 5. button Touch* ## 样式定义 不支持选择器。不支持选择器。不支持选择器。 只能内联或者在每一个节点上声明外部css定义。所以,一般没有复用的样式使用内联,有复用的才使用外部定义。 [链接](http://reactnative.cn/docs/0.24/style.html#content) # 开发环境 ## ios xcode+app的ios代码+js编辑器+rn开发服务器+真机or模拟器 ## android jdk+androidSdk+androidStudio+app的android代码+js编辑器+rn开发服务器+真机or模拟器 # 开发过程 1. 启动rn开发服务器 2. 启动模拟器,点开app,设置自动reload 3. 启动js代码编辑器,coding---save---看效果 4. tips:可以直接让首页显示你要开发的页面,可以快速查看效果 ``` // tips用到的代码,替换index页面的最后一行 AppRegistry.registerComponent('Ask', () => require('../../../demo/demo1/animate/drag2')); ``` # 调试过程 1. console.log 2. js断点调试 3. alert 3. 页面元素检测器 # 组件设计 [链接](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E7%9A%84%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1.md) # 坑坑坑 1. android的textinput不能设置边框圆角 1. android的图片resizemodal=contain不好用 1. 循环依赖报错的问题 1. 安卓rn首屏白屏时间长 1. 安卓的中绝对定位元素移动出父元素会不可见,设置overflow=hidden也不好使 1. 不支持隐藏元素 1. 安卓中,使用缓存rootview,并且键盘打开情况下,直接后退出rn模块,在进入会报错 1. 魅族smartbar不占高 1. android中,设置overflow=hidden,父容器高度不能为0,至少为0.01,否则hidden无效 1. 加固后动画有掉帧现象 # 架构设计 ``` 业务模块 业务框架 技术框架 reactnative android、ios操作系统 ``` # 框架设计 参考技术框架和业务框架的目录结构介绍。 # 版本发布 ## 全量发布 全量发布是将rn相关资源打包进app中,是无需网络下载的。 一般过程: 1. coding结束 2. 使用全量打包命令,打出android和ios全量包 3. 将包拷贝到android和ios工程约定好的位置 4. 按照android和ios原有打包方式进行打包即可 ## 增量发布 [增量升级方案](https://github.com/cnsnake11/blog/blob/master/ReactNative%E5%BC%80%E5%8F%91%E6%8C%87%E5%AF%BC/ReactNative%E5%A2%9E%E9%87%8F%E5%8D%87%E7%BA%A7%E6%96%B9%E6%A1%88.md) 一般过程: 1. coding结束 2. 使用全量打包命令,打出android和ios全量包 3. 使用增量打包命令,打出android和ios增量包 4. 将增量包上传到后台管理系统,上线即可 ================================================ FILE: ReactNative开发指导/ReactNative增量升级方案.md ================================================ # ReactNative增量升级方案 # 前言 facebook的react-native给我们带来了用js写出原生应用的同时,也使得使用RN编写的代码的在线升级变得可能,终于可以不通过应用市场来进行升级,极大的提升了app修bug和赋予新功能的能力。----使用h5的方式也可以做到,但是rn的用户体验可要远远超过h5啊。 一般使用RN编写的app的线上使用方式,是将react-native bundle命令打出bundle文件和assets文件夹,直接内置到app中,app在viewcontroller或者activity中直接加载app内部的bundle文件,比如下图。 ![](media/14534499414604.jpg) 当修改了代码或者图片的时候,只要app使用新的bundle文件和assets文件夹,就完成了一次在线升级。 本文主要基于以上思路,讲解增量升级的解决方案。 # 何为增量? 一个完整的RN-app程序通常包含以下几个部分: 1. native代码部分-objc或者java 2. js代码部分-rn代码、依赖的第三方库、业务代码等 3. 图片资源部分 native代码别想了,没法在线升级,要是能大家就都不使用应用市场ota升级了。 能进行在线升级的是js代码部分和图片资源部分,具体到代码就是bundle文件和assets文件夹。 因为在线升级是要走网络的,我们要想办法将网络消耗降到最低,所以要使用增量升级的方式。 针对js代码部分(即bundle文件)的增量指的是,代码的改动有多少,增量patch的补丁就有多少,那些没有改动的代码部分是不在补丁的范围内的。 针对图片部分(即assets)的增量指的是,升级补丁包中只包含新增的图片和有改动的图片。 那么在app端,下载升级补丁包,只需要和现有的版本进行合并,就能计算出最新版本的全量包。 总结下流程:()中为例子 首先,计算增量包:新版本(v10) - 旧版本(v1到v9) = 增量包 (会有9个包,v1~v10.zip,v2~v10.zip,,,,,v9-v10.zip) 然后,app根据自己的当前版本(比如V6),下载对应的增量包(V6-V10.zip)。 最后,app中通过 旧版本(v6) + 增量包(v6~v10.zip) = 新版本(v10) ,计算出了新版本的全量包。 # 增量算法 assets增量算法,比较简单,就是比对,可以很容易的比较出新增的文件,和不同的文件(使用md5)。 bundle文件的增量算法,确实比较复杂,刚开始没有什么头绪,后来在leader的指引下,很幸运的找到了google写的一个开源的库,可以对大字符串进行diff和patch,并且支持java、objc、js等等语言,完全的满足了我们的需求。 只用到2个接口,具体请参考github上的文档 1. 生成增量包时候:patch_make(text1, text2) => patches 2. app生成全量包时候:patch_apply(patches, text1) => [text2, results] google开源库地址:https://github.com/bystep15/google-diff-match-patch # codepush 微软的codepush也做了类似的事情,不过由于以下原因,我们团队没敢使用其作为解决方案。 1. 其增量升级仅仅是针对图片资源的 2. 其升级服务器端程序并不开源 3. 其升级服务器在美国,国内访问很慢且不稳定 不过,codepush客户端的源码和文档也给我们提供了很多思路,在此感谢codepush团队。 codepush地址:http://microsoft.github.io/code-push/ # bundle要求的app最小版本 本文中一般用min-v或者appMinV表示。 因为js代码是依赖于native代码的,所以,jsbundle对app的版本有要求,所以有这个概念。 试想,如果bundle依赖了一个native的一个新的接口,这个接口在v3版本的app中才发布,如果v2版本的app升级了这个bundle,那么必然会报错,严重的可能会导致app的崩溃。 # 系统结构设计与各模块职责 ![312313123](media/312313123.png) # bundle仓库设计 存储全量bundle和生成增量patch ## node patch 命令 在bundle目录下放入一个符合要求【参考目录结构说明】的新版本目录,比如0.3.0,然后执行以下命令。 命令:node patch 版本号 , 示例:node patch 0.3.0 -d参数: node patch 版本号 -d ,如果加入-d参数,会先删除patch目录下的对应版本目录,然后进行patch生成 然后在patch目录中就会生成0.3.0的增量包,同时patch目录下的update.json文件也会重新生成. ## node update.json 命令 在patch目录下重新生成update.json文件 ## 目录结构说明 ![](media/14534529649291.jpg) ``` bundle 存放全量bundle和全量assets的目录,里面的文件基本上是使用react-native bundle命令生成的 0.1.0 略 0.2.0 android 略,同ios ios config.json 此版本的配置信息,包含要求app的最低版本等,手动配置 index.jsbundle 全量jsbundle文件,使用react-native bundle命令生成 assets 全量图片目录,使用react-native bundle命令生成 0.3.0 略 patch 存放增量补丁的目录,里面文件都是命令生成的,无需手动维护 0.1.0 第一版本无文件 0.2.0 android 略,同ios ios 0.1.0-0.2.0.zip 增量包.zip 0.3.0 android 略,同ios ios 0.1.0-0.3.0.zip 增量包.zip 0.2.0-0.3.0.zip 增量包.zip update.json 所有的升级包信息 src 存放打包用的源码 lib 存放打包用依赖的第三方的源码 patch.js patch命令入口 update.json.js update.json命令入口 ``` ## config.json示例 ``` { "v": "0.3.0", //版本 "min-v": "4.0.0", //此版本要求的最小app版本 "date": "2016-01-01", //打包日期 "des": [ "修复xxbug", "添加xx功能" ] } ``` ## update.json示例 ``` [ { "v": "0.1.0", "min-v": "4.0.0", "date": "2016-01-01", "des": [ "修复xxbug", "添加xx功能" ], "iosBundleMd5": "11f82563f8fd3f22dccb80ad2297f7bc", "androidBundleMd5": "11f82563f8fd3f22dccb80ad2297f7bc" }, { "v": "0.2.0", "min-v": "4.0.0", "date": "2016-01-01", "des": [ "修复xxbug", "添加xx功能" ], "iosBundleMd5": "3ca2824b008132cee515c0ea29938ff2", "androidBundleMd5": "3ca2824b008132cee515c0ea29938ff2" }, { "v": "0.3.0", "min-v": "4.0.0", "date": "2016-01-01", "des": [ "修复xxbug", "添加xx功能" ], "iosBundleMd5": "dbb81d2383112abb50eb19970c486acd", "androidBundleMd5": "dbb81d2383112abb50eb19970c486acd" } ] ``` # 升级服务器设计 ## 接口patch/query 例如:http://localhost:3000/patch/query?bundleV=0.2.0&appV=4.0.0&platform=ios 此接口会有以下4个场景的使用情况,每个场景返回的json示例已经提供在后面文档中。 ### 输入参数 ``` bundleV : app中的bundle版本 appV : app版本 platform : app的平台 ``` ### 返回json各项说明 1. status : 本次请求后台是否发生了错误 1. msg : 给用户看的中文提示信息,静默升级时候没什么用 1. latestBundleV : 当前的最新bundle版本 1. latestAppMinV : 最新bundle要求的app最低版本 1. canUpdate : 能否升级,boolean 1. canUpdateBundleV : 能升级的bundle版本 1. canUpdateAppMinV : 能升级的bundle要求的app最低版本 1. patchUrl : 补丁包相对地址 1. platform : 平台:ios or android ### 场景1 能升级,且能升级到最新版 ``` { status: 'success', msg: '可以升级,bundle最新版为0.3.0', latestBundleV: '0.3.0', latestAppMinV: '4.0.0', canUpdate: true, canUpdateBundleV: '0.3.0', canUpdateAppMinV: '4.0.0', patchUrl: 'patch/0.3.0/ios/0.2.0-0.3.0.zip', platform: 'ios' } ``` ### 场景2 无需升级,已经是最新版本 ``` { status: 'success', msg: '无需升级,已经是最新版本', canUpdate: false, platform: 'ios' } ``` ### 场景3 能升级,能升级到当前appMinV的最新bundle版本,但不是最新的bundle,想升最新bundle,必须先升app ``` { status: 'success', msg: '可以升级,但app版本3.0.0太低,只能升到bundleV0.2.0,bundleV最新为0.3.0', latestBundleV: '0.3.0', latestAppMinV: '4.0.0', canUpdate: true, canUpdateBundleV: '0.2.0', canUpdateAppMinV: '3.0.0', patchUrl: 'patch/0.2.0/ios/0.1.0-0.2.0.zip', platform: 'ios' } ``` ### 场景4 不能升级,已经是当前appMinV的最新bundle,但不是最新的bundle,想升最新bundle,必须先升app ``` { status: 'success', msg: '不能升级,当前已经是app3.0.0的最新bundle了,但不是最新bundle0.3.0,想升级bundle到最新,请先升级app', latestBundleV: '0.3.0', latestAppMinV: '4.0.0', canUpdate: false, platform: 'ios' } ``` # native客户端设计 客户端的主要工作就是发请求到升级服务器询问是否能升级,然后根据返回的信息,下载升级包,解压升级包,安装升级包。 过程中要保证下载的文件是正确的(md5校验),要保证补丁安装之后的全量bundle文件是正确的(md5校验)。 整个过程用户无感知。 此部分详细设计后边会补充。 # 遗留问题 目前rn将安卓的图片放入到res目录中,导致安卓图片不能使用在线升级的解决方案,但是codepush的作者已经重构了此部分内容,并提pr到rn,rn团队也接受了这个pr,会在近期的版本中发布。 参考地址:https://github.com/facebook/react-native/pull/4527 # 近期开源 本部分内容核心的几个部分都已经完成,近期完善之后,会开源出来。 ================================================ FILE: ReactNative开发指导/ReactNative安卓首屏白屏优化.md ================================================ # ReactNative安卓首屏白屏优化 #问题描述 公司现有app中部分模块使用reactnative开发,在实施的过程中,rn良好的兼容性,极佳的加载、动画性能,提升了我们的开发、测试效率,提升了用户体验。 但是,在android中,当点击某个rn模块的入口按钮,弹出rn的activity到rn的页面展现出来的过程中,会有很明显的白屏现象,不同的机型不同(cpu好的白屏时间短),大概1s到2s的时间。 注意,只有在真机上才会有此现象,在模拟器上没有此现象完全是秒开。ios上也是秒开,测试的最低版本是ios7,iphone4s。 reactnative版本0.20.0。 jsbundle文件大小717kb。 #优化效果 经过了大量的源码阅读,和网上资料查找,最终完美的解决了这个问题,无论什么机型,都可以达到秒开,如图(虽然下图是模拟器的截图,但是真机效果基本一样): ![1](media/1.gif) #优化过程 ##时间分布 一般优化速度问题,首先就是要找到时间分布,然后根据二八原则,先优化耗时最长的部分。 android集成rn都会继承官方提供的ReactActivity ``` public class MainActivity extends ReactActivity { ``` 然后只在自己的activity中覆盖一些配置项。 在官方的ReactActivity中的onCreate方法中 ``` protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) { // Get permission to show redbox in dev builds. if (!Settings.canDrawOverlays(this)) { Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); startActivity(serviceIntent); FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE); Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show(); } } mReactInstanceManager = createReactInstanceManager(); ReactRootView mReactRootView = createRootView(); mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions()); setContentView(mReactRootView); } ``` 最慢的就是这两行代码,占了90%以上的时间。 ``` ReactRootView mReactRootView = createRootView(); mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions()); ``` 这两行代码就是把jsbundle文件读入到内存中,并进行执行,然后初始化各个对象。 ##优化思路---内存换时间 在app启动时候,就将mReactRootView初始化出来,并缓存起来,在用的时候直接setContentView(mReactRootView),达到秒开。 ###步骤1 缓存rootview管理器 缓存rootview管理器主要用于初始化和缓存rootview对象。 ``` import android.app.Activity; import android.os.Bundle; import android.view.ViewParent; import com.facebook.react.LifecycleState; import com.facebook.react.ReactInstanceManager; import com.facebook.react.ReactPackage; import com.facebook.react.ReactRootView; import java.lang.reflect.Field; /** * 缓存view管理 */ public class RNCacheViewManager { private static ReactRootView mRootView = null; private static ReactInstanceManager mManager = null; private static AbsRnInfo mRnInfo = null; //初始化 public static void init(Activity act, AbsRnInfo rnInfo) { init(act, rnInfo, null); } public static void init(Activity act, AbsRnInfo rnInfo, Bundle launchOptions) { if (mManager == null) { updateCache(act, rnInfo, launchOptions); } } public static void updateCache(Activity act, AbsRnInfo rnInfo) { updateCache(act, rnInfo, null); } //更新cache,适合于版本升级时候更新cache public static void updateCache(Activity act, AbsRnInfo rnInfo, Bundle launchOptions) { mRnInfo = rnInfo; mManager = createReactInstanceManager(act); mRootView = new ReactRootView(act); mRootView.startReactApplication(mManager, rnInfo.getMainComponentName(), launchOptions); } //设置模块名称,因为是private,只能通过反射赋值 public static void setModuleName(String moduleName) { try { Field field = ReactRootView.class.getDeclaredField("mJSModuleName"); field.setAccessible(true); field.set(getReactRootView(), moduleName); } catch (Throwable e) { throw new RuntimeException(e); } } //设置启动参数,因为是private,只能通过反射赋值 public static void setLaunchOptions(Bundle launchOptions) { try { Field field = ReactRootView.class.getDeclaredField("mLaunchOptions"); field.setAccessible(true); field.set(getReactRootView(), launchOptions); } catch (Throwable e) { throw new RuntimeException(e); } } public static ReactRootView getReactRootView() { if(mRootView==null){ throw new RuntimeException("缓存view管理器尚未初始化!"); } return mRootView; } public static ReactInstanceManager getReactInstanceManager() { if(mManager==null){ throw new RuntimeException("缓存view管理器尚未初始化!"); } return mManager; } public static AbsRnInfo getRnInfo() { if(mRnInfo==null){ throw new RuntimeException("缓存view管理器尚未初始化!"); } return mRnInfo; } public static void onDestroy() { try { ViewParent parent = getReactRootView().getParent(); if (parent != null) ((android.view.ViewGroup) parent).removeView(getReactRootView()); } catch (Throwable e) { e.printStackTrace(); } } public static void clear() { try { if (mManager != null) { mManager.onDestroy(); mManager = null; } if (mRootView != null) { onDestroy(); mRootView = null; } mRnInfo = null; } catch (Throwable e) { e.printStackTrace(); } } private static ReactInstanceManager createReactInstanceManager(Activity act) { ReactInstanceManager.Builder builder = ReactInstanceManager.builder() .setApplication(act.getApplication()) .setJSMainModuleName(getRnInfo().getJSMainModuleName()) .setUseDeveloperSupport(getRnInfo().getUseDeveloperSupport()) .setInitialLifecycleState(LifecycleState.BEFORE_RESUME); for (ReactPackage reactPackage : getRnInfo().getPackages()) { builder.addPackage(reactPackage); } String jsBundleFile = getRnInfo().getJSBundleFile(); if (jsBundleFile != null) { builder.setJSBundleFile(jsBundleFile); } else { builder.setBundleAssetName(getRnInfo().getBundleAssetName()); } return builder.build(); } } ``` ###步骤2 重写ReactActivity 将官方的ReactActivity粘出来,重写2个方法,onCreate和onDestroy,其余代码不动。 onCreate方法中使用缓存rootview管理器来获得rootview对象,而不是重新创建。 这里曾尝试继承ReactActivity,而不是重写这个类,但是子类覆盖onCreate方法时候,必须要调用super.onCreate,否则编译会报错,但是super.onCreate方法会重新创建rootview,所以实在是绕不过去了。 ``` protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (RNCacheViewManager.getRnInfo().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) { // Get permission to show redbox in dev builds. if (!Settings.canDrawOverlays(this)) { Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); startActivity(serviceIntent); FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE); Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show(); } } mReactInstanceManager = RNCacheViewManager.getReactInstanceManager(); ReactRootView mReactRootView = RNCacheViewManager.getReactRootView(); setContentView(mReactRootView); } ``` onDestroy方法中,不能再调用原有的mReactInstanceManager.destroy()方法了,否则rn初始化出来的对象会被销毁,下次就用不了了。同时,要卸载掉rootview的parent对象,否则下次再setContentView时候回报错。 ``` protected void onDestroy() { RNCacheViewManager.onDestroy(); super.onDestroy(); } ``` RNCacheViewManager.onDestroy的方法: ``` public static void onDestroy() { try { ViewParent parent = getReactRootView().getParent(); if (parent != null) ((android.view.ViewGroup) parent).removeView(getReactRootView()); } catch (Throwable e) { e.printStackTrace(); } } ``` ###步骤3 在app启动时候初始化缓存rootview管理器 ``` RNCacheViewManager.init((Activity) context, new RnInfo(moduleName, launchOptions)); ``` 其中RnInfo如下: ``` public class RnInfo extends AbsRnInfo { private String mModuleName; private Bundle mLaunchOptions; public RnInfo(String moduleName) { this.mModuleName = moduleName; } public RnInfo(String moduleName, Bundle launchOptions) { this.mModuleName = moduleName; this.mLaunchOptions = launchOptions; } @Nullable @Override public Bundle getLaunchOptions() { return mLaunchOptions; } @Override public String getMainComponentName() { return mModuleName; } @Override public String getJSMainModuleName() { return RNKeys.Default.DEf_JS_MAIN_MODULE_NAME; } @Nullable @Override public String getJSBundleFile() { return RNManager.getJsBundlePath(); } @Override public boolean getUseDeveloperSupport() { return true; } @Override public List getPackages() { return Arrays.asList( new MainReactPackage(), new BBReactPackage() ); } } ``` #结语 希望本篇文档能帮助遇到类似问题的小伙伴们。 reactnative虽然不是银弹,但是在目前移动端浏览器兼容性弱爆了的情况下,还是能极大的提升开发测试效率的,性能也是极佳的,看好rn的未来。 #参考文章 http://zhuanlan.zhihu.com/magilu/20587485 http://zhuanlan.zhihu.com/magilu/20259704 https://yq.aliyun.com/articles/3208?spm=5176.100239.yqblog1.39.t2g49u&utm_source=tuicool&utm_medium=referral ================================================ FILE: ReactNative开发指导/ReactNative导航设计与实现.md ================================================ # ReactNative导航设计与实现 # 前言 关于reactnaitve的导航,官方提供了2个组件,NavigatorIOS和Navigator,其中官方并不推荐使用NavigatorIOS,它不是官方维护的,不能保证及时的更新和维护。 所以本文中是以Navigator组件为基础,进行导航的设计和实现。 Navigator的劣势:Navigator组件是纯js的实现,所以在页面进行转场动画的过程中,如果js不能保证在16ms内完成其它操作的话,转场动画会有卡顿现象,后面会介绍优化的方案。 官方的Navigator组件使用方式较为灵活,本文的目的是选取一种最佳用法,并提取出通用功能应对常用场景,规范和设计项目中导航的使用。 # 定义 rn应用:全站rn应用,简称rn应用。 rn模块:部分模块使用rn,简称rn模块。 rn首页:无论是rn应用还是rn模块,进入rn页面的第一屏,简称rn首页。 nav:Navigator组件对象的简称,注意是实例化好的对象,不是类。页面间传递的导航对象统一使用此命名。 Header:自定义的导航栏组件。 # 体系结构、设计原则 一个rn应用或者一个rn模块,有且只有一个Navigator组件被定义。 在rn首页定义Navigator组件。 各个子页面统一使用首页定义的Navigator组件对象nav。 不要使用Navigator的navigationBar,请自定义导航栏组件,例如Header组件。 # Navigator组件的定义和初始化 在rn首页中的render方法中,定义一个Navigator组件,并做好以下几件事: 1. 实现好通用的renderScene方法, 2. 实现好android的物理返回按键 3. 初始化真正的rn首页 ## 实现统一路由函数renderScene renderScene函数是Navigator组件的必填函数,入参是route对象和当前的nav对象,返回值是jsx。 此函数的意思是根据传入的route,返回一个作为新页面的jsx,也就是说所有的路由算法都是在此函数中实现的。 其中route对象是一个自定义的对象,是nav.push方法中传入的对象。 此函数设计相当于门面模式,此函数是路由的统一处理器,所有的页面跳转请求都会通过此函数的计算来获得具体的jsx页面。 既然是统一的路由处理器,必然要求传入的route对象要满足统一的规则,否则无法实现统一的算法。 在此,设计route对象如下: ``` { name: 'page2', //名字用来做上下文判断和日志输出 page: , //jsx形式的page,作为新页面的jsx // page: () => , //或者函数形式的page,此函数必须返回jsx,此jsx作为新页面的jsx } ``` 根据route对象设计,设计统一的renderScene方法如下: ``` _renderPage(route, nav) { if (!route.page) { console.error('页面导航请求没有传入page参数.'); return null; } let page; if (typeof route.page === 'function') { page = route.page(); } else { page = route.page; } let name = route.name; if (!name) { if (page) { name = page.type.name; } } console.log(`in render page ${name}`); return page; } ``` 业务代码中,页面跳转的时候,只需要如下代码 ``` nav.push({ name: 'page2', page: , }); ``` ## android物理返回按键的处理 如果你的应用需要支持android的话,那就要实现andorid的物理返回按键的对应处理。 一般按物理返回按键要么是返回上一页面,要么是返回页面的上一状态【例如,有打开的弹窗,按返回是关闭这个弹窗】。 返回上一页面因为有通用路由器的存在,所以可以通用处理,直接使用nav.pop()即可。 但是返回页面上一状态,并不容易统一处理,所以使用基于事件扩展的方式,交给业务代码自行实现。 在此重构route对象的规则,添加事件onHardwareBackPress,如下 ``` { name: 'page2', //名字用来做上下文判断和日志输出 page: , //jsx形式的page,作为新页面的jsx // page: () => , //或者函数形式的page,此函数必须返回jsx,此jsx作为新页面的jsx onHardwareBackPress: () => alert('点物理按键会触发我'), // 返回false就终止统一路由器的默认动作,即终止页面返回动作,可以在此方法中实现返回页面上一状态的相关实现 } ``` android物理返回按键的统一处理代码如下, ``` componentWillMount() { BackAndroid.addEventListener('hardwareBackPress', () => { if (this.refs.nav) { let routes = this.refs.nav.getCurrentRoutes(); let lastRoute = routes[routes.length - 1]; // 当前页面对应的route对象 if (lastRoute.onHardwareBackPress) {// 先执行route注册的事件 let flag = lastRoute.onHardwareBackPress(); if (flag === false) {// 返回值为false就终止后续操作 return true; } } if (routes.length === 1) {// 在第一页了 // 此处可以根据情况实现 点2次就退出应用,或者弹出rn视图等 } else { this.refs.nav.pop(); } } return true; }); } ``` ## 初始化真正的rn首页 此处较为简单,直接使用Navigator组件的initialRoute属性来指定初始化的route对象。 ``` , // Home为伪代码,自定义的首页组件 name: 'home', }} /> ``` # 页面跳转 根据前面设计好的renderScene方法,直接使用如下代码,即可跳转到Page2,并将nav对象传递给了Page2. ``` nav.push({ name: 'page2', page: , }); ``` # 页面返回 页面返回直接使用 ``` nav.pop(); ``` # 页面转场优化 前面提到,Navigator组件完全使用js实现,由于js的单线程特点,如果在页面转场动画过程中,js干其他事情【比如渲染个某个jsx】超过了16ms,那么转场动画将不足60帧,给用户的感觉就是动画有卡顿。 为了避免这种情况,一种简单粗暴的办法就是在转场动画中不要让js来干别的事情。 那么我们如何知道转场动画什么时候结束呢,官方提供了动画交互管理器InteractionManager,示例伪代码如下: ``` InteractionManager.runAfterInteractions(() => { alert('哈哈 转场动画结束了!'); }); ``` 大多数的场景:点击page1的某个按钮,要跳转到page2,并且page2要和服务器请求数据,根据返回的数据来渲染page2的部分or全部内容。 针对上述场景,解决方案如下,用伪代码描述: 1. page2的state至少有2个值,转场动画进行中=true,服务器查询=true 1. page2的componentWillMount方法中发起异步服务器交互请求,当请求结束setState:服务器查询=false 2. page2的componentWillMount方法中注册InteractionManager.runAfterInteractions事件,当转场结束setState:转场动画进行中=false 3. page2的render方法中,先判断(转场动画进行中=true || 服务器查询=true)就返回一个loading的提示,否则返回真正的jsx,并且此时,服务器返回的数据已经可用了 也可以参考官方文档: http://reactnative.cn/docs/0.22/performance.html#content # 刷新的实现 目标:实现类似于html中window.reload的方法。 由于我们对route的规则限定,所以我们可以做到统一的刷新页面的逻辑。 思路是, 1. 首先获得当前页面对应的route对象 2. 然后获取route中的page属性,page属性可能是当前页面的jsx,也可能是可以产生当前页面jsx的方法 3. 最后使用官方Navigator组件提供的replace方法,来用新的route替换掉原有的route 示例参考代码如下: ``` /** * 刷新页面,route可以为空,会刷新当前页面 * @param nav * @param route */ refresh(nav, route) { if (!route) { let routes = nav.getCurrentRoutes(); let length = routes.length; route = routes[length - 1]; // 使用当前页对应的route } // todo 最好的方式是直接使用route.page,但是不好使,这种写法只支持一层节点,如果有多层会有问题 // todo 暂时未处理page是function的情况 let Tag = route.page.type; nav.replace({ page: , }); } ``` 然后业务代码中这样调用,当前页面就被刷新了。 ``` Util.refresh(nav); //Util是伪代码,是你定义refresh方法的对应对象 ``` # rn首页直接跳转子页面 如果你开发的是rn模块【rn模块嵌入到已有app中,定义可以参考前面定义一节】,可能进入rn模块的入口会很多,比如,用rn开发一个论坛模块,正常入口进来是直接展现帖子列表,也可能会有点击某个其它按钮【此按钮是不是rn的】会直接跳转到某个帖子的详情页。 使用官方Navigator组件提供的initialRouteStack属性,可以完美的解决此问题,官方文档对此属性的说明如下:提供一个路由集合用来初始化。如果没有设置初始路由的话则必须设置该属性。如果没有提供该属性,它将被默认设置成一个只含有initialRoute的数组。 说白了就是,initialRouteStack要定义一个数组,里面是很多route对象,然后Navigator对象会展现到最后一个,而且数组中的其他route也都被初始化过了,你想返回到任何一个route都是可以的,是不是爽歪歪了。 给个示例代码吧,这是我项目中真正的代码,请当伪代码来阅读: ``` getInitialRouteStack() { let props = this.getProps(); let detailId = props.detailId; if (detailId) { // 如果传入了详情id,那么跳转到详情页 return [{name: 'home', }, { page: , backIsClose: true, }]; } let wantAsk = props.wantAsk; if (wantAsk === true || wantAsk === 'true') { // 如果传入了提问属性=true,那么直接跳转到提问页面 return [{name: 'home', }, { page: , backIsClose: true, }]; } // 跳转到首页 return [{name: 'home', }]; } ``` # 实现代码参考: 根据以上设计思路,笔者封装了一个Navigator组件,是对官方的navigator组件进行了一层封装,供大家参考: ``` import React from "react-native"; const { Platform, Animated, View, DeviceEventEmitter, Dimensions, Navigator, BackAndroid, } = React; class Navigator2 extends React.Component { componentWillMount() { BackAndroid.addEventListener('hardwareBackPress', () => { if (this.refs.nav) { let routes = this.refs.nav.getCurrentRoutes(); let lastRoute = routes[routes.length - 1]; if (lastRoute.onHardwareBackPress) {// 先执行route注册的事件 let flag = lastRoute.onHardwareBackPress(); if (flag === false) {// 返回值为false就终止后续操作 return true; } } if (routes.length === 1) {// 在第一页了 if (this.props.nav) {// 父页面仍有nav this.props.nav.pop(); } if (this.props.onHardwareBackPressInFirstPage) { this.props.onHardwareBackPressInFirstPage(); } } else { if (lastRoute.backIsClose === true) { if (this.props.onHardwareBackPressInFirstPage) { this.props.onHardwareBackPressInFirstPage(); } } else { this.refs.nav.pop(); } } } return true; }); } getLastRoute() { if (this.refs.nav) { let routes = this.getCurrentRoutes(); let lastRoute = routes[routes.length - 1]; return lastRoute; } return null; } render() { return ; } _renderPage(route, nav) { if (!route.page) { console.error('页面导航请求没有传入page参数.'); return null; } let page; if (typeof route.page === 'function') { page = route.page(); } else { page = route.page; } let name = route.name; if (!name) { if (page) { name = page.type.name; } } console.log(`in render page ${name}`); return page; } // todo 以下的方法为实现原版navigator的方法,这样做不好,但是没想到其它好办法 getCurrentRoutes() { return this.refs.nav.getCurrentRoutes(...arguments); } jumpBack() { return this.refs.nav.jumpBack(...arguments); } jumpForward() { return this.refs.nav.jumpForward(...arguments); } jumpTo(route) { return this.refs.nav.jumpTo(...arguments); } push(route) { return this.refs.nav.push(...arguments); } pop() { return this.refs.nav.pop(...arguments); } replace(route) { return this.refs.nav.replace(...arguments); } replaceAtIndex(route, index) { return this.refs.nav.replaceAtIndex(...arguments); } replacePrevious(route) { return this.refs.nav.replacePrevious(...arguments); } immediatelyResetRouteStack(routeStack) { return this.refs.nav.immediatelyResetRouteStack(...arguments); } popToRoute(route) { return this.refs.nav.popToRoute(...arguments); } popToTop() { return this.refs.nav.popToTop(...arguments); } } module.exports = Navigator2; ``` #参考地址 http://reactnative.cn/docs/0.21/navigator.html#content http://reactnative.cn/docs/0.22/performance.html#content ================================================ FILE: ReactNative开发指导/ReactNative开发技巧总结.md ================================================ # ReactNative开发技巧总结 #样式 不支持选择器 无复用就尽量用行内,避免命名难题 不支持position=fixed,利用flexbox可以搞定。 RN样式没有提供类似于display=none的方式,使用BaseCss提供的hidden样式,原理是绝对定位,left=-9999 总结的较好:http://segmentfault.com/a/1190000002658374 #调试 在系统首页调试可以极大的提供效率,系统首页直接使用你要调试的组件作为app js可以用chrome来调试 css和结构就靠刷新了,官方给的结构查看器不是很好用 在一些重要逻辑部分多用console.log,特别是在触摸事件中,有时候还可以发现隐藏问题,习惯用console.log代替一些注释 #touch系列 一般包裹在view的外面,margin写在touch上,不要写在view上,否则margin会有动画效果,padding写在view上。 一般使用touchOpacity,不使用touchHeighlight #text 一定不要有padding margin,如果要有请在外面套一个view,写在view上 #listview,scrollview 必须有高,否则不能滚动,flex=1也可以,相当于给了高 ================================================ FILE: ReactNative开发指导/ReactNative打离线包-android篇.md ================================================ # ReactNative打离线包-android篇 官方文档: 官方文档2: 离线包就是把RN和你写的js图片等资源都打包放入app,不需要走网络下载。 #打包命令说明 react-native bundle Options: --entry-file Path to the root JS file, either absolute or relative to JS root [required] --platform Either "ios" or "android" --transformer Specify a custom transformer to be used (absolute path) [default: "/Users/babytree-mbp13/projects/xcodeProjects/AwesomeProject/node_modules/react-native/packager/transformer.js"] --dev If false, warnings are disabled and the bundle is minified [default: true] --prepack If true, the output bundle will use the Prepack format. [default: false] --bridge-config File name of a a JSON export of __fbBatchedBridgeConfig. Used by Prepack. Ex. ./bridgeconfig.json --bundle-output File name where to store the resulting bundle, ex. /tmp/groups.bundle [required] --bundle-encoding Encoding the bundle should be written in (https://nodejs.org/api/buffer.html#buffer_buffer). [default: "utf8"] --sourcemap-output File name where to store the sourcemap file for resulting bundle, ex. /tmp/groups.map --assets-dest Directory name where to store assets referenced in the bundle --verbose Enables logging [default: false] #安卓打包步骤 1. 在工程根目录下执行打包命令,比如``` react-native bundle --entry-file demo/index.js --bundle-output ./android/app/src/main/assets/index.android.jsbundle --platform android --assets-dest ./android/app/src/main/res/ --dev false ```请参考上面命令说明,根据自己的情况进行修改再执行。注意要先保证[./android/app/src/main/assets/]文件夹存在。 2. ps:增量升级的话不要把图片资源直接打包到res中,脚本如下:``` react-native bundle --entry-file demo/index.js --bundle-output ./bundle/androidBundle/index.android.jsbundle --platform android --assets-dest ./bundle/androidBundle --dev false ``` 1. 命令执行完生成资源如图![2015-12-24 11.05.31](media/2015-12-24%2011.05.31.png) 1. 保证MainActivity.java中的setBundleAssetName与你的jsbundle文件名一致,比如`.setBundleAssetName("index.android.jsbundle")`就与我生成的资源名一致 2. 一切OK 打包测试吧 #To disable the developer menu for production builds: For iOS open your project in Xcode and select Product → Scheme → Edit Scheme... (or press ⌘ + <). Next, select Run from the menu on the left and change the Build Configuration to Release. For Android, by default, developer menu will be disabled in release builds done by gradle (e.g with gradle assembleRelease task). Although this behavior can be customized by passing proper value to ReactInstanceManager#setUseDeveloperSupport. ================================================ FILE: ReactNative开发指导/ReactNative打离线包-ios篇.md ================================================ # ReactNative打离线包-ios篇 官方文档,内容很旧: 相关链接: 离线包就是把RN和你写的js图片等资源都打包放入app,不需要走网络下载。 升级服务器的使用方法: 在mobile-rn/bbt-rn-update-server目录中,执行命令npm start,然后访问http://localhost:3000/ #打包命令说明 react-native bundle Options: --entry-file Path to the root JS file, either absolute or relative to JS root [required] --platform Either "ios" or "android" --transformer Specify a custom transformer to be used (absolute path) [default: "/Users/babytree-mbp13/projects/xcodeProjects/AwesomeProject/node_modules/react-native/packager/transformer.js"] --dev If false, warnings are disabled and the bundle is minified [default: true] --prepack If true, the output bundle will use the Prepack format. [default: false] --bridge-config File name of a a JSON export of __fbBatchedBridgeConfig. Used by Prepack. Ex. ./bridgeconfig.json --bundle-output File name where to store the resulting bundle, ex. /tmp/groups.bundle [required] --bundle-encoding Encoding the bundle should be written in (https://nodejs.org/api/buffer.html#buffer_buffer). [default: "utf8"] --sourcemap-output File name where to store the sourcemap file for resulting bundle, ex. /tmp/groups.map --assets-dest Directory name where to store assets referenced in the bundle --verbose Enables logging [default: false] #ios打包步骤 1. 在工程根目录下执行打包命令,比如``` react-native bundle --entry-file demo/index.js --bundle-output ./bundle/iosBundle/index.ios.jsbundle --platform ios --assets-dest ./bundle/iosBundle --dev false ```请参考上面命令说明,根据自己的情况进行修改再执行。注意要先保证bundle文件夹存在。 2. 命令执行完生成如下资源 ![323424sf](media/323424sf-1.png) 2. 在xcode中添加assets【必须用Create folder references的方式,添加完是蓝色文件夹图标】和index.ios.jsbundle,如图![2015-12-23 17.35.50](media/2015-12-23%2017.35.50.png) 3. 参考官方文档,修改AppDelegate.m文件,使用OPTION 2处的代码 ``` jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"index.ios" withExtension:@"jsbundle"]; ``` 4. 一切OK 运行模拟器看效果吧 #ios打包遇到的问题 1. 离线包如果开启了chrome调试,会访问调试服务器,而且会一直loading出不来。 2. 如果bundle的名字是main.jsbundle,app会一直读取旧的,改名就好了。。。非常奇葩的问题,我重新删了app,clean工程都没用,就是不能用main.jsbundle这个名字。 3. 必须用Create folder references【蓝色文件夹图标】的方式引入图片的assets,否则引用不到图片 4. 执行bundle命令之前,要保证相关的文件夹都存在 #To disable the developer menu for production builds: For iOS open your project in Xcode and select Product → Scheme → Edit Scheme... (or press ⌘ + <). Next, select Run from the menu on the left and change the Build Configuration to Release. For Android, by default, developer menu will be disabled in release builds done by gradle (e.g with gradle assembleRelease task). Although this behavior can be customized by passing proper value to ReactInstanceManager#setUseDeveloperSupport. ================================================ FILE: ReactNative开发指导/ReactNative的ios崩溃整理分析.md ================================================ # ReactNative的ios崩溃整理分析 共12种异常,如下: ``` wanghonglu 18:14 >---------------------- Row 1 -----------------------< => Start Unable to execute JS call: __fbBatchedBridge is undefined -> translating『 0x100ac2b60x100aa8004 』=> RCTFatal /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTAssert.m: line 120 -> translating『 0x100ac2b60 』=> -[RCTBatchedBridge stopLoadingWithError:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 476 -> translating『 0x1006d72fc 』=> main /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/main.m: line 13 => End Unable to execute JS call: __fbBatchedBridge is undefined >------------------------------------------------------< >---------------------- Row 2 -----------------------< => Start Application received signal SIGSEGV -> translating『 0x1012891bc 』=> -> translating『 0x100abf734 』=> __64-[RCTJSCExecutor executeApplicationScript:sourceURL:onComplete:]_block_invoke /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 515 -> translating『 0x100abf944 』=> -[RCTJSCExecutor executeBlockOnJavaScriptQueue:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 547 -> translating『 0x100abdc3c 』=> +[RCTJSCExecutor runRunLoopThread] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 151 => End Application received signal SIGSEGV >------------------------------------------------------< wanghonglu 18:14 >---------------------- Row 4 -----------------------< => Start Application received signal SIGSEGV -> translating『 0x1012891bc 』=> -> translating『 0x100abf734 』=> __64-[RCTJSCExecutor executeApplicationScript:sourceURL:onComplete:]_block_invoke /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 515 -> translating『 0x100abf944 』=> -[RCTJSCExecutor executeBlockOnJavaScriptQueue:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 547 -> translating『 0x100abdc3c 』=> +[RCTJSCExecutor runRunLoopThread] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 151 => End Application received signal SIGSEGV wanghonglu 18:14 >---------------------- Row 7 -----------------------< => Start SyntaxError: Unexpected end of script -> translating『 0x100aa8004 』=> RCTFatal /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTAssert.m: line 120 -> translating『 0x100ac2b60 』=> -[RCTBatchedBridge stopLoadingWithError:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 476 -> translating『 0x1006d72fc 』=> main /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/main.m: line 13 => End SyntaxError: Unexpected end of script >------------------------------------------------------< >---------------------- Row 8 -----------------------< => Start Unable to execute JS call: __fbBatchedBridge is undefined -> translating『 0x100aa8004 』=> RCTFatal /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTAssert.m: line 120 -> translating『 0x100ac2b60 』=> -[RCTBatchedBridge stopLoadingWithError:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 476 -> translating『 0x1006d72fc 』=> main /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/main.m: line 13 => End Unable to execute JS call: __fbBatchedBridge is undefined >------------------------------------------------------< wanghonglu 18:14 >---------------------- Row 11 -----------------------< => Start Application received signal SIGSEGV -> translating『 0x1012891bc 』=> -> translating『 0x100a92140 』=> __46-[RCTNetworking buildRequest:completionBlock:]_block_invoke /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/Libraries/Network/RCTNetworking.m: line 215 -> translating『 0x100a923bc 』=> -[RCTNetworking processDataForHTTPQuery:callback:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/Libraries/Network/RCTNetworking.m: line 264 -> translating『 0x100a91e2c 』=> -[RCTNetworking buildRequest:completionBlock:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/Libraries/Network/RCTNetworking.m: line 204 -> translating『 0x100a940a8 』=> -[RCTNetworking sendRequest:responseSender:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/Libraries/Network/RCTNetworking.m: line 442 -> translating『 0x100aa67ec 』=> -[RCTModuleMethod invokeWithBridge:module:arguments:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTModuleMethod.m: line 426 -> translating『 0x100ac53f0 』=> -[RCTBatchedBridge _handleRequestNumber:moduleID:methodID:params:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 899 -> translating『 0x100ac4de0 』=> __33-[RCTBatchedBridge handleBuffer:]_block_invoke.383 /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 848 => End Application received signal SIGSEGV >------------------------------------------------------< wanghonglu 18:15 >---------------------- Row 16 -----------------------< => Start Unable to execute JS call: __fbBatchedBridge is undefined -> translating『 0xa3b4ef 』=> RCTFatal /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTAssert.m: line 120 -> translating『 0xa52b37 』=> -[RCTBatchedBridge stopLoadingWithError:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 476 -> translating『 0x68b883 』=> main /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/main.m: line 13 => End Unable to execute JS call: __fbBatchedBridge is undefined >------------------------------------------------------< wanghonglu 18:15 >---------------------- Row 22 -----------------------< => Start Application received signal SIGSEGV -> translating『 0x1012891bc 』=> -> translating『 0x100ac1c30 』=> -[RCTBatchedBridge registerModuleForFrameUpdates:withModuleData:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 357 -> translating『 0x100ac707c 』=> -[RCTModuleData finishSetupForInstance] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTModuleData.m: line 73 -> translating『 0x100ac1a74 』=> -[RCTBatchedBridge initModules] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 249 -> translating『 0x100ac02a8 』=> -[RCTBatchedBridge start] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 99 -> translating『 0x100ac016c 』=> -[RCTBatchedBridge initWithParentBridge:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 68 -> translating『 0x100ad5648 』=> -[RCTBridge setUp] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBridge.m: line 257 -> translating『 0x100ad5068 』=> -[RCTBridge initWithDelegate:launchOptions:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBridge.m: line 156 -> translating『 0x100071c60 』=> __36-[BBRNManager reactNativeBridgeInit]_block_invoke /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/ReactNative/Utils/BBRNManager.m: line 56 -> translating『 0x1006d72fc 』=> main /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/main.m: line 13 => End Application received signal SIGSEGV >------------------------------------------------------< wanghonglu 18:15 >---------------------- Row 24 -----------------------< => Start Application received signal SIGABRT -> translating『 0x1012891bc 』=> -> translating『 0x100abf290 』=> __52-[RCTJSCExecutor _executeJSCall:arguments:callback:]_block_invoke /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 391 -> translating『 0x100abf944 』=> -[RCTJSCExecutor executeBlockOnJavaScriptQueue:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 547 -> translating『 0x100abeda4 』=> -[RCTJSCExecutor _executeJSCall:arguments:callback:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 385 -> translating『 0x100abeb7c 』=> -[RCTJSCExecutor callFunctionOnModule:method:arguments:callback:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 368 -> translating『 0x100ac43b0 』=> -[RCTBatchedBridge _actuallyInvokeAndProcessModule:method:arguments:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 747 -> translating『 0x100ac3774 』=> __39-[RCTBatchedBridge enqueueJSCall:args:]_block_invoke /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 642 -> translating『 0x100abf944 』=> -[RCTJSCExecutor executeBlockOnJavaScriptQueue:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 547 -> translating『 0x100abdc3c 』=> +[RCTJSCExecutor runRunLoopThread] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 151 => End Application received signal SIGABRT >------------------------------------------------------< wanghonglu 18:15 >---------------------- Row 25 -----------------------< => Start Application received signal SIGABRT -> translating『 0x1012891bc 』=> -> translating『 0x100ace79c 』=> _RCTJSONStringifyNoRetry /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTUtils.m: line 26 -> translating『 0x100ace5bc 』=> RCTJSONStringify /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTUtils.m: line 71 -> translating『 0x100abeeec 』=> __52-[RCTJSCExecutor _executeJSCall:arguments:callback:]_block_invoke /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 391 -> translating『 0x100abf944 』=> -[RCTJSCExecutor executeBlockOnJavaScriptQueue:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 547 -> translating『 0x100abeda4 』=> -[RCTJSCExecutor _executeJSCall:arguments:callback:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 385 -> translating『 0x100abeb7c 』=> -[RCTJSCExecutor callFunctionOnModule:method:arguments:callback:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 368 -> translating『 0x100ac43b0 』=> -[RCTBatchedBridge _actuallyInvokeAndProcessModule:method:arguments:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 747 -> translating『 0x100ac3774 』=> __39-[RCTBatchedBridge enqueueJSCall:args:]_block_invoke /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 642 -> translating『 0x100abf944 』=> -[RCTJSCExecutor executeBlockOnJavaScriptQueue:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 547 -> translating『 0x100abdc3c 』=> +[RCTJSCExecutor runRunLoopThread] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Executors/RCTJSCExecutor.m: line 151 => End Application received signal SIGABRT >------------------------------------------------------< wanghonglu 18:16 >---------------------- Row 26 -----------------------< => Start Application received signal SIGSEGV -> translating『 0x1012891bc 』=> -> translating『 0x100ac1c30 』=> -[RCTBatchedBridge registerModuleForFrameUpdates:withModuleData:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 357 -> translating『 0x100ac707c 』=> -[RCTModuleData finishSetupForInstance] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTModuleData.m: line 73 -> translating『 0x100ac7400 』=> -[RCTModuleData instance] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTModuleData.m: line 122 -> translating『 0x100ac113c 』=> -[RCTBatchedBridge moduleForName:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 231 -> translating『 0x100ad540c 』=> -[RCTBridge moduleForClass:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBridge.m: line 236 -> translating『 0x100ae16d4 』=> -[RCTUIManager setBridge:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Modules/RCTUIManager.m: line 270 -> translating『 0x100ac6f2c 』=> -[RCTModuleData setBridgeForInstance] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTModuleData.m: line 58 -> translating『 0x100ac19b0 』=> -[RCTBatchedBridge initModules] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 249 -> translating『 0x100ac02a8 』=> -[RCTBatchedBridge start] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 99 -> translating『 0x100ac016c 』=> -[RCTBatchedBridge initWithParentBridge:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 68 -> translating『 0x100ad5648 』=> -[RCTBridge setUp] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBridge.m: line 257 -> translating『 0x100ad5068 』=> -[RCTBridge initWithDelegate:launchOptions:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBridge.m: line 156 -> translating『 0x100071c60 』=> __36-[BBRNManager reactNativeBridgeInit]_block_invoke /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/ReactNative/Utils/BBRNManager.m: line 56 -> translating『 0x1006d72fc 』=> main /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/main.m: line 13 => End Application received signal SIGSEGV >------------------------------------------------------< wanghonglu 18:16 >---------------------- Row 27 -----------------------< => Start SyntaxError: Unexpected EOF -> translating『 0x100aa8004 』=> RCTFatal /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTAssert.m: line 120 -> translating『 0x100ac2b60 』=> -[RCTBatchedBridge stopLoadingWithError:] /Users/kevin/Code/pregnancy-rn/mobile-rn/node_modules/react-native/React/Base/RCTBatchedBridge.m: line 476 -> translating『 0x1006d72fc 』=> main /Users/wanghonglu/Desktop/project/pregnancy-v2-ios/pregnancy/pregnancy/main.m: line 13 => End SyntaxError: Unexpected EOF >------------------------------------------------------< ``` # __fbBatchedBridge is undefined 3次;Unexpected end of script 1次;Unexpected EOF 1次; 场景:异常从RCTBatchedBridge stopLoadingWithError方法【此方法是当jsbundle加载出现异常时就会执行】抛出,应为加载的jsbundle文件有问题,有可能是文件下载不对或存储空间不足等,建议加载前,使用md5进行校验。 解决:建议加载前,使用md5进行校验。校验不通过的不进行加载,尝试回退or重新下载补丁等动作。 android端也出现了__fbBatchedBridge is undefined的异常,解法也是使用md5来校验jsbundle。 参考链接:http://www.jianshu.com/p/3db03c8c4ae7 # Start Application received signal SIGSEGV ## [RCTJSCExecutor executeApplicationScript:sourceURL:onComplete:]2次 -> translating『 0x1012891bc 』=> 最根的没有转换出来 RCTJSCExecutor executeApplicationScrip RCTJSCExecutor executeBlockOnJavaScriptQueue 场景:暂时没有线索 解决: todo ## [RCTNetworking buildRequest:completionBlock:] 1次 -> translating『 0x1012891bc 』=> 最根的没有转换出来 场景:js调用原生,发送网络请求 解决:todo ## [RCTBatchedBridge registerModuleForFrameUpdates:withModuleData:] 2次 -> translating『 0x1012891bc 』=> 最根的没有转换出来 场景:初始化bridge的过程,[BBRNManager reactNativeBridgeInit]中调用[RCTBridge initWithDelegate:launchOptions:]的时候。 解决:todo # Start Application received signal SIGABRT ## [RCTJSCExecutor _executeJSCall:arguments:callback:] 1次 -> translating『 0x1012891bc 』=> 最根的没有转换出来 场景:是由RCTBatchedBridge enqueueJSCall调用,是native调用js。 解决:todo ## RCTUtils.m 中的 _RCTJSONStringifyNoRetry 1次 -> translating『 0x1012891bc 』=> 最根的没有转换出来 场景:将json对象转换为json字符串。也是由[RCTJSCExecutor _executeJSCall:arguments:callback:]调用造成的。是由RCTBatchedBridge enqueueJSCall调用,是native调用js。 解决:todo ================================================ FILE: ReactNative开发指导/ReactNative的架构设计.md ================================================ # ReactNative的组件架构设计 还有一篇较早的文章,也是分析的过程,可以对本篇文章进行一个补全:RN组件状态设计思考:http://segmentfault.com/a/1190000004180955 请注意,本篇写的是react native的架构设计,如果你用react来开发web程序,本篇文章只能仅供参考,问题都没有在web上去考虑过。 本篇较长,前面是目前flux开源框架的一些分析,后面是架构设计过程。您可以直奔主题。 用RN最大的难题是设计思想的转变,以前的设计方法论已经不太适用了。而RN仅仅提供了view的框架,构建完整app的架构并没有直接提供。 考虑目前遇到的如下问题,希望架构给出解决方案。 1. **交互**:如何解决组件间通信【父子、子父、兄弟等,特别是跨层or反向数据流动等】;用state还是接口操作组件; 3. **职责**:组件状态放哪,业务逻辑放哪,数据放哪,因为太灵活了,怎么做都可以实现功能,但是怎么做才是最好的,才是最正确的呢? *todo一个问题:由于react是面向状态编程,相当于react的组件只关注数据的最终状态,数据是怎么产生的并不关心,但是某些场景下,数据如何产生的是会影响到组件的一些行为的【比如一个新增行要求有动画效果,查询出的行就不需要等】,这在RN中很难描述。。。。。* RN架构就是为解决上述问题提供的指导和方法论,是通盘考虑整个开发、测试、运维的状况,做出的考虑最全面的抉择,或者为抉择提供依据。 目前为react服务的架构也有一些了,如Flux,Reflux,Redux,Relay,Marty。 ##Flux flux是官方提供的架构,目的是分层解耦,职责划分清晰,谁负责干啥很明确。具体描述可以参考官方文档,这里不详述。 1. action 封装请求 2. dispatcher 注册处理器、分发请求 3. store 是处理器,处理业务逻辑,保存数据 4. view 根据store提供的数据进行展现;接受用户的输入并发出action请求。 ![](media/14500845993004.jpg) 数据流动: Action-> Dispatcher -> Store -> Component 但我觉得解耦的太细了,干一个事,要做太多太多的额外工作了。 光注册监听动作就2次,一次是store注册到dispatcher,一次是view注册到store中。 而且,注册到dispatcher的监听应该都不叫注册,架构完全没有提供任何封装,直接暴露一个统一的回调方法,里面自行if else路由不同的store。 ##Reflux 结构上与flux架构基本一致,去掉了flux的一些冗余操作【比如没有了dispatcher】,架构更加简洁和紧凑,用到了一些约定大于配置的理念。 基本上将flux的架构冗余都简化了,可以说是flux的去冗余提升版,但是没有本质的变化。 ``` ╔═════════╗ ╔════════╗ ╔═════════════════╗ ║ Actions ║──────>║ Stores ║──────>║ View Components ║ ╚═════════╝ ╚════════╝ ╚═════════════════╝ ^ │ └──────────────────────────────────────┘ ``` 1. 更容易的监听。listenables和约定以on开头的方法。等。 2. 去掉了dispatcher。 3. action可以进行aop编程。 4. 去掉了waitfor。store可以监听store。 5. component提供了一系列mixin,方便注册\卸载到store的监听和与store交互等。 ##Redux 社区内比较受推崇,因为用起来相对比较简单 ![](media/14502335830493.jpg) 特性: 1. 分层设计,职责清晰。 2. 要求store reducer都是页面单例,易于管理。 2. action为请求dto对象,是请求类型,请求数据的载体。 3. reducer是处理请求的方法。不允许有状态,必须是纯方法。必须严格遵守输入输出,中间不允许有异步调用。不允许对state直接进行修改,要想修改必须返回新对象。 4. store 5. 维持应用的state; 1. 提供 getState() 方法获取 state; 2. 提供 dispatch(action) 方法分发请求来更新 state;门面模式,要求所有的请求满足统一的格式【可以进行路由、监控、日志等】,统一的调用方式。 1. 通过 subscribe(listener) 注册监听器监听state的变化。 1. 官方文档写的较为详细,从设计到开发都有,比flux要好 痛处如下,看能否接受或者解决: 1. redux的原则1:state不能被修改。 4. 其实这个用react的state也会有同样的问题,最好把state设计的没有冗余,尽量少出这种情况 4. **解决方案:**参考官方:因为我们不能直接修改却要更新数组中指定的一项数据,这里需要先把前面和后面都切开。如果经常需要这类的操作,可以选择使用帮助类 React.addons.update,updeep,或者使用原生支持深度更新的库 Immutable。最后,时刻谨记永远不要在克隆 state 前修改它。 4. 单一的庞大的reducer的拆分 5. 这块设计也不好做,会让人疑惑 6. 官方给的demo中直接按state的内容区分,我觉得这样做不好,如果后期有跨内容的情况,就比较奇怪了。官方给的combineReducers方案,也只是减少代码量,本质没有变化,state还是拆分处理,路由还是业务逻辑自己来做。 7. **解决方案**:还是处理一整个state,可以按照约定写reducer类而不是方法,类里按照actionType建方法,架构自动路由并调用。 8. 以前做java架构,路由一定是架构来调用的,目前感觉各大flux框架都是解决问题不彻底。 1. 官方建议设计模式:顶层容器组件才对redux有依赖,组件间通过props来传递数据。按照这样设计还是没有解决组件间交互和数据传递的问题。官方react设计建议:react的设计建议:http://camsong.github.io/redux-in-chinese/docs/basics/UsageWithReact.htm 2. 使用connect将state绑定到component。此处有些黑盒了。 2. 异步action用来请求服务端数据,利用middleware增强createStore的dispatch后即支持。 ##Relay 没有时间,没做研究 ##Marty 没有时间,没做研究 ##结论 开源架构封装的简单的flux会产生较多的冗余代码。 开源架构封装的复杂的redux,其和RN绑定封装了一些东西,是一个黑盒,不易理解和维护。 介于上述两者之间的开源架构reflux,文档较上述2个少,不知道其可持续性如何。如果一定要用开源架构的话,我觉得他稍加封装是一个较为推荐的选择。 不是特复杂的程序【一般spa的程序会更复杂一些,而RN并不是spa】,这些概念只会增加你的开发难度,并且对后面维护的人要求更高。 我们继续头脑风暴,继续抽象总结一下flux系列框架, flux系列框架干了什么,没干什么,针对开篇提出的问题。 2. 【解决职责】flux系列框架都做到了解耦,分层,谁该干什么就干什么,不许干别的,让代码读起来更有预测性和一致性,方便维护 3. 【解决通信】继续解耦,flux系列框架采用事件机制解决各层之间通信,采用props传递解决各组件之间通信。 ####事件系统是关键 flux系列架构解决通信问题的方法是使用事件系统,事件系统中的回调函数是业务逻辑,redux是【store action reducer】,flux是【action dispacher store】。 我们真的需要事件系统吗? 事件系统的好处: 1. 一个事件可以注册多个回调函数 2. 各回调函数间没有耦合。 关于1 需要注册多个的这种情况并不多见,不信你去翻看你已经写好的代码,是不是大部分都是注册一个。 关于2 解耦确实很彻底,但是当我需要控制执行顺序,需要等a执行完在执行b,怎么办?ok你可以先注册a在注册b啊。那a要是一个fetch或ajax操作呢?这时候只能乖乖的在a的请求结束回调函数中进行调用b了。又变成a依赖b了。当然,你可以继续dispatch(b),这就没有耦合了。但是你要知道注册一个事件是要有成本的,要写action,而且这种dispatch的方式,真的不太适合人类的阅读,dispatch一下,下一步都有谁来执行都不知道,这哪有直接调用来的爽快。 好吧说到这,最后的结论也出来了,不使用开源架构,借助其好的思想,替换其事件系统为面向对象结构,自行封装架构。 ## 架构设计 再次强调:目前仅考虑如何应用于react native ###先扣题,针对开篇问题的解决方案如下 ####交互 1. 组件对外发布:组件对外只允许使用props来暴露功能,不允许使用接口及其它一切方式 2. 父子组件间:组件的子组件通过父组件传递的接口来与父组件通信 3. 兄弟组件间: 4. 方案1:假设a要调用b,参考第一条的话,其实就是a要改变b的props,那么a只要改b的props的来源即可,b的props的来源一般就是根组件的state。那么根组件就要有组织和协调的能力。 5. 方案2:利用事件机制,基本同flux架构。略复杂,且我们并不需要事件的特性,本架构设计不推荐。 #### 职责 1. root-存放state,组织子view组件,组织业务逻辑对象等 2. 子view组件-根据this.props渲染view。 3. 业务逻辑对象-提供业务逻辑方法 根据以上推导,我将其命名为面向对象的ReactNative组件架构设计,它与flux系列架构的最大的不同之处在于,用业务逻辑对象来代替了【store action dispatcher】or【store reducer】的事件系统。业务逻辑对象就是一组对象,用面向对象的设计理念设计出的n个对象,其负责处理整个页面的业务逻辑。 以上为推导过程,干货才开始。。。。 ### 面向对象的ReactNative组件\页面架构设计 一个独立完整的组件\页面一般由以下元素构成: 1. root组件,1个, 1. 负责初始化state 2. 负责提供对外props列表 2. 负责组合子view组件形成页面效果 3. 负责注册业务逻辑对象提供的业务逻辑方法 4. 负责管理业务逻辑对象 1. view子组件,0-n个, 1. 根据props进行视图的渲染 1. 业务逻辑对象,0-n个, 2. 提供业务逻辑方法 ####root组件 root组件由以下元素组成: 1. props-公有属性 2. state-RN体系的状态,必须使用Immutable对象 3. 私有属性 4. 业务逻辑对象的引用-在componentWillMount中初始化 4. 私有方法-以下划线开头,内部使用or传递给子组件使用 5. 公有方法【不推荐】,子组件和外部组件都可以用,但不推荐用公有方法来对外发布功能,破坏了面向状态编程,尽可能的使用props来发布功能 ![root](media/root.png) ####子view组件 子view组件中包含: 1. props-公有属性 2. 私有属性-强烈不建议有,除非你能理解以下几点,建议放在父组件or业务逻辑对象中 3. 绝对不允许和父组件的属性or状态有冗余。无论是显性冗余还是计算结果冗余,除非你能确定结算是性能的瓶颈。 4. 此属性只有自己会用,父组件和兄弟组件不会使用,如果你不确定这点,请把这个组件放到父组件上,方便组件间通信 3. 私有方法-仅作为渲染view的使用,不许有业务逻辑 4. 公有方法【不推荐,理由同root组件】 ![gsddfa](media/gsddfa.png) ####业务逻辑对象 业务逻辑对象由以下元素组成: 3. root组件对象引用-this.root 2. 构造器-初始化root对象,初始化私有属性 2. 私有属性 3. 公有方法-对外提供业务逻辑 3. 私有方法-以下划线开头,内部使用 ![dafa](media/dafa.png) #### ps1:通用型组件只要求尽量满足上述架构设计 通用型组件一般为不包含任何业务的纯技术组件,具有高复用价值、高定制性、通常不能直接使用需要代码定制等特点。 可以说是一个系统的各个基础零件,比如一个蒙板效果,或者一个模态弹出框。 架构的最终目的是保证系统整体结构良好,代码质量良好,易于维护。一般编写通用型组件的人也是经验较为丰富的工程师,代码质量会有保证。而且,作为零件的通用组件的使用场景和生命周期都和普通组件\页面不同,所以,仅要求通用组件编写尽量满足架构设计即可。 ####ps2:view子组件复用问题 抛出一个问题,设计的过程中,子组件是否需要复用?子组件是否需要复用会影响到组件设计。 6. 需复用,只暴露props,可以内部自行管理state【尽量避免除非业务需要】 7. 不需复用,只暴露props,内部无state【因为不会单独使用,不需要setState来触发渲染】 其实, 一般按照不需复用的情况设计,除非复用很明确,但这时候应该抽出去,变成独立的组件存在就可以了,所以这个问题是不存在的。 ##适用场景分析 ###flux系列框架 flux系列框架的适用场景我觉得应具有以下特点: **一个页面中组件较多,组件之间较为独立,但是重叠使用模型,模型的变化会影响很多组件的展现和行为。** 比如,开发一个类似qq的聊天页面,左侧是联系人列表,右侧是与某人的消息对话框,当收到一个消息之后,1要刷新左侧联系人列表的最近联系人,2要右侧的消息对话框中显示这个消息,3要页面title要提示新消息。这就是典型的一个新消息到来事件【消息模型发生了变化】触发三个无关联的组件都有行为和展现的变化。如果用事件系统来开发就做到了解耦的极致,未来如果还要加入第4种处理也不用修改原来的逻辑,就直接注册一下就可以了,满足了开闭原则。 **需要对app运行过程进行监控,数据采样等** flux系列框架是一个典型的门面模式,业务动作都要通过统一的门面dispatch进行,天生具有良好的监控解决方案。 ###面向对象的RN组件架构 面向对象的RN组件架构的使用场景特点我没有总结出来,我觉得所有场景都可以用,只要你业务逻辑对象设计的好,都不是问题。 还拿上面聊天界面举例子,面向对象的RN组件架构其实也可以解耦的写出写上述场景,你完全可以将业务逻辑对象之间的交互设计成一个小的事件系统,只是架构没有直接约束这种解耦,flux系列架构直接在架构中就强制编码人员做到了解耦,但是如果我不需要解耦的时候就相当于增加了复杂度,得不偿失了。 所以面向对象的RN组件架构要更灵活,也更简单更容易让人理解,更容易预测代码的执行流向,但同时因为灵活对业务逻辑对象设计者的要求也较高,针对较为复杂or重要页面建议进行详细设计并leader检查来保证质量。 ##如何做监控 因为面向对象的RN架构中去掉了统一的业务逻辑调用facade入口dispatch,那我们如何来做监控呢。 ###方案1:在需要监控的地方人为加入监控点。 这个方案对业务代码和监控代码的耦合确实有点大,是最差的解决方案了。不推荐。 ###方案2:在基类BaseLogicObj的构造器中对对象的所有方法进行代理-todo待验证 这个方案对业务代码透明,但是还只是个想法,未进行代码测试和验证。 ###方案3.....还没有想出别的方案,有没有同学给点思路? ##架构之美 最后在分享demo代码之前,摘抄了天猫前端架构师团队对架构的认识,个人觉得十分认同。 简单: 简单的东西才能长久,HTML、CSS和JavaScript之所以能够活到现在,而其他类似的很牛的方案都死掉了,原因之一是简单,才有那么多人用它,所以我们需要把技术和产品方案朝着简单的思路发展,简单才是本质,复杂一定是临时的会过时的。天猫的前端技术架构为什么都基于Kissy,为什么是两层架构,就是朝着简单的方式去思考。看起来简单,用起来简单。 高效: 简单是高效的前提,复杂的高效都是临时的会过时的,技术架构一定要提高团队的工作效率,否则一定会被抛弃,因此把简单的规则自动化,把精确的重复的事情让机器去做,前端这么多年为什么开发环境不够成熟,就是自动化的工具太少,前端又很少能力驾驭编写工具的语言,而Nodejs的出现是一个前所未有的机会。 灵活: 高效往往和灵活是对立的,就像移动上Native和Web的关系,而我们就需要思考如何做到两者兼顾,既高效又灵活,所以要不断把事情做简单,思考本质、看到本质,基于本质去实现。比如Apple为什么敢于把鼠标和键盘去掉,是因为确信人直接和界面打交道比借助一个中间硬件更能够表达人机交互的本质。 新鲜: 面向未来,前端需要不停地更新自己,无论是思想还是技术。比如整个天猫基于Kissy,那么就使用最新的Kissy版本,基础设施能够升级是一种能力,如果有一天基础设施升不了啦,那么这套技术架构就老去了。比如发现Gulp比Grunt更能够代表未来,那么我们毫不犹豫地整个团队开始进行升级。 ##完整demo代码 此demo仿照redux提供的todolist demo编写。 redux demo 地址:http://camsong.github.io/redux-in-chinese/docs/basics/ExampleTodoList.html demo截图: ![](media/14503398097842.jpg) todolist页面: ``` 'use strict' let React=require('react-native'); let Immutable = require('immutable'); var BbtRN=require('../../../bbt-react-native'); var { BaseLogicObj, }=BbtRN; let { AppRegistry, Component, StyleSheet, Text, View, Navigator, TouchableHighlight, TouchableOpacity, Platform, ListView, TextInput, ScrollView, }=React; //root组件开始----------------- let Root =React.createClass({ //初始化模拟数据, data:[{ name:'aaaaa', completed:true, },{ name:'bbbbb', completed:false, },{ name:'ccccc', completed:false, } ,{ name:'ddddd', completed:true, }], componentWillMount(){ //初始化业务逻辑对象 this.addTodoObj=new AddTodoObj(this); this.todoListObj=new TodoListObj(this); this.filterObj=new FilterObj(this); //下面可以继续做一些组件初始化动作,比如请求数据等. //当然了这些动作最好是业务逻辑对象提供的,这样root组件将非常干净. //例如这样:this.todoListObj.queryData(); }, //状态初始化 getInitialState(){ return { data:Immutable.fromJS(this.data),//模拟的初始化数据 todoName:'',//新任务的text curFilter:'all',//过滤条件 all no ok } }, //这里组合子view组件 并 注册业务逻辑对象提供的方法到各个子view组件上 render(){ return (