用了两天才搞定了这些琐碎的操作,从真机调试到利用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)
一、打离线包(打包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
个人利用第一句命令创建的包,目录如下:
离线包目录结构.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.png3)添加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.png3. 集成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 拉入项目.png2)在Link Binary With Libraries中添加Libraries/CodePush.xcodeproj /Products中的libCodePush.a以及libz.tbd。
Link Binary With Libraries.png3)在Build Settings的Header Search Paths那一项中加入(路径要注意)
Header Search Paths.png$(SRCROOT)/../node_modules/react-native-code-push
方式三: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更新的版本要对应)
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文件的时候,并且想立刻发布。则仅仅需要以下的操作:
- 将你修改的js文件(当前文件是index.ios.js)打包为二进制文件,放入指定的地方(当前位置为根目录)。
react-native bundle --platform ios --entry-file index.ios.js --bundle-output codepush.js --dev false
- 将二进制文件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)
- –assets-dest 就是你放图片的位置(当前仅仅是做测试,实际上最好建个文件夹专门存入相关图片)
react-native bundle --platform ios --entry-file index.ios.js --bundle-output ./main.jsbundle --assets-dest ./<img 地址>
// main.jsbundle 为自定义名字的bundle名
- 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
-
CodePush更新规则
CodePush更新规则.png - 更新后都需要重启才能看到最新的变化 再次进入后要等一会。TODO:还要验证后确定稳定时机。
- 查看历史上传过的版本:
查看历史上传过的版本.pngcode-push deployment history NativeTest Staging
更多的CodePush相关命令推荐参考以下几篇文章:
Code Push 热更新使用详细说明和教程
app实现热更新codepush
以及CodePush的Github:
https://github.com/Microsoft/react-native-code-push
网友评论
jsCodeLocation = [CodePush bundleURLForResource:@"index"];
这段代码会不会把 jsCodeLocation = [CodePush bundleURL];覆盖了然后造成CodePush 不能进行热更新
react-native bundle --entry-file index.ios.js --platform ios --bundle-output main.jsbundle
返回的是下面这些:
Usage: code-push deployment ls <appName> [options]
选项:
--format Output format to display your deployments with ("json" or "table") [string] [默认值: "table"]
--displayKeys, -k Specifies whether to display the deployment keys [boolean] [默认值: false]
示例:
deployment ls MyApp Lists the deployments for app "MyApp" in tabular format
deployment ls MyApp --format json Lists the deployments for app "MyApp" in JSON format
是不是哪方面有问题