最近一段时间在学习和实践React Native,做一些有趣有用总结和大家分享。这个系列文章可以在这里找到。
Facebook官方提供的迁移方式适合于简单的项目,并且没有运行react-native init
命令构建的项目的一些自动化配置。于是摸索了一下适合于大型复杂项目的迁移方式。
集成React Native库方式的选择
官方推荐的迁移方式是通过CocoaPods来管理需要的React Native库(不止一个)。但由于既有项目的结构可能比较复杂,所以也可以直接引入源码自己配置环境。本文采用自己配置的方式,如果既有项目结构允许用CocoaPods配置的话还是推荐CocoaPods。
在现有项目中集成React Native
项目结构
我们先来回顾一下执行react-native init
命令创建的项目结构,现有项目配置之后也希望与之相同。
项目名为firststep
,和项目相关的代码文件存储在firststep
目录下:
- ios: iOS项目相关代码,就是普通iOS开发中在Xcode运行的一坨东西;
- android:同理android项目相关代码;
- index.ios.js: ios平台加载的JS脚本;
- index.android.js:android平台加载的JS脚本;
- package.json:把当前项目作为npm package的配置文件;内容如下:
{
"name": "firststep",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "react-native start"
},
"dependencies": {
"react-native": "^0.15.0"
}
}
初始化React Native环境
在我们要配置React Native环境的项目中,进入到*.xcodeproj
文件的上级目录,运行React Native初始化react-native init [Project Name]
。
prompt
先输入yes,这样会在ios
目录下生成一个同名工程,接下来我们需要把这个同名工程的配置迁移到现有项目。
友情提示:
init
过程可能会有一点慢,也没有提示,需要耐心等一下。
链接React Native的Libraries
查看ios
目录下的那个同名工程,会看到这个工程引用的React Native库如下:
默认项目中链接了这么多库,我们暂时可能用这么多。在开发过程中如果需要其他组件,也以相同方式加入到项目中。具体操作如下:
- 右键项目目录,选择
New Group
,新建一个名为Libraries
的逻辑目录; - 在node modules目录中找到
$root_path/node_modules/react-native/Libraries
,React Native的所有库都是以static library的形式提供; - 将对应的
.project
文件拖入到Libraries
逻辑目录下,效果如上图所示; - 在
Build Phase
中link刚才添加的库;
编译前执行React Native shell脚本
React Native的iOS项目在编译时会先运行一个shell脚本,其作用有两个:
- 从Terminal启动了一个Node.js的server用于React Native调试;
- 将React Native的资源文件打包放在编译目录下。
具体添加方式是: - 点击
Build Phase
界面左上角的+
号, - 添加一个
Run Script Phase
。 - 指定开始编译时运行shell脚本:
node_modules/react-native/packager/react-native-xcode.sh
。 - 注意:根据当前项目的根目录(project文件所在目录)配置
node_modules
的位置。
解释一下这个脚本,脚本内容如下:
case "$CONFIGURATION" in
Debug)
DEV=true
;;
Release)
DEV=false
;;
"")
echo "$0 must be invoked by Xcode"
exit 1
;;
*)
echo "Unsupported value of \$CONFIGURATION=$CONFIGURATION"
exit 1
;;
esac
# Xcode project file for React Native apps is located in ios/ subfolder
cd ..
set -x
DEST=$CONFIGURATION_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH
# Define NVM_DIR and source the nvm.sh setup script
[ -z "$NVM_DIR" ] && export NVM_DIR="$HOME/.nvm"
if [[ -s "$HOME/.nvm/nvm.sh" ]]; then
. "$HOME/.nvm/nvm.sh"
elif [[ -x "$(command -v brew)" && -s "$(brew --prefix nvm)/nvm.sh" ]]; then
. "$(brew --prefix nvm)/nvm.sh"
fi
react-native bundle \
--entry-file index.ios.js \
--platform ios \
--dev $DEV \
--bundle-output "$DEST/main.jsbundle" \
--assets-dest "$DEST"
react-native bundle
:这个脚本其实只是执行了最后一行的bundle
命令;CONFIGURATION
部分从Xcode运行shell的参数中获取当前的编译环境(Build Configuration);cd ..
:值得特殊注意的是:由于默认项目位置在ios/AwsomeProject.xcodeproj
目录下,因此需要先调到上级目录执行,否则取到的参数可能发生错误。如果你的目录结构与默认项目不同,则需要根据实际情况修改这行脚本,或者移动project文件到适当位置。例如project文件和node_moduels等配置文件在统一目录下则直接注释掉这一行,直接在当前目录执行后续命令即可。DEST
:取的是编译后App的目录位置,运行bundle
生成的main.jsbundle
和assets文件都放到这个目录下;nvm
:nvm这个部分最终执行了nvm.sh
这个文件,启动一个Node.js的server在8081端口;- 最后一行在
cd
进入或者当前目录下运行react-native bundle
命令,命令的具体参数解释请看这里。
还遇到一件诡异的事情是,假如说我们在本次运行项目的时候,不希望执行这个sh脚本,在Xcode配置中将它注释掉了。那么注释sh脚本的修改只有重新打开这个Xcode项目后,脚本的修改才生效,当次编译运行还是会运行这个脚本。推测可能是Xcode做了缓存,有了解原因的朋友请留言指教~
此时运行项目即可看到从Terminal启动了一个Node.js的server,并且在DEST目录下生成了jsbundle文件。
清理多余文件
删除ios/
和android/
下的文件,这些是init
命令自动生成的。
特别是
ReactAndroid
目录下的assert.h
会导致Xcode编译不过,删掉这个目录即可。
到此现有项目的迁移工作和React Native库集成已经完成,但是我们还没有具体的功能页面,接下来的文章会介绍。
React Native开发基本流程
现有项目迁移到React Native之后的基本开发流程大致如下,后续的文章也会根据这几部分来写,大家可以提前熟悉一下。
- 运行
react-native init
初始化环境; - 改造现有项目;
- 利用React Native本地server进行功能的调试开发;
react-native bundle
生成jsbundle
文件;- 修改项目中引用的
jslocation=
的代码; - 将引用了
jsbundle
文件的项目打包发布。
参考意见
- 查看React Native的项目结构会发现node_modules目录,包含的是React Native js文件运行的一些依赖库,类似于Ruby的gems,如果不经常变化建议上传到版本控制工具上,这样团队其他成员更新项目之后就不需要运行
npm install
再次配置。
可能用到的小技巧
- Xcode sh脚本Log查看:脚本中echo打印出的log可以在Xcode的
Navigator->Report Navigator
(快捷键:cmd+8)中查看到。 - 如果你的项目配置是用xcconfig文件配置的,那么xcconfig文件递归方式读取headers的方式如下:
"$(SRCROOT)/../node_modules/react-native/React"/**
,即增加/**
。
Where to go
React Native入门实例教程 - 一个完整的React Native例子