小试 iOS 动态化 (一)

16年,也就是去年,iOS 的热更新大火,最火的莫过于JSPatch这个框架,可以说是随意patch绝大部分方法,接着便是React Native这个跨平台的框架,两者都是利用iOS 的 JavaScript Core 这个系统框架来干活。

今年初刚打算学一学热更新,JSPatch就被苹果封杀了,年中闲来无事,打算看看RN,Facebook 修改了React 和 React Native的开源协议,搞得人心惶惶。话说,现在 React 改回了 MIT 协议,RN 还是BSD附带私有协议,但 github 上提交依然非常频繁,也不知道具体情况是什么样。

在原生项目中嵌入React Native 模块

RN 这个框架其实挺鸡肋的,坑很多,痛点很多,说是能跨平台,但项目如果复杂了,很多模块都是一个平台一份,不能通用,动画、内存这些更是拍马都追不上原生。

那么它刚出来的时候凭什么跟原生叫板呢?因为开发速度快,它可以写一行就热更新一次,不需要重新编译,如果熟练的话,开发速度肯定比原生要快的。由于本身的特性,热更新是自带的。

而且自从 JavaScript 的 ES6 发布后,前端的春天一直持续到今天,而且相对来说,前端工资要价比iOS 开发要低,一个本来就用React开发的 wap 端应用,让前端修修补补就成了iOS 或安卓的原生应用了,是挺舒服的。

所以,一些试验性的小功能,用RN来试水也未尝不可,下面简单地在原生应用里嵌入RN模块。

  • 新建一个React Native 项目

React Native 的安装就不赘述了,就按官方示例来新建一个RN项目

1
react-native init AwesomeProject

该命令后可跟

0.44.3``` 来指定RN版本,这里默认用当前最新的 0.50.4
1
2
3
4
5
6
7

- 打包React Native 项目

在该项目根目录下新建一个文件夹,命名为 "release_ios",执行命令

```shell
react-native bundle --entry-file index.js --platform ios --dev false --bundle-output release_ios/awesome.jsbundle --assets-dest release_ios/

执行完后,release_ios 文件夹下会有一个awesome.jsbundle文件,将它上传到自己的博客服务器,或随便一个github仓库,获得下载地址即可。
这里注意一下,0.49 之前的版本

后应该跟 ```index.ios.js```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

- 新建一个原生的 iOS 项目

在项目根目录下新建一个文件夹,命名为 "ReactComponent",复制粘贴上面RN项目根目录下的package.json到 "ReactComponent"下,编辑该文件,删减得剩如以下字段

```json
{
"name": "AwesomeProject",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start"
},
"dependencies": {
"react": "16.0.0",
"react-native": "0.50.4"
}
}

接着执行

1
npm install
  • 安装React 模块

还需要用 cocoapods 安装一些模块,podfile如下

1
2
3
4
5
6
7
8
9
10
11
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'DevSupport', # 如果RN版本 >= 0.43,则需要加入此行才能开启开发者菜单
'RCTText',
'RCTNetwork',
'RCTWebSocket', # 这个模块是用于调试功能的
'BatchedBridge',
# 在这里继续添加你所需要的模块
]
# 如果你的RN版本 >= 0.42.0,则加入下面这行
pod "yoga", :path => "../node_modules/react-native/ReactCommon/yoga"

注意

这个模块,这个官方教程里没有的,以前旧的版本也不需要,在0.50.4这个版本里必须加入才能进行原生嵌入,不然会报错,其它版本未知。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

- 初始化RN模块

在需要加载RN模块的控制器viewdidload里加入以下代码

```objc
NSString * strUrl = @"https://sephilex.com/awesome.bundle";
NSURL * jsCodeLocation = [NSURL URLWithString:strUrl];
// NSURL * jsCodeLocation = [NSURL URLWithString:[[NSBundle mainBundle] pathForResource:@"index.ios.bundle" ofType:nil]];
RCTRootView * rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"AwesomeProject"
initialProperties:nil
launchOptions:nil];
rootView.frame = CGRectMake(0, 0, 375, 600);
[self.view addSubview:rootView];

_backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_backBtn setBackgroundColor:[UIColor darkGrayColor]];
[_backBtn setTitle:@"BACK" forState:UIControlStateNormal];
[_backBtn setFrame:CGRectMake(0, 600, 375, 67)];
[_backBtn addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_backBtn];

要注意module名要与打包的index.js里的一致,此外服务器返回的http响应头有可能会使应用报错,nginx 遇到这种’bundle’这种不认识的后缀,会返回的默认的 Content-type application/octet-stream,这版本的RN要求是 application/javascripttext/javascript,这时可以考虑把这个bundle下载到本地再去加载。

运行截图

2017-12-01-71512107096_.pic

  • 修改RN项目

在RN项目稍作修改后,再打包上传,iOS应用不需要重新编译执行,重新进入RN页面即可

运行截图

2017-12-01-81512107096_.pic

这里改了文字和背景颜色,这样就模拟了一次热更新

本文标题:小试 iOS 动态化 (一)

文章作者:Sephilex

发布时间:2017年11月20日 - 20:11

最后更新:2020年07月09日 - 18:07

原始链接:https://sephilex.com/2017/11/20/小试 iOS 动态化 (一)/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!