记一次 RN 升级(0.49.5->0.56.0)

一、前言

首先我们可以在官方文档里查到RN升级的文档Upgrading to new React Native versions
文中介绍了两种办法:

1.通过react-native-git-upgrade

因为这个方法是一键式升级,所以它会改变项目中的iOSAndroid文件,由于我们的项目是混合型的,这样严重破坏了客户端的代码,会让解决冲突变得十分困难,不推荐。

2.通过修改package.jsonreact-native的版本号来升级

这个方案只牵涉到前端,破坏性小,还可以通过rn-diff来查看到升级的一些文件改变,通常react-native的升级和react的升级是绑定的,我们就可以通过rn-diff找到相应的版本号,接下来我们就用这种办法来进行升级。

二、踏坑

1.修改package.json

"react": "16.4.1",
"react-native": "0.56.0",
npm i

2.运行iOS的pod:update

发现pod.file中为了兼容scrollView的一段代码已无用,故删除。

3.执行react-native run-ios

错误信息:

Failing run on IOS and Android because a syntax error in `local-cli`

解决:
需要升级 node,版本大于8.3

sudo npm cache clean -f && sudo npm install -g n install "n" && sudo n stable

4.启动后的红屏

错误信息1:

Unable to resolve module AccessibilityInfo from XXX

解决:
清除缓存

npm run reset  
相当于 
watchman watch-del-all && rm -rf node_modules && npm install && npm start --reset-cache

这里还需要提到一点,在修复过程中,经常发现改动了代码但没生效,这时候需要重启服务试试

npm start --reset-cache

错误信息2:

Using the export keyword between a decorator and a class is not allowed

解决:

将
@dec1
@dec2({
  option1: "foo" 
})
export class C {
} 
改为
export
@dec1
@dec2({
  option1: "foo" 
})
class C {
} 

错误信息3:

Cannot read property 'bindings' of null at Scope.moveBindingTo

错误信息4:

Property right of AssignmentExpression expected node to be of a type ["Expression"] but instead got null

解决:
3和4都是由于在0.56版本,RN需要使用babel7+

package.json
 "dependencies": {
    "react": "16.4.1",
    "react-native": "0.56.0",
    "@babel/core": "7.0.0-beta.47",
    "@babel/plugin-proposal-decorators": "7.0.0-beta.47",
    "@babel/plugin-transform-classes": "7.0.0-beta.47",
    "@babel/register": "7.0.0-beta.47",
  }
  "devDependencies": {
    "babel-jest": "23.4.0",
    "babel-preset-react-native": "^5",
    "jest": "23.4.1",
    "react-test-renderer": "16.4.1"
  }
.babelrc
{
    "presets": ["react-native"],
    "plugins": [
        ["@babel/plugin-proposal-decorators", { "legacy": true }]
    ]
}

5.第三方库错误

react-native-scrollable-tab-view

错误信息:

A trailing comma is not permitted after the rest element

解决:
由于此库已无人维护,所以把源码放到本地维护,去除错误提示代码中的几处逗号即可

react-native-modal

错误信息:

bundling failed: Error: We don't know what to do with this node type. We were previously a Statement but we can't fit in here?

解决:
升级库

"react-native-modal": "^6.5.0"

react-native-svg-uri

错误信息:

element type is invalid expected a string (for built-in components) or a class/function check 

解决:
升级库

"react-native-svg": "^6.5.2",
"react-native-svg-uri": "^1.2.3"

react-native-root-siblings

错误信息:

Exporting local "_default", which is not declared.

解决:
由于此库近半年无人维护,所以把源码放到本地维护

export default class {
改为
export default class xxx { 

6.RN内部组件的调整

Text/TextInput

错误信息:

Text.prototype.render undefined

解决:

Text.prototype.render
改为
Text.render

create-react-class

错误信息:

Module create-react-class does not exist in the Haste module map

解决:
由于把第三方库引入本地,代码老旧,新版已不支持create-react-class的方式,所以逐个改造为es6的语法

7.Android上的修改

解决完上面的问题,在iOS上就可以正常的跑起来了,但在Androidbuild都过不了。
想到客户端同学之前提到的一个坑:

