ReactNative解決方案研究

前言

2014年8月,faceBook內(nèi)部傳出用一種新技術(shù)開發(fā)App的新方案,次年3月,該技術(shù)開源并正式發(fā)布,它的名字如雷貫耳——“React Native”,我們簡稱為“RN”。在實(shí)現(xiàn)媲美 NativeApp 用戶體驗(yàn)的同時(shí),RN允許Web開發(fā)者更多地基于現(xiàn)有經(jīng)驗(yàn)組件化開發(fā)App,并且具備跨平臺(tái)特性,開發(fā)效率和成本都相當(dāng)可觀。不過它也有著一般跨平臺(tái)App共有的短板,就是它的兼容性,尤其是針對(duì) 國內(nèi)層出不窮的 Android機(jī)型,逐個(gè)兼容似乎不太可能。不過好在這類系統(tǒng)提供商不停地升級(jí)系統(tǒng),facebook 也孜孜不倦地提升RN的性能和兼容性,再配合它的強(qiáng)大社區(qū)和React生態(tài)圈,目前使用RN構(gòu)建App這個(gè)技術(shù)已經(jīng)日趨成熟。國內(nèi)很多知名互聯(lián)網(wǎng)企業(yè)也開始應(yīng)用這門技術(shù),比如 京東App,攜程App,美團(tuán)App里也混合了RN頁面。

目前主流的應(yīng)用大體分為三類:NativeApp、WebApp和HybridApp,先羅列一下三者的特點(diǎn):

目前App三者優(yōu)缺點(diǎn)

NativeApp特點(diǎn)

  • 性能好
  • 完美的用戶體驗(yàn)
  • 開發(fā)成本高,無法跨平臺(tái)
  • 升級(jí)困難(審核), 維護(hù)成本高

WebApp特點(diǎn)

  • 開發(fā)成本低,更新快,版本升級(jí)容易,自動(dòng)升級(jí)
  • 跨平臺(tái),”Write Once , Run Anywhere”
  • 無法調(diào)用系統(tǒng)級(jí)的API
  • 臨時(shí)入口,用戶留存度低
  • 性能差,體驗(yàn)差,設(shè)計(jì)受限制
  • 相比Native App,Web App體驗(yàn)中受限于以上5個(gè)因素:網(wǎng)絡(luò)環(huán)境,渲染性能,平臺(tái)特性,受限于瀏覽器,系統(tǒng)限制。

HybridApp特點(diǎn)

  • NativeApp 和 WebApp 折中的方案,保留了 NativeApp 和 WebApp 的優(yōu)點(diǎn)。

  • 但是還是性能差。頁面渲染效率低,在Webview中繪制界面,實(shí)現(xiàn)動(dòng)畫,資源消耗都比較大, 受限于技術(shù), 網(wǎng)速等因素

    關(guān)系圖

為了解決上述問題,一套高效率,高性能的跨平臺(tái)方案成為了大家熱衷的話題,也就有了下面要比較的 ReactNative 或 Weex 這類解決方案。

ReactNative的特點(diǎn)

優(yōu)勢(shì)相對(duì)HybirdApp或者WebApp

  1. 不用Webview,徹底擺脫了Webview讓人不爽的交互和性能問題
  2. 有較強(qiáng)的擴(kuò)展性,這是因?yàn)镹ative端提供的是基本控件,JS可以自由組合使用
  3. 可以直接調(diào)用Native原生的模塊

優(yōu)勢(shì)相對(duì)于NativeApp

  1. 可以通過更新遠(yuǎn)端JS,直接更新app(熱更新)
  2. 跨平臺(tái)特性
  3. 學(xué)習(xí)成本低,組件式開發(fā),代碼復(fù)用性高。

劣勢(shì)

