React Native 插曲 打离线包和CodePush发布更新

框架

浏览数:245

2019-3-11

AD:资源代下载服务

用了两天才搞定了这些琐碎的操作,从真机调试到利用CodePush进行热更新。。。知道最后才发现一个血的教训:即使用了CodePush,本地也必须要有一个离线包才行,即本地需要打包一个main.jsbundle放进项目中,才可以成功运行。

首先,讲React Native的发布更新,这就涉及到React Native的热更新主要是怎么工作的,关键在于bundle的更新
我们先来看看ReactNative是怎么处理bundle文件(bundle文件用于资源管理)的。可以有如下两种方式:
1、app发版时,将bundle文件内置到app中,同时将图片和js文件内置到app中(Android和iOS处理的不同)
2、app在合适的时机,加载服务端最新的bundle文件

下面一张流程图简单介绍ReactNative的热更新机制:
(参考自:http://blog.csdn.net/codetomylaw/article/details/52115704

ReactNative的热更新.png

一、打离线包(打包bundle文件内置到app中)

离线包即本地存着一个离线包,保证没有网络的时候程序不会报错。
1、将js代码打包到jsbundle文件中
在index.ios.js所在目录下,执行以下命令(文件名自定义,使用codepush的时候最好命名为默认的main.jsbundle):

//  无图片资源等
react-native bundle --entry-file index.ios.js --platform ios --bundle-output main.jsbundle

//  有图片资源
react-native bundle --entry-file index.ios.js --platform ios --bundle-output index.ios.bundle --platform ios --dev false --assets-dest ./  

//  或者放bundle文件夹里(注意要先保证bundle文件夹存在!!!) 
react-native bundle –entry-file index.ios.js –bundle-output ./bundle/iosBundle/index.ios.jsbundle –platform ios –assets-dest ./bundle/iosBundle –dev false 

/*
entry-file: RN的入口文件
bundle-out: 输出bundle文件的输出路径(bundle文件名和oc里面的代码要对应)
asset-dest:输出的asset资源目录
*/

2、添加到项目中。
在xcode中添加assets【必须用Create folder references的方式,添加完是蓝色文件夹图标】和index.ios.jsbundle(或者main.jsbundle、命名不同)

网上参考的放bundle文件夹的目录结构和项目结构如下:

bundle文件夹的目录结构和项目结构(自网上).png

参考自:https://segmentfault.com/a/1190000004189538

个人利用第一句命令创建的包,目录如下:

离线包目录结构.png

3、修改AppDelegate.m文件(只使用离线包不使用热更新的情况)

//  URLForResource根据第一步对应的名字对应修改
jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"index.ios" withExtension:@"jsbundle"];

4、模拟器或者真机调试都没有问题。

顺带列举下ios打包可能遇到的问题(虽然我都没遇到哈):
1)离线包如果开启了chrome调试,会访问调试服务器,而且会一直loading出不来。
2)如果bundle的名字是main.jsbundle,app会一直读取旧的,改名就好了。。。非常奇葩的问题,我重新删了app,clean工程都没用,就是不能用main.jsbundle这个名字。
3)必须用Create folder references【蓝色文件夹图标】的方式引入图片的assets,否则引用不到图片
4)执行bundle命令之前,要保证相关的文件夹都存在

二、使用CodePush实现热更新

1. 环境准备(Mac)

1)安装Code-push cli

$ npm install -g code-push-cli

2) 注册CodePush账户

$ code-push register

执行以上命令将会自动打开如下图所示的注册页面:

注册页面.png

可以使用github或者Microsoft帐户注册(本文使用GitHub账户进行注册),注册成功后将会生成如下图所示相应的access token:

access token.png

按照提示在终端输入刚生成的access token(会自动登录):

输入access token.png

3)添加CodePush应用

$ code-push app add XXX      //xxx 为你要热部署的app name

结果如下:

添加CodePush应用.png

2. 安装CodePush

在项目更目录(或者说package.json所在目录)执行以下语句安装CodePush,会自动对应修改package.json内容。

npm install --save react-native-code-push

效果如下:

安装CodePush.png

3. 集成code-push 到工程文件

CodePush集成到项目中,一般有三种方法:
RNPM、CocoaPods 、”Manual”

详细可参考CodePush的Github:
https://github.com/Microsoft/react-native-code-push

下文分别对其接入方法进行简单介绍:
个人感觉按情况分,如果是将React Native集成到原生项目中的项目,因为本身应该有用到cocoaPods,所以推荐使用cocoaPods接入,比较方便简单。
如果是纯React Native项目,则可以考虑手动接入。

