React Native 入門實踐

React Native

Learn once, write anywhere.
React Native為前端開發(fā)工程師開發(fā)Native應用提供了一種能力。保障開發(fā)效率,同時兼顧平臺的性能日丹。

React

相比于傳統(tǒng)的DOM的優(yōu)勢

  • VirtualDOM 高效diff算法(O(n^3) -> O(n)),實現(xiàn)局部刷新
  • 對外暴露Component良哲,js & css 統(tǒng)一管理

相比于傳統(tǒng)WebView H5優(yōu)勢

  • 不需要兼容各個WebView了
  • 效率會更高焊刹,它最終會調用native組件本地的渲染。

Get Started

參考官網(wǎng)或者中文網(wǎng)的Get Started,里面介紹了如何配置環(huán)境新症,以及通過簡單的命令很快就能run出一個簡單的示例工程步氏,然后在index.ios.js或者index.android.js里簡單改吧改吧就能開啟React Native開發(fā)之旅了。

NPM(Yarn)

npm 全稱是 Node Package Manager徒爹,是隨同NodeJS一起安裝的包管理工具荚醒,包括包(模塊)的下載芋类,安裝, 和發(fā)布界阁,以及管理侯繁。Yarn是Facebook提供的npm的替代工具,一般我們通過一個package.json文件做配置泡躯。當我們執(zhí)行npm init命令的時候贮竟,會自動提示我們輸入以下信息,得到package.json较剃。

{
  "name": "ls",
  "version": "1.0.0",
  "description": "this is a test",
  "main": "index.js",
  "dependencies": {},
  "devDependencies": {},
  "scripts": {
    "test": "test"
  },
  "repository": {
    "type": "git",
    "url": "\"\""
  },
  "keywords": [
    "123"
  ],
  "author": "sl",
  "license": "UNLICENSED"
}

對應我們在React Native中的配置,舉例

{
  "name": "RN",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start"
  },
  "dependencies": {
    "react": "15.4.2",
    "react-native": "0.44.0",
    "react-native-linear-gradient": "^2.0.0"
  }
}

當我們執(zhí)行npm start命令的時候坝锰,其實執(zhí)行的就是node node_modules/react-native/local-cli/cli.js start,其中cliCommand line interface的縮寫,在local-cli里相當豐富的命令重付。

構建Android應用

1顷级、新建一個普通的Adroid工程,以ReactApplication為例确垫。
2弓颈、在工程目錄里配置package.json,如上配置即可删掀,執(zhí)行npm install翔冀,安裝npm組件和下載react native 包。
3披泪、在Project的build.gradle配置,上步會把react-native的相關代碼打成arr纤子,jar,pom等格式放到/node_modules/react-native/android目錄下款票,新版React Native只通過npm發(fā)布控硼,所以,需要在jcenter外配置本地倉庫艾少,獲取依賴包卡乾。

repositories {
     jcenter()
     maven {
         // All of React Native (JS, Android binaries) is installed from npm
         url "$projectDir/../node_modules/react-native/android"
     }
}

4、在主module里配置

compile "com.facebook.react:react-native:0.44.0"  

5缚够、 工程目錄下幔妨,新建index.android.js

import React, { Component } from 'react';
import {
   AppRegistry,
   StyleSheet,
   Text,
   View,
   Image
} from 'react-native';

class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.note}>Welcome to React Native</Text>
        <Image style={styles.sss} source={{uri:'https://facebook.github.io/react/img/logo_og.png'}}></Image>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  note: {
    fontSize: 20
    },
  sss: {
    width : 200,
    height : 100
  },
});

AppRegistry.registerComponent('react-native-module', () => App);

在React Native中,JS端對外暴露的是一個個Component(組件)谍椅,AppRegistry模塊是React Native應用運行JS的入口误堡,所有的根組件都可以registerComponent來注冊,這里是App,react-native-module是對外(Native)暴露的組件的名稱雏吭。

6锁施、自定義ReactInstanceManager可以理解成React的超級管家,我們可以進行各種配置。 然后內部會做一系列的初始化工作:包括index.android.bundle的加載沾谜,js和native module的注冊等等膊毁,在調試模式下會生成DevSupportManager,所有調試相關的如:調試彈窗(RedBox)基跑,與本地server端的通信婚温,bundle的更新與加載都由它統(tǒng)一管理。