[!RN的劣勢(shì)]

  1. 擴(kuò)展性仍然遠(yuǎn)遠(yuǎn)不如web,也遠(yuǎn)遠(yuǎn)不如直接寫 Native code
  2. 從Native到Web,要做很多概念轉(zhuǎn)換,勢(shì)必造成雙方都要妥協(xié)。比如web要用一套CSS的閹割版,Native通過css-layout拿到最終樣式再轉(zhuǎn)換成native原生的表達(dá)方式(比如iOS的Constraint\origin\Center等屬性),再比如動(dòng)畫。另外,若Android和iOS都要做相同的封裝,概念轉(zhuǎn)換就更復(fù)雜了。
  3. 內(nèi)存占用較大。

RN與Weex的選擇

首先我覺得RN和Weex都是很棒的跨平臺(tái)解決方案,兩者都有各自的優(yōu)秀之處,知乎和簡書上關(guān)于兩者比較的文章車載斗量,不過推薦RN的居多吧。我本人搞RN開發(fā)也1年多了,期間踩過的坑不少,不過基本能通過Google最后解決。這里順帶一提,RN的社區(qū)非常強(qiáng)大,不過很大一部分活躍在國外,所以很多優(yōu)秀的解決方案和三方模塊來自國外,需要一定的外語閱讀能力和Google技巧,算是門檻上比Weex要高一些。我這里不帶太多的情緒,因?yàn)闆]有實(shí)際在項(xiàng)目中運(yùn)用過 Weex,不過多評(píng)價(jià)這門框架。我只是從其他角度去考慮:

  1. 技術(shù)棧。我們目前的前端技術(shù)棧用的React,那么切換到ReactNative的成本應(yīng)該比 vue技術(shù)棧的 Weex 低一些。
  2. 現(xiàn)有積累。我對(duì)RN有一定的項(xiàng)目經(jīng)驗(yàn),支持現(xiàn)階段的產(chǎn)品需求難度不大,而且目前國內(nèi)介紹和總結(jié)RN的書籍很多,門檻已經(jīng)沒有1年前那么高了。
  3. 長遠(yuǎn)考慮。RN的版本更新速度快(性能和兼容性方面的改進(jìn)工作迅速),社區(qū)活躍,靈活度高,國內(nèi)成功案例很多,技術(shù)成熟度上應(yīng)該比 Weex 好一些。

潛在隱患

Android稍微好一些,AppStore對(duì)于RN和其他跨平臺(tái)App開發(fā)始終抱有一絲敵意,從長遠(yuǎn)考慮,HybridApp的方案應(yīng)該保留,可以做為未知情況的降級(jí)方案。

RN實(shí)現(xiàn)原理簡析

普通的JS-OC通信實(shí)際上很簡單,OC向JS傳信息有現(xiàn)成的接口,像Webview提供的stringByEvaluatingJavaScriptFromString方法可以直接在當(dāng)前context上執(zhí)行一段JS腳本,并且可以獲取執(zhí)行后的返回值,這個(gè)返回值就相當(dāng)于JS向OC傳遞信息。ReactNative也是以此為基礎(chǔ),通過各種手段,實(shí)現(xiàn)了在OC定義一個(gè)模塊方法,JS可以直接調(diào)用這個(gè)模塊方法并還可以無縫銜接回調(diào)。簡單地說就是:“模塊化,模塊配置表,傳遞ID,封裝調(diào)用,事件響應(yīng)”

IOS端實(shí)現(xiàn)原理

OC原理

Android端實(shí)現(xiàn)原理

Java原理

RN常用的構(gòu)建方案

RN目前有兩種構(gòu)建方案:

(1)RN為主,Native為輔。整個(gè)App都由RN構(gòu)建,Native把功能模塊按照RN的規(guī)則進(jìn)行封裝,然后交給RN進(jìn)行調(diào)用。適合交互場(chǎng)景不太復(fù)雜的App,如金融、物流管理類App,基于Labs的微社交類App。

(2) Native為主,RN為輔。整個(gè)App構(gòu)建工作由Native完成,然后把某些功能模塊的入口換成RN,然后控制權(quán)交給RN,RN又可以切換回Native。簡單地說也就是替代Hybrid之前的位置。適合做一些功能不需要很酷炫,版本迭代頻繁的業(yè)務(wù)場(chǎng)景(如專場(chǎng)、活動(dòng)頁、產(chǎn)品詳情等)。

