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 config
to 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
platforms
configuration. It instructs the CLI on how to find information about native dependencies. This information is exposed through theconfig
command 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
端同理,这里不再深究
网友评论