@Override
protected ReactInstanceManager createReactInstanceManager() {
    ReactInstanceManager reactInstanceManager = ReactInstanceManager.builder().setApplication(getApplication())
            .setBundleAssetName("index.android.bundle")
            .setJSMainModuleName("index.android")
            .setJSBundleFile("XXX")
            .addPackage(new MainReactPackage())
            .addPackage(new ReactCellPackage())
            .setUseDeveloperSupport(true)
            .setInitialLifecycleState(LifecycleState.RESUMED)
            .setUIImplementationProvider(new MyUIImplementationProvider())
            .build();
    return reactInstanceManager;

簡單來說index.android.bundle就是對React Native里JS端代碼的打包媳否。
1) 一般release打包會放到assets目錄下栅螟,然后它的資源文件也會放到assets目錄下。通過設置BundleAssetName就可以利用AssetManager讀取bundle篱竭,看到XX工程里的bundle接近1MB力图。這也是我們后面可以優(yōu)化的點。

  • bundle包的拆分掺逼,按需加載吃媒,優(yōu)化頁面的加載時間,攜程專門有篇文章是講bundle的拆分的吕喘。
  • 提前加載bundle
  • 動態(tài)下發(fā)

2)通過設置JSBundleFile赘那,可以讀取本地的bundle,RN的熱更新就可以通過這個下發(fā)氯质,然后Native重新加載一下即可募舟。
3)ReactCellPackage就是我們自定義的Module和組件。

7闻察、加載React布局

1)繼承ReactActiviy

public class MyReactActivity extends ReactActivity {

    @Override
    protected String getMainComponentName() {
        return "react-native-module";
    }
}

ReactActivity是FB為我們提供的基礎類拱礁,內部會在根布局里設置成ReactRootView(實際上就是一個FrameLayout),并做一些RN的初始化工作辕漂。JS端的Component最終會被加載成為ReactRootView的子布局呢灶。

  1. 自定義ReactRootView加載
    ReactRootView可以設置成根布局或者頁面的一個子View。startReactApplication方法首次就會異步做一些初始化工作钮热,ReactActivity里也是調用這個方法填抬。
public abstract class BaseActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler{

    private ReactRootView mReactRootView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mReactRootView = new ReactRootView(this);
        mReactRootView.startReactApplication(SSApplication.inst().getReactNativeHost().getReactInstanceManager(), getRNComponentName(), null);
        setContentView(mReactRootView);
    }

    protected abstract String getRNComponentName();


    @Override
    protected void onResume() {
        super.onResume();
        SSApplication.inst().getReactNativeHost().getReactInstanceManager().onHostResume(this, this);
    }

    @Override
    protected void onPause() {
        SSApplication.inst().getReactNativeHost().getReactInstanceManager().onHostPause();
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        SSApplication.inst().getReactNativeHost().getReactInstanceManager().onHostDestroy(this);
        SSApplication.inst().getReactNativeHost().getReactInstanceManager().detachRootView(mReactRootView);
        super.onDestroy();
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        SSApplication.inst().getReactNativeHost().getReactInstanceManager().onBackPressed();
    }

    @Override
    public void invokeDefaultOnBackPressed() {
        super.onBackPressed();
    }
}

8、自定義ReactPackage

public class ReactCellPackage implements ReactPackage {
    //需要注冊的自定義的NativeModule列表
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
    
    //JS Module 接口隧期,需要在JS中做對等實現(xiàn),并打包的bundle中赘娄。
    @Override
    public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }

    //自定義View的ViewManager列表
    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Arrays.<ViewManager>asList(new ReactCellViewManager()
                , new MyReactImageViewManager()
                , new MyReactTextViewManager()
                , new RealRecyclerItemViewManager()
                , new RealRecyclerViewManager());
    }
}

至此我們就可以開始玩React Native了仆潮。

React Native 通信

config

這張圖比較省略,Java和JS之間還有一個Bridge(C/C++層)遣臼,簡單理解來說性置,Java和JS各自維護了一份對等的映射config,比如Java傳入A就可以調用js config表里揍堰,A對應的模塊鹏浅。反之亦然嗅义。這個配置是在初始化React Native 上下文的時候生成的。

