RN 0.60 支持 autolinking了,对于有native 代码的第三方库,npm install 或者 yarn add之后,不需要执行react-native link XXX 了,但是由于iOS端默认使用了cocoapods 来管理依赖,我们还需要进入项目的iOS目录,执行pod install.
于是,安装完有native依赖的第三方库后,运行 git status 看一下项目的文件变化,咦?原生ios目录或者android 目录下的文件居然没有任何变化。之前版本RN手动link后,会多出一些原生代码的变动,比如setting.gradle文件下,会有指明这个module所在路径的代码,mainApplication.java中,会有new xxxPackage() 的代码,ios 的 podfile 文件中,会有指明这个pod的所在路径的代码...
这个autolink 这么神奇吗?居然不需要改动任何native代码就可以添加带native代码的第三方依赖到项目中? 我仔细研究了一下官方的说明文档,发现了一些与原来不一样的地方:
- iOS中,在
podfile中,多一些这样的代码
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
use_native_modules!
看了一下native_modules.rb的源码,是一个Ruby脚本文件(CocoaPods本身就是Ruby写的, podfile就是个Ruby文件),当你执行pod install时,执行了这个脚本
This is a function which is used inside your Podfile. It uses
react-native configto grab a list of dependencies, and pulls out all of the ones which declare themselves to be iOS dependencies (via having a Podspec) and automatically imports those into your current target.
native_modules.rb是在podfile中执行的一个function,它利用react native config这个cli命令,来抓取项目的所有依赖,然后把含有podspec文件的依赖自动的引入到cocoapods中
所以,有了这个脚本,安装完依赖后,podfile就没有任何变化了,只是podfile.lock文件有变化
- Android中,同理,也有一些变化
setting.gradle文件也有了变化:
rootProject.name = 'kjkDoctor'
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle");
applyNativeModulesSettingsGradle(settings)
include ':app'
app/build.gradle最后一行多了如下代码
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle");
applyNativeModulesAppBuildGradle(project)
applyNativeModulesSettingsGradle和 applyNativeModulesAppBuildGradle这两个gradle方法都是在native_modules中的脚本定义的
native_modules是一个gradle脚本,在/node_modules/@react-native-community 文件夹中,源码链接
- At build time, before the build script is run:
1⃣️.A first Gradle plugin (in settings.gradle) runs applyNativeModulesSettingsGradle method. It uses the package metadata from react-native config to add Android projects.
2⃣️.A second Gradle plugin (in app/build.gradle) runs applyNativeModulesAppBuildGradle method. It creates a list of React Native packages to include in the generated /android/build/generated/rn/src/main/java/com/facebook/react/PackageList.java file.- At runtime, the list of React Native packages generated in step 1.2 is registered by getPackages method of ReactNativeHost in MainApplication.java.
build阶段,在build脚本运行之前,先运行了setting.gradle中的applyNativeModulesSettingsGradle方法,这个方法利用react-native config 命令,拿到package metadata,添加依赖到你的android项目中.
然后,再运行了build.gradle中的applyNativeModulesAppBuildGradle 方法,在/android/build/generated/rn/src/main/java/com/facebook/react/PackageList.java中,生成了一系列的package (之前是在mainApplication中通过new xxxPackage()),注意这个目录是android/build下的目录,是排除版本控制的,所以用git status 就没有任何文件变化了...
运行阶段,MainApplication.java 中的ReactNativeHost方法会将上一步中生成的package注册到项目中,这样就达到了与之前手动link一样的效果了.
Each platform defines its own
platformsconfiguration. It instructs the CLI on how to find information about native dependencies. This information is exposed through theconfigcommand in a JSON format. It's then used by the scripts run by the platform's build tools. Each script applies the logic to link native dependencies specific to its platform.
总结来说,含有原生代码的库的每个platform都有自己的配置信息,来告诉CLI 如何找到native依赖,这些信息可以通过 CLI 的 config 命令,输出成一个JSON 形式的配置信息。 然后各个平台的build工具(gradle、cocoapods)可以利用这些配置信息,来自动link这些native依赖
update
config 命令的原理是什么呢?为什么这个命令能找到依赖库的相关信息呢
如果第三方库的项目根目录下有react-native.config.js文件,则直接可以读取到相关信息;
否则:
Gets android project config by analyzing given folder and taking some defaults specified by user into consideration
比如Android端,通过分析项目的目录结构,来找到相关信息:https://github.com/react-native-community/cli/blob/master/packages/platform-android/src/config/index.ts
config命令典型的输出格式如下:
...
"jpush-react-native": {
"root": "/Users/FaiChou/Projects/XXX/node_modules/jpush-react-native",
"name": "jpush-react-native",
"platforms": {
"ios": {
"sourceDir": "/Users/FaiChou/Projects/XXX/node_modules/jpush-react-native/ios",
"folder": "/Users/FaiChou/Projects/XXX/node_modules/jpush-react-native",
"pbxprojPath": "/Users/FaiChou/Projects/XXX/node_modules/jpush-react-native/ios/RCTJPushModule.xcodeproj/project.pbxproj",
"podfile": null,
"podspecPath": "/Users/FaiChou/Projects/XXX/node_modules/jpush-react-native/JPushRN.podspec",
"projectPath": "/Users/FaiChou/Projects/XXX/node_modules/jpush-react-native/ios/RCTJPushModule.xcodeproj",
"projectName": "RCTJPushModule.xcodeproj",
"libraryFolder": "Libraries",
"sharedLibraries": [],
"plist": [],
"scriptPhases": []
},
"android": {
"sourceDir": "/Users/FaiChou/Projects/XXX/node_modules/jpush-react-native/android",
"folder": "/Users/FaiChou/Projects/XXX/node_modules/jpush-react-native",
"packageImportPath": "import cn.jiguang.plugins.push.JPushPackage;",
"packageInstance": "new JPushPackage()"
}
},
"assets": [],
"hooks": {},
"params": []
},
...
有了这些信息,RN 就知道如何自动生成package了,也就实现了AutoLink.
iOS端同理,这里不再深究









网友评论