前言
? ? ? ? 某個公司考慮使用React-Native的理由有很多,熱更新/熱部署可能是一個很大的原因。如果公司認(rèn)為熱更新/熱部署是主要原因的話,那么這應(yīng)該是一個很錯誤的決定。因為APP的質(zhì)量和具有前瞻性的規(guī)劃才是您真正需要考慮的事情,而不是熱更新的補救。當(dāng)然不可否認(rèn)的是熱更新/熱部署很符合敏捷開發(fā)這個思維.......
進(jìn)入正題
? ? ? ? 現(xiàn)在熱更新的第三方服務(wù)很多:微軟的CodePush,RN中文網(wǎng)的Pushy......國內(nèi)還是推薦使用國內(nèi)的服務(wù)吧。
? ? ? ? 既然有這么多第三方服務(wù)為何還要自己開發(fā)熱更新/熱部署功能呢?
? ? ? ? 1、安全性
? ? ? ? 2、可控性
? ? ? ? 3、實現(xiàn)簡單的業(yè)務(wù)邏輯開發(fā)起來其實很簡單
? ? ? ? 4、之前使用的微軟CodePush的服務(wù)下載時出現(xiàn)很多問題,于是決定自己開發(fā)。
開發(fā)思路

開發(fā)步驟
1、編譯項目
ios打包編譯:CD到項目根目錄運行命令
react-native bundle --entry-file index.ios.js --platform ios --dev false --bundle-output ./ios/bundle/index.ios.jsbundle --assets-dest ./ios/bundle
安卓打包請移步到打包APK
ios打包完成后在項目中的ios目錄中多出了bundle文件夾,這里就是我們打包出來的RN運行文件,實際上RN運行的就是這里的.jsbundle文件。

2、壓縮編譯文件
把bundle文件夾壓縮為ZIP包
3、把壓縮的bundle的ZIP包上傳到后端
4、更新后臺相關(guān)的配置文件,比如:RN包的版本號、更新日志、是否強制更新、是否靜默更新、是否回滾......具體有多少功能是你們的自己的事
5、下載最新的包,這里開始是寫代碼的時候了
5.1首先網(wǎng)絡(luò)請求服務(wù)的RN配置內(nèi)容
5.2對比本地RN的版本號
5.3彈出更新
5.4下載解壓最新的包
下載和解壓文件的代碼:下載使用的是AFN、解壓使用的是SSZipArchive
AFHTTPSessionManager *mgr = [AFHTTPSessionManager manager];
? ? //下載地址
? ? NSURL*mgrurl = [NSURLURLWithString:self.updateUrl];
? ? NSURLRequest*request = [NSURLRequestrequestWithURL:mgrurl];
? ? NSURLSessionDownloadTask*download = [mgrdownloadTaskWithRequest:requestprogress:^(NSProgress*_NonnulldownloadProgress) {
? ? ? //在主線程中調(diào)用
? ? ? [[NSOperationQueue mainQueue]addOperationWithBlock:^{
? ? ? ? NSLog(@"進(jìn)度%f",1.0*downloadProgress.completedUnitCount/ downloadProgress.totalUnitCount);
? ? ? ? CGFloatjsw =1.0* downloadProgress.completedUnitCount/ downloadProgress.totalUnitCount* (PW-40);
? ? ? ? self.jdView.frame=CGRectMake(0,0, jsw,5);
? ? ? ? self.jdText.text= [NSStringstringWithFormat:@"更新進(jìn)度(%.0f/100)",100.0*downloadProgress.completedUnitCount/ downloadProgress.totalUnitCount];
? ? ? ? self.suduText.text= [NSStringstringWithFormat:@"%lldkb/s",[MLRNSingtongetInterfaceBytes]/1024/1024];
? ? ? }];
? ? }destination:^NSURL*_Nonnull(NSURL*_NonnulltargetPath,NSURLResponse*_Nonnullresponse) {
? ? ? NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:response.suggestedFilename];
? ? ? NSLog(@"targetPath:%@",targetPath);
? ? ? NSLog(@"fullPath:%@",fullPath);
? ? ? return[NSURLfileURLWithPath:fullPath];
? ? }completionHandler:^(NSURLResponse*_Nonnullresponse,NSURL*_NullablefilePath,NSError*_Nullableerror) {
? ? ? //下載完成,解壓
? ? ? //Caches路徑
? ? ? NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
? ? ? NSString *xxxx = [[filePath absoluteString] substringFromIndex:7];
? ? ? [SSZipArchive unzipFileAtPath: xxxx toDestination:cachesPath];
? ? ? [[NSOperationQueue mainQueue]addOperationWithBlock:^{
? ? ? ? [MBProgressHUD hideHUD];
? ? ? ? UIStoryboard*storayobard = [UIStoryboardstoryboardWithName:@"main"bundle:nil];
? ? ? ? self.view.window.rootViewController = storayobard.instantiateInitialViewController;
? ? ? }];
? ? }];
? ? [downloadresume];

5.5運行本地的RN文件
在使用調(diào)用RN的地方(一般在AppDelegate中)
修改RCTRootView初始化方法為
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:[[RCTBridge alloc] initWithDelegate:self launchOptions:nil] moduleName:@""name"" initialProperties:nil];

添加代理RCTBridgeDelegate

實現(xiàn)RCTBridgeDelegate中的sourceURLForBridge方法
- (NSURL*)sourceURLForBridge:(RCTBridge*)bridge {
? <#code#>
}

在sourceURLForBridge方法中確定您是要使用最新那個URL中的包
我這里的邏輯是
判斷沙盒是否存在.jsbundle文件 --->? 有則使用沙盒中的.jsbundle
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 沒有則使用本地打包的文件
- (NSURL*)sourceURLForBridge:(RCTBridge*)bridge{
? NSURL*jsCodeLocation;
? // 取得沙盒目錄
? NSString *localPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
? // 要檢查的文件目錄
? NSString*filePath = [localPath? stringByAppendingPathComponent:@"bundle/index.ios.jsbundle"];
? NSFileManager *fileManager = [NSFileManager defaultManager];
? if([fileManagerfileExistsAtPath:filePath]) {
? ? NSString*newUrl = [NSStringstringWithFormat:@"file://%@",filePath];
? ? jsCodeLocation = [NSURLURLWithString:newUrl];
? ? NSLog(@"文件存在");
? ? returnjsCodeLocation;
? }else{
#if DEBUG
? ? // 原來的jsCodeLocation
? ? jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];
#else
? ? jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"bundle/index.ios"withExtension:@"jsbundle"];
#endif
? ? NSLog(@"文件不存在");
? ? returnjsCodeLocation;
? }
}

大功告成運行項目。
到這里最基礎(chǔ)的熱更新/熱部署服務(wù)就搭建完成。如果想要更加完善還是需要花點功夫的,畢竟師傅領(lǐng)進(jìn)門修行靠個人。