*RN混合開發(fā)技術(shù)

也是上面說的第二類構(gòu)建方案,核心是實(shí)現(xiàn)RN與Native之間的通信和互相調(diào)用。

IOS原生界面跳轉(zhuǎn)RN界面

現(xiàn)階段混合開發(fā)中,一般就是在原有原生項(xiàng)目基礎(chǔ)上面添加RN開發(fā)的頁面。那么這邊我們講解一下從原生界面跳轉(zhuǎn)到RN頁面的方法。其實(shí)是非常簡單的,就是普通push一個(gè) ViewController 即可,在新打開的 ViewController 中加入 RCTRootView 視圖,具體承載RN頁面的控制器的代碼如下:

#import "TwoViewController.h"

#import "RCTRootView.h"

#import "ThreeViewController.h"

@implementation TwoViewController

- (void)viewDidLoad {

  [super viewDidLoad];

  self.title=@"RN界面";

  NSURL *jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];

  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation

                                                      moduleName:@"mixedDemo"

                                               initialProperties:nil

                                                   launchOptions:nil];

  self.view=rootView;

}

@end

RN訪問調(diào)用IOS原生方法

要實(shí)現(xiàn)這個(gè)功能,我們首先需用創(chuàng)建一個(gè)實(shí)現(xiàn)"RCTBridgeModule"協(xié)議的RNBridgeModule橋接類,看一下RNBridgeModule.h文件:


#import <Foundation/Foundation.h>

#import "RCTBridgeModule.h"

@interface RNBridgeModule : NSObject<RCTBridgeModule>

@end

接著我們需要在 RNBridgetModule 的實(shí)現(xiàn)類中,實(shí)現(xiàn) RCT_EXPORT_MODULE() 宏定義,括號(hào)參數(shù)不填為默認(rèn)橋接類的名稱,也可以自定義填寫。該名稱用來指定在 JavaScript 中訪問這個(gè)模塊的名稱。

使用Callback進(jìn)行回調(diào)

接下來我們?cè)?RNBridgeModule.m 文件里添加如下的方法:

//RN傳參數(shù)調(diào)用原生OC,并且返回?cái)?shù)據(jù)給RN  通過CallBack

RCT_EXPORT_METHOD(RNInvokeOCCallBack:(NSDictionary *)dictionary callback:(RCTResponseSenderBlock)callback){

   NSLog(@"接收到RN傳過來的數(shù)據(jù)為:%@",dictionary);

   NSArray *events = [[NSArray alloc] initWithObjects:@"張三",@"李四", nil];

   callback(@[[NSNull null], events]);

}

如果原生的方法要被 JavaScript 進(jìn)行訪問,那么該方法需要使用 RCT_EXPORT_METHOD() 宏定義進(jìn)行聲明。該聲明的 RNInvokeOCCallBack 方法有兩個(gè)參數(shù):第一個(gè)參數(shù)代表從 JavaScript 傳過來的數(shù)據(jù),第二個(gè)參數(shù)是回調(diào)方法,通過該回調(diào)方法把原生信息發(fā)送到 JavaScript 中。其中上面的 callback 方法中傳入一個(gè)參數(shù)數(shù)組,其實(shí)該數(shù)組的第一個(gè)參數(shù)為一個(gè) NSError 對(duì)象,如果沒有錯(cuò)誤返回 null,其余的數(shù)據(jù)作為該方法的返回值回調(diào)給 JavaScritpt

最后我們需要在 JavaScript 文件中進(jìn)行定義導(dǎo)出在原生封裝的模塊,然后調(diào)用封裝方法訪問即可:


var { NativeModules } = require('react-native')

var RNBridgeModule = NativeModules.RNBridgeModule

RNBridgeModule.RNInvokeOCCallBack(

  { 'name': 'jiangqq', 'description': 'http://www.lcode.org' },

  (error, events) => {

    if(error) {

      console.error(error)

    } else {

      this.setState({ events: events })

    }

  }

)

使用Promise進(jìn)行回調(diào)

