1. 程式人生 > >React Native實現一個自定義模組

React Native實現一個自定義模組

概述

在 前期介紹React Native 專案結構的時候,我們講解過React的專案組成,其中說過 node_modules 資料夾,這是一個存放 node 模組的地方。我們知道React是用npm來管理專案的。提到npm,一般指兩層含義:一是 Node.js 開放式模組登記和管理系統,另一種是 Node.js 預設的模組管理器,是一個命令列軟體,用來安裝和管理 node 模組。

npm 使用介紹

npm 是一個 Node.js 模組,安裝 Node.js 會預設安裝 npm,可以在終端中使用以下命令來檢視 npm 的版本:
npm -v
升級可以使用下面的命令
sudo npm install npm -g
安裝模組(安裝完畢後會產生一個node_modules目錄,其目錄下就是安裝的各個node模組)
npm install <ModuleName>

其他的用法還請參考之前的部落格:npm使用詳解
今天我們要說的是用npm來建立一個我們自己的模組,就是Android的Libary

建立自定義模組

React Native 雖然實現了很多 Native 元件,並且提供了豐富的 API,但是有些原生庫還是不支援的,而且有很多開源的元件和庫是面向原生的,因此要想在 React Native 中使用這些元件和庫就需要自己定義一個模組,這樣也方便別人整合,我們還可以把它發到出去供別使用。首先我們執行init建立一個專案:
react-native init AwesomeProject

這裡以 Android 為例,用 Android Studio 選擇選單 File->open 開啟 AwesomeProject 資料夾下的 android 資料夾,然後選擇 File -> New -> New Module,選擇建立一個 Android Library,如圖:



然後將所需要依賴的 jar 放到 libs 目錄下,這裡以使用 jpush-sdk 為例,將官網上下載的 libs 複製到 libs 下,把相關的資原始檔放到 res 資料夾下,再把 AndroidManifest 檔案內容複製過來,更改一下包名,最後在 build.gradle 中配置一下。
apply plugin: 'com.android.library'
android {    
  compileSdkVersion 23    
  buildToolsVersion "23.0.2"    
  defaultConfig {        
    minSdkVersion 16        
    targetSdkVersion 22        
    versionCode 1        
    versionName "1.0"        
    manifestPlaceholders = [                
      JPUSH_APPKEY: "yourAppKey",  //在此修改JPush的AppKey                    
      APP_CHANNEL: "developer-default"      //應用渠道號        
    ]    
  }    
  lintOptions {        
    abortOnError false        
    warning 'InvalidPackage'    
  }    
  sourceSets {        
    main {            
      jniLibs.srcDirs = ['libs']        
    }    
}
}
repositories {    
  mavenCentral()
}

dependencies {    
  compile fileTree(dir: 'libs', include: ['*.jar'])    
  compile "com.facebook.react:react-native:+"
}
接下來需要寫 Native 和 JS 互動的程式碼,這個可以參考之前的關於原生和js互動的文章點選開啟連結。假設我們已經完成了 Native 部分程式碼,我們如何才能在 JS 中讓他人能夠通過 import 的方式呼叫我們的 JS 程式碼,從而呼叫 Native 呢?首先進入 my-react-library 資料夾,然後在終端執行:
npm init
生成 package.json 檔案(注意這裡的 name 欄位,這裡是別人引用你的模組的名字),然後再建立一個 index.js 檔案,這是 node 模組的 JS 入口。例如我引用了jpush
import {NativeModules, Platform, DeviceEventEmitter} from 'react-native';

// 通過 NativeModules 找到我們在 Native 定義的 JPushModule 類
const JPushModule = NativeModules.JPushModule;

export default class JPush {

    /**
     * Android only
     * 初始化JPush 必須先初始化才能執行其他操作
    */
    static initPush() {
        JPushModule.initPush();
    }
}

釋出自定義模組

到此為止,我們已經完成了 React Native 自定義模組。現在可以釋出我們的自定義模組了。在 package.json 所在的目錄下執行:
npm publish
這樣就可以把我們的自定義模組上傳到 npm 庫了,每次更新版本時,需要改動 package.json 中的 version 值,然後再執行 npm publish 即可。

儲存自定義模組

安裝完成後就會把這個模組儲存到 node_modules 資料夾下,由於我們的模組是一個 Android Library 專案,所以在 Native 中還需要配置一下。我們使用如下命令儲存自己的模組。
npm install my-react-library --save

someone's react-native project/some module/build.gradle

這裡主要是新增專案依賴

dependencies {    
  compile fileTree(dir: "libs", include: ["*.jar"])    
  compile "com.android.support:appcompat-v7:23.0.1"    
  compile "com.facebook.react:react-native:+"  // From node_modules   
  // 在 dependecies 中加入自定義模組 
  compile project(':my-react-library')
}
然後在 settings.gradle 中也要配置一下(這個搞過Android就很熟悉)

include ':app', ':my-react-library'
project(':my-react-library').projectDir = new File(rootProject.projectDir, '../node_modules/my-react-library/android')

在 MainActivity 中將自定義的 Package 新增進去(因為啟動時在這裡觸發的)
mReactInstanceManager = ReactInstanceManager.builder()                
.setApplication(getApplication())                
.setBundleAssetName("index.android.bundle")                
.setJSMainModuleName("react-native-android/index.android")                
.addPackage(new MainReactPackage())
//新增自定義的 package
.addPackage(new MyReactPackage())
如果是 RN 0.29.0 以上版本,則還應在 MainApplication 中新增
@Overrideprotected List<ReactPackage> getPackages() {    
return Arrays.<ReactPackage>asList(            
    new MainReactPackage(),            
    new MyReactPackage()    
  );
}
到此為止我們完成了 Native 部分的配置(完成後 sync 一下),接下來就可以使用了。
export default class SomeClass  extends React.Component {
    componentDidMount() {
      // 呼叫 index.js 中定義的 doSomething()
      MyModule.doSomething();
    }
}