 //解决同一个库依赖不同的版本 例如react-native 有0.49.5 有的是+ 统一成0.49.5
    subprojects {
        project.configurations.all {
            resolutionStrategy.eachDependency { details ->
                if (details.requested.group == 'com.facebook.react'
                    && details.requested.name.contains('react-native')) {
                    details.useVersion "0.49.5"
                }
                if (details.requested.group == 'com.airbnb.android'
                        && details.requested.name.contains('lottie')) {
                    details.useVersion "2.5.0"
                }
            }
        }
    }

他们需要把依赖的库统一版本,于是把上述的0.49.5改为0.56.0

 //解决同一个库依赖不同的版本 例如react-native 有0.56.0 有的是+ 统一成0.56.0
    subprojects {
        project.configurations.all {
            resolutionStrategy.eachDependency { details ->
                if (details.requested.group == 'com.facebook.react'
                    && details.requested.name.contains('react-native')) {
                    details.useVersion "0.56.0"
                }
                if (details.requested.group == 'com.airbnb.android'
                        && details.requested.name.contains('lottie')) {
                    details.useVersion "2.5.0"
                }
            }
        }
    }

重新build,成功了,但启动后马上Crash,打开AndroidStudio发现有错误信息:


解决:将这些项目android文件下的 ***.iml 文件删掉,重新编译
错误信息没有了,但启动依旧Crash,这时候和客户端同学沟通可能是so库的问题,于是替换了so库(这里有一个小技巧,要获得一个新版本的so库,可以通过创建一个新版本的Demo,然后从中获取),依旧出错,但错误信息变了:

java.lang.NoClassDefFoundError: Failed resolution of: Lcom/facebook/common/soloader/SoLoaderShim;

通过Google大致猜测是gif库出了问题

 //fresco
    compile "com.facebook.fresco:animated-gif:$rootProject.ext.frescoVersion"
    compile "com.facebook.fresco:fresco:$rootProject.ext.frescoVersion"

官网的升级日志中查到

Update Fresco to v1.9.0, okhttp3 to v3.10.0

于是把版本改为1.9.0,重新编译运行,一切正常。

8.Android Jenkins 打包失败

错误信息:


解决:
这个问题相当奇葩,几乎找不到思路,试过删除目录,再拉代码、拉库都无效,最后是在react-native bundle命令加中增加一个--reset-cache参数,清除打包的缓存文件,终于可以了。

9.Android Code-Push错误

错误信息(issue):

facebook::react::Recoverable: Could not open file ReactNativeDevBundle.js: No such file or directory

解决:
暂时还未解决,code-push的代码中可以看到一段注释说明解决代码的片段,但实际仍未解决,不过好在只出现在本地开发模式,这个还需继续跟进。

三、总结

1.看升级日志

升级前,建议看一遍官方的升级日志,这样可以对碰到的问题有预期和解决方向。

2.勤升级

有精力的话,建议更勤快的升级,这样每次升级带来的问题不会那么多,而且通过特定版本的升级日志通常就能定位问题。像这次跨多版本的升级,中间涉及的错误修复可能需要你仔细阅读多个版本的升级日志,困难度增大不少。

3.谨慎选择第三方库

对于第三方库,除了高star,还要注意它的更新频率。
如果在更新时碰到第三方库出错并很长时间都无人维护了,可通过下面几种办法解决:

  • Fork一份,修改代码,自己发npm或者引用fork地址;
  • 本地新建一个组件,利用继承的方式,修改源码,引用本地的新组件;
  • 把GitHub上的代码直接放在本地维护。

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 166,820评论 24 703
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 11,345评论 2 56
  • 今天,没有听微课,没有艾灸。很颓废。给木泽做了一个蛋糕。 给他做蛋糕的时候,决定还是买一个吧。自己的难日,何必为难...
    馨儿妈阅读 161评论 0 0
  • https://docs.python.org/3/library/functions.html 内置的基本函数语...
    laughingsolo阅读 312评论 0 0
  • 第14天 · 21天告别拖延 #玩卡不卡·每日一抽# 每一位都可以通过这张卡片觉察自己: 1、直觉他叫什么名字?...
    张梦格阅读 123评论 0 0
  • 走出文右大厦的时候,正是正午时分。已是深秋初冬,却感觉不到一丝寒意,一种暖意熨贴着心房。不用急着赶回家,脚下的路越...
    悠我悠心阅读 346评论 2 2