我們?cè)?RNBridgetModule 的實(shí)現(xiàn)類添加如下的方法代碼:


//RN傳參數(shù)調(diào)用原生OC, 并且返回?cái)?shù)據(jù)給RN通過Promise

RCT_EXPORT_METHOD(RNInvokeOCPromise:(NSDictionary *)dictionary resolver:(RCTPromiseResolveBlock)resolve

                  rejecter:(RCTPromiseRejectBlock)reject) {

   NSLog(@"接收到RN傳過來的數(shù)據(jù)為:%@",dictionary);

   NSString *value = [dictionary objectForKey:@"name"];

   if([value isEqualToString:@"jiangqq"]) {

     resolve(@"回調(diào)成功啦,Promise...");

   } else {

     NSError *error = [NSError errorWithDomain:@"傳入的name不符合要求,回調(diào)失敗啦,Promise..." code:100 userInfo:nil];

     reject(@"100",@"傳入的name不符合要求,回調(diào)失敗啦,Promise...",error);

   }

}

這邊定義了 RNInvokeOCPromise 方法,共有三個(gè)參數(shù):

  • dictionary: JavaScript傳入的數(shù)據(jù)
  • resolve: 成功,回調(diào)數(shù)據(jù)
  • reject: 失敗,回調(diào)數(shù)據(jù)
    其中resove方法傳入具體的成功信息即可,但是reject方法必須傳入三個(gè)參數(shù)分別為,錯(cuò)誤代碼code ,錯(cuò)誤信息message以及NSError對(duì)象。最終看一下JavaScript中的調(diào)用方式:

var { NativeModules } = require('react-native')

var RNBridgeModule = NativeModules.RNBridgeModule

//獲取Promise對(duì)象處理

async _updateEvents() {

  try {

    var events = await RNBridgeModule.RNInvokeOCPromise({ 'name': 'jiangqqlmj' })

    this.setState({ events })

  } catch(e) {

    this.setState({ events: e.message })

  }

}

IOS原生訪問調(diào)用RN

如果我們需要從iOS原生方法發(fā)送數(shù)據(jù)到JavaScript中,那么可以使用eventDispatcher

首先我們需要在 RCTBridgeModule 的實(shí)現(xiàn)中中引入:

#import "RCTBridge.h"

#import "RCTEventDispatcher.h"

@synthesize bridge = _bridge;

接下來就能通過OC原生代碼來訪問JavaScript


self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder" body:@{@"name":[NSString stringWithFormat:@"%@",value],@"errorCode":@"0",@"msg":@"成功"}];

這里補(bǔ)充說明一下 sendAppEventWithName 方法,它包含2個(gè)參數(shù):

  • EventReminder:自定義的一個(gè)事件名稱
  • 具體摻入JavaScript 的數(shù)據(jù)信息
OC調(diào)用RN的具體代碼
// OC調(diào)用RN

