因为一些缘由,笔者最近变动到了RN的团队,回归到了hybrid app的开发的圈子中,当然是有蛮多新鲜感和新机遇的,不过遥想起之前在hybrid中各类view以前跳转的头疼等各类问题,笔者怀着忐忑的心情开始了一段波折的hybrid之旅。其实大概的结果以前的文章也有说起了,不过因为大部分只是以“记笔记”的形式描述的,因此可贵想抽个时间,好好的总结一下,本身的心路历程。前端
众所周知,传统的webapp因为只能发挥native80%不到的机能,在性能和能力上一直为人所诟病,而传统的native app又须要耗费大量的人力,在这个互联网发展速度“过快”的时代,不多会有公司会持续这样“双份”的投资,因此hybrid app在一开始便以更高的性价比得到了业界的青睐,特别是进入react时代以后,react-native一经问世,便受到了业界大量的关注,然而几年过去,react已经更新到16.x,内部也迎来了极大的重构,fiber的推出,异步组件的提出,生命周期函数的unsafe,react彷佛正在朝着愈来愈强大的方向不断进步,而react-native却依旧在0.5x.x的版本使劲挣扎,这里面却有一些不得不说的苦楚。java
RN升级的辛酸node
由于笔者团队维护了多个rn的app,可是因为一些历史缘由,两个app几乎是独立的,投入度高的app rn版本能到0.4x,而另外一个则仅有0.35.x,可是RN在升级的过程当中,每每是伴随着一些破坏性更新的,不少旧的api在新的rn版本中是得不到支持的,但不少机型的兼容性问题或者说一些新的特性又只有新版本的RN才会提供,这也致使了RN升级的这件事是必需要实施的。也许对前端的同窗来讲,升级不就是换一下package.json的版本号就行了么?但实际上却至关的复杂:react
一、首先,因为xcode自己开发上的一些设计,无论是第三方库仍是官方库,都有经过pod或者经过Library的方式来引入,可是Library中的库中的有关引用的写法,随着版本的迭代大部分都不支持了,并且它那种组织方式自己就不是一种可复用的组织方式。android
并且,在调试中你还须要不停地本身手动的清除xcode的缓存,xcode自带的clean并不会清除它的构建缓存,这将致使,你对组件的变动,可能根本没有生效!webpack
解决方案:在iOS中,修改Library中有关库的引用方式,将它们所有迁移到经过pod来管理git
二、而后,propTypes和createClass二者虽然早起都集成在react的包中(固然RN的包中也有集成),可是随着版本的迭代,二者都被移出的react的主包中,和react-dom同样,须要开发者手动引用,然而对于一个5w+行的项目,组件量达到300+,要进行这样的变动,是一件很是痛苦的事情。。github
解决方案:只有手动的逐行变动代码,使用alias的一些小花招会令你的代码可读性变差web
三、接着,你会发现,以前一直使用的fb提供的官方组件也没有了。。并且官方明确地表示,他们都被废弃了,须要开发者本身去引用react-native-deprecated-custom-components,好比范用度特别高的Navigator,可是为何官方会废弃它们呢?笔者的我的观点是:“官方也没有提供一个令他们满意的解决方案”。好比刚刚提到的navigator,由于其自身在android上无可避免的会出现状态丢失的问题(虽然这是android的内存管理机制引发的),可是做为结果是,navigator就必定会致使在android上出现view状态丢失的问题,并且这种问题,无论是单vc仍是多vc的环境下都会产生,且没有很好的修复办法(虽然从理论上来说,推行全app的类redux状态管理器能够解决该问题),因而官方变废弃了该类组件。json
解决方案:针对本身的业务场景,本身实现本身须要的功能,笔者的团队就采用了fork官方组件,再二次实现的方式(固然,最终仍然须要状态管理器来解决根本的android的bug)
四、而后,当你修好了官方组件的bug,以为你的模拟器将要跑起来的时候,第三方的组件又开始大量报错了,固然,处理了上述那么多问题的你,这时已经想到了缘由:由于react的版本和rn的版本都发生了大量的迭代了,其相关组件也跟着须要破坏性更新也是很正常的。可是现实是并非全部的第三方组件做者都会持续更新。。因此,若是做者还在维护这个组件,那么你是幸运的,若是做者已经没有维护了,那么,要么你能在社区找到可以替换它的组件,要么你还得fork对应的组件,本身进行改动,而且成为维护人。
笔者也是如此碰到了一些使人头疼的问题:在笔者团队维护的一个app中,使用了一个历史比较悠久的第三方组件,而笔者的团队由因为一些缘由,没有持续使用fb提供的packager或者metrobundler进行打包,而是使用了webpack,这致使了原做者可能并不了解前端的模块规范和打包方式,他写的组件能在metro中跑起来,可是当环境切换到webpack时。。整个组件却不能正常编译了。。
另外好比不少早起的组件在android中都会重载createJSModules的方法,可是到后续的rn版本中,该方法变成了虚方法,不须要重载直接实现就行了,而这些组件的维护者彷佛都没有继续维护了,就须要使用者本身去fork组件来修改java代码。
解决方案:第三方组件,3分依靠做者对本身组件的责任心,7分依靠整个生态的热度,剩下90分,都得靠本身。
有关升级笔者碰到的问题大概就是这些了,固然这是除去IDE上使用的一些坑(小问题笔者便按下不表了,上文也只提到了严重影响笔者使用的地方),鉴于上文也提到了有关rn打包的事情,笔者接下来再聊聊rn打包的一些感觉。
RN打包的一些感觉
最开始笔者并不了解问什么fb非要从新造一个packager去进行打包,由于rn归根结底也不过是把写的jsx编译成一个es5的jsbundle,依赖对应平台的jsc来执行,一样是“打包”,为何不使用webpack呢?也许是因为rn本身的特殊性吧?笔者如是想,可是无论是packager仍是后来的metro bundler,作的事情几乎和webpack是同样的,并且他们都是用了babel社区的ast能力,去解析jsx文件,就连最后的注入require和__d的定义也和webpack注入webpack_require同样,为何呢?也许是fb的工程师们在着手作这件事的时候,webpack的文档和社区并不像今时今日这样的强大吧?因而也致使了metro造了一个和webpack几乎相似的轮子,并且它的文档和它的前人同样差!且因为生态和环境相对闭塞,metro bundler的文档严重滞后了不少个版本。
话说回来,笔者之因此要更换打包工具,主要缘由也是由于要作一些拆包的事情,可是因为metro-bundler自身生态的闭塞,且文档确实少得可怜,而业内对于rn拆包打包仍是有一些例如haul这类基于webpack的实现的,因而笔者也借鉴了社区的一些智慧,进行该实现。
可是每每理想很美好,显示很残酷。虽然webpack的生态已经很成熟了,可是在使用webpack来打包rn的时候,仍是不可避免的会碰到一些问题,好比:
一、编译环境的不一样。由于webpack针对的不只是是给浏览器的,还包括了node端,因此会带有一些编译平台所自由的方法或者类库,好比说console、Math、Date、crypto,可是rn因为是在native环境运行的,而fb一开始考虑的场景就和传统的web应用不彻底同样,笔者在进行打包工具切换的时候就遭遇了本来metro可以运行的库可是在webpack的编译环境中会报错。
解决方案:对于编译环境的不一样能够尝试使用webpack的target属性为webworker,这个属性设置的时候会更贴切rn的编译环境
二、图片处理的不一样。rn对图片有个比较贴心的处理,它可以自动经过@2x @3x去自动适应2倍和3倍屏的图片,可是本来的webpack是不支持的。
解决方案:本身编写一个loader去解析图片
三、一些全局变量。相信进行rn开发的同窗或多或少都会使用debugger,可是debugger会依赖rn挂载在全局的一些变量,好比require和__DEV__,而这些变量在webpack里则须要经过providePlugin或者其余的方式注入。
另外,笔者的项目因为一些历史缘由,像比较流行的严格模式都会引发项目运行报错,还有一些json文件的读写,也是metro的编译比较友好(或者没有传统前端严格,好比json文件里的注释),而webpack的相关loader则不会让一些这些“友好”的写法经过编译。
总的来讲,其实大部分使用RN的同窗可能和笔者都会有一样的想法:
“本来美好的write once,run anywhere,但现实是write once,fix everywhere,并且根本无法fix完。”
其实rn的问题或者说特性还有不少,像那个依赖setState通讯的动画,主线不断推送的事件,还有不少单平台才有的属性,都是rn开发的障碍, 因此就笔者而言:目前的rn更适合一些有native能力的团队在一些并非很要求app性能的条件下使用的解决方案,在没有通过适当优化的基础上其性能表现并不必定有webapp好。