(因为我测试用的是原生集成项目,所以就使用了cocoaPods)

方式一:cocoaPods接入

1)修改Podfile文件:(自己注意路径哈)

pod 'CodePush', :path => './node_modules/react-native-code-push'

2)执行命令行更新pod:

pod install 
//  或者 
pod install --verbose --no-repo-update

方式二:手动接入

1)将react-native-code-push文件夹中 CodePush.xcodeproj 直接拉入项目中Libraries中

CodePush.xcodeproj 拉入项目.png

2)在Link Binary With Libraries中添加Libraries/CodePush.xcodeproj /Products中的libCodePush.a以及libz.tbd。

Link Binary With Libraries.png

3)在Build Settings的Header Search Paths那一项中加入(路径要注意)

$(SRCROOT)/../node_modules/react-native-code-push

Header Search Paths.png

方式三:RNPM接入

好像是官方推荐使用最简单的方式,不过没有试过哈
1)安装rnpm

$ npm -g install rnpm xcode

2)使用

$ rpm link

4. 修改项目配置

1)在命令行下使用code-push deployment ls <AppName> –displayKeys 查出Staging的值,在info.plist文件中添加CodePushDeploymentKey并且把Staging的值填入。
2)在Info.plist中将Bundle versions string, short的值修改为1.0.0(或者其它,结构一样为x.x.x,且和之后code push更新的版本要对应)

info.plist配置.png

5. 修改OC代码和JS代码

1)在加载 js bundle的类中 修改代码

a)首先,引入头文件:

#import "CodePush.h"

b)然后修改jsCodeLocation值:

//该文章是讨论在Release下进行相关的操作,如果是debug版本加上如下代码,系统在运行时候会自动切换。
NSURL *jsCodeLocation;
    
#ifdef DEBUG
    jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
#else
    jsCodeLocation = [CodePush bundleURL];
#endif

//如果本地js离线包不是命名main.jsbundle,需要对CodePush进行相应设置
jsCodeLocation = [CodePush bundleURLForResource:@"index"];
2)在js文件中(该项目文件是index.ios.js) 修改代码

a)引入对应头文件

import codePush from "react-native-code-push";

b)在componentDidMount调用sync方法,当你的App启动的时候会在后台更新

componentDidMount(){
    codePush.sync();
  }

6. 发布更新

1)发布更新 (JavaScript-only)

当你修改js文件的时候,并且想立刻发布。则仅仅需要以下的操作:

  1. 将你修改的js文件(当前文件是index.ios.js)打包为二进制文件,放入指定的地方(当前位置为根目录)。
react-native bundle --platform ios --entry-file index.ios.js --bundle-output codepush.js --dev false
  1. 将二进制文件push到staging 环境中
code-push release [--deploymentName ] [--description ]  [--mandatory]
eg:
code-push release NativeTest codepush.js 1.0.0

// 上面代码运行有问题的话,试试把codepush.js后缀改成codepush.jsbundle。。好像目前版本和我上年底的要求不同了

2)发布更新 (JavaScript + images)

  1. –assets-dest 就是你放图片的位置(当前仅仅是做测试,实际上最好建个文件夹专门存入相关图片)
react-native bundle --platform ios --entry-file index.ios.js --bundle-output ./main.jsbundle --assets-dest ./<img 地址>
// main.jsbundle 为自定义名字的bundle名
  1. push bundle文件
code-push <release/debug> <projectName(与注册的app同名)><bundle文件名> <版本号>
eg:
code-push release NativeTest main.jsbundle 1.0.0

到这一步的时候,正常来说,该配置的都配置了,试着更新一个版本push到codepush上,运行下项目,就会出现效果。
注意:如果出现找不到main.jsbundle的报错,很大的原因就是没有在本地打个离线包,详细参考这篇文章的第一部分。(曾经因为这个原因,我看着报错发呆了好久,上网也找不到任何说法😂)

Q&A

  1. CodePush更新规则

    CodePush更新规则.png

  2. 更新后都需要重启才能看到最新的变化 再次进入后要等一会。TODO:还要验证后确定稳定时机。
  3. 查看历史上传过的版本:

code-push deployment history NativeTest Staging

查看历史上传过的版本.png

更多的CodePush相关命令推荐参考以下几篇文章:
Code Push 热更新使用详细说明和教程
app实现热更新codepush
以及CodePush的Github:
https://github.com/Microsoft/react-native-code-push