RCT_EXPORT_METHOD(VCOpenRN:(NSDictionary *)dictionary) {

  NSString *value = [dictionary objectForKey:@"name"];

  if([value isEqualToString:@"jiangqq"]) {

    [self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder" body:@{@"name":[NSString stringWithFormat:@"%@",value],@"errorCode":@"0",@"msg":@"成功"}];

  } else {

    [self.bridge.eventDispatcher sendAppEventWithName:@"EventReminder" body:@{@"name":[NSString stringWithFormat:@"%@",value],@"errorCode":@"0",@"msg":@"輸入的name不是jiangqq"}];

  }

}

然后在 JavaScript 端進(jìn)行調(diào)用的方法如下:

import { NativeAppEventEmitter } from 'react-native'

// ...省略一部分代碼

  componentDidMount() {

    console.log('開始訂閱通知...')

    subscription = NativeAppEventEmitter.addListener(

      'EventReminder',

      (reminder) => {

        let errorCode = reminder.errorCode

        if (errorCode === 0) {

          this.setState({ msg: reminder.name })

        } else {

          this.setState( {msg: reminder.msg })

        }

      }

    )

  }

  componentWillUnmount() {

    subscription.remove()

  }

RN界面調(diào)用ANDROID原生界面

在Android原生開發(fā)中,我們知道打開一個(gè)Activity一般有兩種方法:顯式和隱式。隱式方法一般通過AndroidManifest配置文件中的Activity Intent-Filter中進(jìn)行相關(guān)攔截配置。那么這邊我們主要講解的是顯示啟動(dòng)Activity

下面我們這邊在創(chuàng)建繼承ReactContextBaseJavaModuleIntentModule模塊,具體代碼如下:


package com.mixeddemo;

import android.app.Activity;

import android.content.Intent;

import android.text.TextUtils;

import com.facebook.react.bridge.Callback;

import com.facebook.react.bridge.JSApplicationIllegalArgumentException;

import com.facebook.react.bridge.ReactApplicationContext;

import com.facebook.react.bridge.ReactContextBaseJavaModule;

import com.facebook.react.bridge.ReactMethod;

public class IntentModule  extends ReactContextBaseJavaModule {

  public IntentModule(ReactApplicationContext reactContext) {

    super(reactContext);

  }

  @Override

  public String getName() {

    return "IntentModule";

  }

  /**

   * 從JS頁面跳轉(zhuǎn)到原生activity   同時(shí)也可以從JS傳遞相關(guān)數(shù)據(jù)到原生

   * @param name  需要打開的Activity的class

   * @param params

   */

  @ReactMethod

  public void startActivityFromJS(String name, String params){

    try {

      Activity currentActivity = getCurrentActivity();

      if(null!=currentActivity) {

        Class toActivity = Class.forName(name);

        Intent intent = new Intent(currentActivity,toActivity);

        intent.putExtra("params", params);

        currentActivity.startActivity(intent);

      }

    } catch(Exception e) {

      throw new JSApplicationIllegalArgumentException(

        "不能打開Activity : "+e.getMessage());

    }

  }

}

我們?cè)谶@邊加入了一個(gè)@ReactMethod注解的startActivityFromJS方法,該用于在JS代碼進(jìn)行調(diào)用,從JS端傳過來是兩個(gè)參數(shù),第一個(gè)參數(shù)為需要打開的Activityclass,第二個(gè)參數(shù)為傳遞過來的數(shù)據(jù)。至于為什么用class,那是因?yàn)樵a這邊用反射的形式更加方便了,其他方式都有局限性。這邊我們根據(jù)傳入的class,然后直接調(diào)用startActivity直接打開相應(yīng)的Activity即可,甚至也可以攜帶一些參數(shù)值。具體調(diào)用方法方式如下:

const { NativeModules } = require('react-native')
NativeModules.IntentModule.startActivityFromJS("com.hunhedemo.TwoActivity", "我是從JS傳過來的參數(shù)信息.456")}

等待原生返回?cái)?shù)據(jù)

如果我們的RN界面打開了原生界面,同時(shí)獲取到原生的返回?cái)?shù)據(jù)呢?原生Activity中回調(diào)的數(shù)據(jù)一般在onActivityResult中或者其他回調(diào)方法中的,但是如果需要返回給RN的數(shù)據(jù)是通過封裝的原生模塊方法中的Callback進(jìn)行傳輸?shù)?。為了解決這個(gè)問題,我們這邊創(chuàng)建一個(gè)阻塞的隊(duì)列來實(shí)現(xiàn),一旦有原生回調(diào)數(shù)據(jù)加入到隊(duì)列中,那么數(shù)據(jù)就會(huì)從阻塞隊(duì)列中取出來,再通過回調(diào)方法傳入到RN界面中。

下面我們看一下重載了onActivityResult方法的MainActivity類中的寫法:

package com.mixeddemo;

import android.content.Intent;

import com.facebook.react.ReactActivity;

import com.facebook.react.ReactPackage;

import com.facebook.react.shell.MainReactPackage;

import java.util.Arrays;

import java.util.List;

import java.util.concurrent.ArrayBlockingQueue;

public class MainActivity extends ReactActivity {

