2019-08-20 RN 0.60 autolink 探究

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() 的代码,iospodfile 文件中,会有指明这个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)

applyNativeModulesSettingsGradleapplyNativeModulesAppBuildGradle这两个gradle方法都是在native_modules中的脚本定义的
native_modules是一个gradle脚本,在/node_modules/@react-native-community 文件夹中,源码链接

  1. 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.
  2. 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 the config 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依赖,这些信息可以通过 CLIconfig 命令,输出成一个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端同理,这里不再深究

你可能感兴趣的:(2019-08-20 RN 0.60 autolink 探究)