初始化流程

1)生成ReactApplicationContext實例.
2)初始化3個線程隐砸,UI線程之碗,NativeModule線程,JSThread,以及 jsExecutor季希。
3)處理各類Package褪那,解析并在Java側維護了各個JavaScriptModule和NativeModule表

  1. initBridge,將NativeModule表下發(fā)給C/C++層
  2. runJSBundle 加載JSBundle

Java -> JS

可以這樣理解式塌,JavaScriptModule就是JS暴露給Java的調用接口博敬。當Java調用JS方法的時候:

  1. 先找到對應的JS Module,并填充相應的參數(shù)峰尝。
    2)Proxy通過jni調用生成JS Module的實例
    3)jni調用 實例偏窝,方法,參數(shù)作為產(chǎn)生 下發(fā)的到Bridge層
    4)Bridge層 執(zhí)行對應的 JS 方法

JS -> JAVA

  1. JS Module的方法調用時武学,會把module囚枪,methond,params放入到MessageQueue里
    2)當message的消息間隔大于5ms時劳淆,觸發(fā)Queue的情況操作链沼。
    3)Bridge層 通過JNI找到 對應的NativeModule
    4)NativeModule執(zhí)行相應的method.

View(UIManagerModule)

JS端觸發(fā)view刷新等都會走到UIManagerModule中,這里會將這些“消息”封裝成一個一個Operation沛鸵,由UIViewOperationQueue統(tǒng)一調度括勺。這個就回到在UI Thread里了,后續(xù)處理的就是Native本地的渲染了曲掰。

痛點

性能

React Native相比于Native性能還是偏弱疾捍。包括渲染效率,加載延時栏妖,以及View的復用等等乱豆,大廠們紛紛都在對React Native 做特定場景的優(yōu)化。比如是listview吊趾。目前我們也在做listview的優(yōu)化宛裕。

穩(wěn)定性

1、crash
2论泛、國產(chǎn)手機的兼容性

AnyWay

React Native 確實為Native 開發(fā)提供一種思路揩尸,也許后面性能不再是瓶頸,那么未來是光明的屁奏。眼下岩榆,我們還是要多踩坑。

參考

React 源碼剖析系列 - 不可思議的 react diff
React Native Android 從學車到補胎和成功發(fā)車經(jīng)歷
React Native Android 源碼框架淺析
其實沒那么復雜!探究react-native通信機制

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末勇边,一起剝皮案震驚了整個濱河市犹撒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌粒褒,老刑警劉巖识颊,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異怀浆,居然都是意外死亡谊囚,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進店門执赡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來镰踏,“玉大人,你說我怎么就攤上這事沙合〉煳保” “怎么了?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵首懈,是天一觀的道長绊率。 經(jīng)常有香客問我,道長究履,這世上最難降的妖魔是什么滤否? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮最仑,結果婚禮上藐俺,老公的妹妹穿的比我還像新娘。我一直安慰自己泥彤,他們只是感情好欲芹,可當我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吟吝,像睡著了一般菱父。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上剑逃,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天浙宜,我揣著相機與錄音,去河邊找鬼炕贵。 笑死梆奈,一個胖子當著我的面吹牛,可吹牛的內容都是我干的称开。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鳖轰!你這毒婦竟也來了清酥?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤蕴侣,失蹤者是張志新(化名)和其女友劉穎焰轻,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體昆雀,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡辱志,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了狞膘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片揩懒。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖挽封,靈堂內的尸體忽然破棺而出已球,到底是詐尸還是另有隱情,我是刑警寧澤辅愿,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布智亮,位于F島的核電站,受9級特大地震影響点待,放射性物質發(fā)生泄漏阔蛉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一癞埠、第九天 我趴在偏房一處隱蔽的房頂上張望状原。 院中可真熱鬧,春花似錦燕差、人聲如沸遭笋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓦呼。三九已至,卻和暖如春测暗,著一層夾襖步出監(jiān)牢的瞬間央串,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工碗啄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留质和,地道東北人。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓稚字,卻偏偏與公主長得像饲宿,于是被迫代替她去往敵國和親厦酬。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,860評論 2 361

推薦閱讀更多精彩內容