  //構(gòu)建一個(gè)阻塞的單一數(shù)據(jù)的隊(duì)列

  public static ArrayBlockingQueue<String> mQueue = new ArrayBlockingQueue<String>(1);

  /**

   * Returns the name of the main component registered from JavaScript.

   * This is used to schedule rendering of the component.

   */

  @Override

  protected String getMainComponentName() {

    return "hunheDemo";

  }

  /**

   * Returns whether dev mode should be enabled.

   * This enables e.g. the dev menu.

   */

  @Override

  protected boolean getUseDeveloperSupport() {

    return BuildConfig.DEBUG;

  }

  /**

   * A list of packages used by the app. If the app uses additional views

   * or modules besides the default ones, add more packages here.

   */

  @Override

  protected List<ReactPackage> getPackages() {

    return Arrays.<ReactPackage>asList(

      new MainReactPackage(),

      new IntentReactPackage()

    );

  }

  /**

   * 打開 帶返回的Activity

   * @param requestCode

   * @param resultCode

   * @param data

   */

  @Override

  public void onActivityResult(int requestCode, int resultCode, Intent data) {

    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == RESULT_OK && requestCode == 200) {

      String result = data.getStringExtra("three_result");

      if (result != null && !result.equals("")) {

        mQueue.add(result);

      } else {

        mQueue.add("無數(shù)據(jù)啦");

      }

    } else {

      mQueue.add("沒有回調(diào)...");

    }

  }

}

然后在IntentModule類中添加startActivityFromJSGetResult方法:

 * 從JS頁面跳轉(zhuǎn)到Activity界面,并且等待從Activity返回的數(shù)據(jù)給JS

 * @param className

 * @param successBack

 * @param errorBack

 */

@ReactMethod

public void startActivityFromJSGetResult(String className, int requestCode, Callback successBack, Callback errorBack){

  try {

    Activity currentActivity = getCurrentActivity();

    if(currentActivity!=null) {

      Class toActivity = Class.forName(className);

      Intent intent = new Intent(currentActivity, toActivity);

      currentActivity.startActivityForResult(intent, requestCode);

      //進(jìn)行回調(diào)數(shù)據(jù)

      successBack.invoke(MainActivity.mQueue.take());

    }

  } catch (Exception e) {

    errorBack.invoke(e.getMessage());

    e.printStackTrace();

  }

}

請(qǐng)注意上面的方法中,啟動(dòng)Activity是通過startActivityForResult()方法,這樣打開的Activity有數(shù)據(jù)返回之后,才會(huì)調(diào)用之前的onActivityResult()方法,然后我們?cè)谶@個(gè)方法中把回調(diào)的數(shù)據(jù)添加到阻塞隊(duì)列中。

接下來RN里我們可以通過如下方式進(jìn)行調(diào)用:

import { ToastAndroid } from 'react-native'

const { NativeModules } = require('react-native')

NativeModules.IntentModule.startActivityFromJSGetResult("com.mixedDemo.ThreeActivity", 200,

  (msg) => {

    ToastAndroid.show('JS界面:從Activity中傳輸過來的數(shù)據(jù)為:' + msg, ToastAndroid.SHORT)

  },

  (result) => {

    ToastAndroid.show('JS界面:錯(cuò)誤信息為:' + result, ToastAndroid.SHORT)

  }

)

Android原生界面調(diào)用RN界面

從上面的介紹,我們發(fā)現(xiàn)Android原生界面打開RN界面,還是非常簡單的,直接啟動(dòng)配置了React Native的界面Activity即可,但我們?nèi)绻朐诖蜷_RN界面同時(shí),從原生Activity中傳點(diǎn)數(shù)據(jù)過去該怎么實(shí)現(xiàn)呢?思路是 在承載RN界面的Activity中獲取當(dāng)前Intent中的數(shù)據(jù),然后通過Callback方法回調(diào)即可。


package com.mixeddemo;

import android.app.Activity;

import android.content.Intent;

import android.text.TextUtils;

import com.facebook.react.bridge.Callback;

import com.facebook.react.bridge.JSApplicationIllegalArgumentException;

import com.facebook.react.bridge.ReactApplicationContext;

import com.facebook.react.bridge.ReactContextBaseJavaModule;

import com.facebook.react.bridge.ReactMethod;

public class IntentModule  extends ReactContextBaseJavaModule {

  public IntentModule(ReactApplicationContext reactContext) {

    super(reactContext);

  }

  @Override

  public String getName() {

    return "IntentModule";

  }

  /**

   * Activtiy跳轉(zhuǎn)到JS頁面,傳輸數(shù)據(jù)

   * @param successBack

   * @param errorBack

   */

  @ReactMethod

  public void dataToJS(Callback successBack, Callback errorBack){

    try {

      Activity currentActivity = getCurrentActivity();

      String result = currentActivity.getIntent().getStringExtra("data");

      if (TextUtils.isEmpty(result)) {

        result = "沒有數(shù)據(jù)";

      }

      successBack.invoke(result);

    } catch (Exception e) {

      errorBack.invoke(e.getMessage());

    }

  }

}

接著RN端我們就可以通過在componentDidMount()時(shí)調(diào)用原生封裝的方法去獲取傳過來的參數(shù):

componentDidMount() {

  //進(jìn)行從Activity中獲取數(shù)據(jù)傳輸?shù)絁S

  NativeModules.IntentModule.dataToJS((msg) => {

    console.log(msg)

    ToastAndroid.show('JS界面:從Activity中傳輸過來的數(shù)據(jù)為:' + msg, ToastAndroid.SHORT)

  },

  (result) => {

    ToastAndroid.show('JS界面:錯(cuò)誤信息為:' + result, ToastAndroid.SHORT)

  })

}

如何運(yùn)用RN

RN作為流行的跨平臺(tái)解決方案,不僅適用前端工程師,同樣適合客戶端工程師學(xué)習(xí)。一個(gè)強(qiáng)大的混合開發(fā)App需要雙方強(qiáng)強(qiáng)聯(lián)手,而RN可以作為中間的Bridge,讓前端和客戶端的銜接更加優(yōu)雅。

UI控制權(quán)接力

針對(duì)前端工程師

主要負(fù)責(zé)跨平臺(tái)部分的頁面構(gòu)建和業(yè)務(wù)邏輯實(shí)現(xiàn),RN可以理解為用facebook提供的一套R(shí)eact框架開發(fā)移動(dòng)端應(yīng)用。所以需要扎實(shí)的ReactRedux功底,對(duì)ReactNative框架提供的組件和API有清晰的認(rèn)識(shí),并能熟練使用。

針對(duì)客戶端工程師

主要負(fù)責(zé)承載RN的視圖容器和入口,把UI的控制權(quán)轉(zhuǎn)交給RN。除此之外還要掌握Native模塊或SDK封裝成RN組件的技巧,最好能形成一套針對(duì)特定業(yè)務(wù)場(chǎng)景完整的底層組件庫,并配備清晰的說明文檔。

資源推薦

常用的開源組件

  • 微軟熱更新開源平臺(tái) react-native-code-push
  • Google三方統(tǒng)計(jì)分析平臺(tái) react-native-google-analytics-bridge
  • 極光三方推送平臺(tái) jpush-react-native
  • sqlite數(shù)據(jù)庫組件 react-native-sqlite-storage
  • 圖像處理組件 react-native-transformable-image
  • 微信SDK組件(授權(quán)、分享、支付) react-native-wechat
  • QQSDK組件 react-native-qq-sdk
  • 支付寶支付組件 react-native-alipay
  • 獲取設(shè)備信息組件 react-native-device-info
  • 國際化處理組件 react-native-il8n

疑難雜癥

  • 小米手機(jī)調(diào)試需要關(guān)閉MUI優(yōu)化引擎
  • Oppo手機(jī)、金立手機(jī) 小鍵盤在頁面路由跳回后無法再開啟。解決方案是 設(shè)置TextInput組件autoFocusedtrue

彩蛋 - RN解決方案研究思維導(dǎo)圖

image.png

@參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容