react-native的簡單使用

一、網上搜集RNandroid的問題

  • 版本更新太快,到現在還沒有到達版本1.0,技術並沒有那麼穩定
  • 使用了RN,就意味着和Google的android開發控件走上了兩條不一樣的路線。

二、目前存在的問題

1、react-native庫會直接帶動整個編譯環境提升到最高,否則編譯不了。

帶來的影響是新的buildtool似乎把很多以前廢棄的函數直接undfine了,即可能找不到該函數了。以前的很多業務代碼報錯,這些都是成本。

2、rn方法數超標的發生

鑑於我們國內app各種sdk的使用,有些比如語音等第三方庫的必不可少很容易導致dexIndexOverFlow,雖然這些是有解決方案,但是5.0以下可能導致不適發生。我懷疑RN是方便那些熟悉Web開發的人只需很少的學習成本就可以轉入移動應用開發。並未聽聞Google對RN的評價,考慮未來andromeda的發佈,某些性能會帶來變革。對於廣大JavaScript出身,想做移動開發而又不想投入人力物力學原生的開發者來說,是個好時機。對於JavaScript出身的開發者,確實是一個拓寬自己職業前進道路的一門新技術,三端(Web,iOS,Android)盡收眼底,單想想就會令人興奮不已。但是對於原本就是原生出身的開發者,和廣大業務型驅動的項目,以及踩坑能力和扛風險能力都比較一般的非一線公司來講,RN可以關注,可以業餘時間來嘗試和開拓眼界,其餘的,收斂一下激動的小心臟,還是該幹啥幹啥,保持淡定,真正到了RN初步成熟的時候,再進軍也不遲(知乎)

3、 任何以後不知道咋樣。

直至當前,任何Java、Objective-C、Swift等官方技術之外,號稱新潮的原生解決方案,都有無數個坑,填了一個又來一個,沒完沒了的。

4、 個人意見

不要作爲主語言,可以另開分支單獨研發測試。我們項目開始使用RN,填坑無數,RN官方更新太頻繁。和原生相比還是有差距。Android官方發佈很多版本,RN後續改動也挺大的。需不需要使用RN我覺得主要是考慮到頁面變化比較頻繁,而又不想通過發佈新版本(其實很多第三方渠道審覈的時候越來越苛刻了)實現版本的更新才需要使用RN,其實也是可以考慮使用webview,webview相信在Google不停的優化下現在比以前好很多。RN其實有點想從開發中間攔截一次一統江湖的願景,讓大家只需要學習使用RN即可,但我想Google不會坐視不理的,一定會維護自己的地位。
React Native我們也要項目在用。但RN思想和語法的學習曲線有些陡,很多有Web開發經驗的人都不一定喜歡。另外很多安卓開發者並沒有學習過js,學習React Native成本會比較高。學習React Native最終還是要對原生開發有一定的瞭解。最後,在一些功能複雜的App,React Native用戶體驗比不上原生 。

三、運行官網demo

在運行官方第一個demo:AwesomeProject的時候需要修改幾個地方:

首先說第一個問題:
需要把AwesomeProject\android\app\build.gradle下的buildToolsVersion修改成已下載的構建版本。當然,如果你已經下載了官方demo默認構建版本的23.0.1,這一步可以忽略不用修改了。
否則可能會出現如下問題:
這裏寫圖片描述
gradle-wrapper.properties修改成你安裝好的就行了。默認的是2.4。
修改好後,可能會出現如下情況
這裏寫圖片描述
由於網絡的原因,下載有可能會很慢,可以使用離線下載的方式。本人使用了astrill,大概兩分鐘不到就下載好了。
然後運行結果虛擬機顯示爲:
這裏寫圖片描述

四、android源生app集成react Native的步驟

1、先按照官方的步驟集成;

1) 在項目的根目錄:npm init

生成的package.json需要在scripts處添加:
“start”: “node node_modules/react-native/local-cli/cli.js start”

2) 在項目的根目錄:npm install –save react react-native

3) Curl –o .flowconfig

https://raw.githubuser content.com/facebook/react-native/master/.flowconfig

4) 在根目錄創建index.android.js文件

5) 在android studio項目的build.gradle添加:

dependencies {
    ...
    compile "com.facebook.react:react-native:+" // From node_modules.
}

6) 在android studio工程的build.gradle添加:

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

注意去掉.. ,最後的:url “$rootDir/node_modules/react-native/android”

7) 在android studio項目裏添加:

<uses-permission android:name="android.permission.INTERNET" />
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />

8) 然後實現官方的MyReactActivity代碼:

package com.rndemo.reactnativedemo;

import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;

import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.react.shell.MainReactPackage;

public class MainActivity extends Activity implements DefaultHardwareBackBtnHandler {
    private ReactRootView mReactRootView;
    private ReactInstanceManager mReactInstanceManager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mReactRootView = new ReactRootView(this);
        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setBundleAssetName("index.android.bundle")
                .setJSMainModuleName("index.android")
                .addPackage(new MainReactPackage())
                .setUseDeveloperSupport(true)  //BuildConfig.DEBUG
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();
        mReactRootView.startReactApplication(mReactInstanceManager, "HelloWorld", null);
        setContentView(mReactRootView);
    }

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

    @Override
    protected void onPause() {
        super.onPause();

        if (mReactInstanceManager != null) {
            mReactInstanceManager.onHostPause(this);
        }
    }
    @Override
    protected void onResume() {
        super.onResume();

        if (mReactInstanceManager != null) {
            mReactInstanceManager.onHostResume(this,this);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mReactInstanceManager != null) {
            mReactInstanceManager.onHostDestroy(this);
        }
    }

    @Override
    public void onBackPressed() {
        if (mReactInstanceManager != null) {
            mReactInstanceManager.onBackPressed();
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
            mReactInstanceManager.showDevOptionsDialog();
            return true;
        }
        return super.onKeyUp(keyCode, event);
    }
}

其中配置文件:

android:theme="@style/Theme.AppCompat.Light.NoActionBar">

9)最後:

react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output android/com/your-company-name/app-package-name/src/main/assets/index.android.bundle --assets-dest android/com/your-company-name/app-package-name/src/main/res/

其中 your-company-name在demo中是:

react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output app/src/main/assets/index.android.bundle --assets-dest
dest app/src/main/res/

當然別忘記在main的文件夾下創建assets文件夾,否則會報錯:找不到文件夾。

2、遇到的坑,需要修改的地方:

1) 紅屏異常:Could not get BatchedBridge, make sure your bundle is packaged correctly

網上有解決方案一或者方案二
如果執行按照上面的命令,在assetes下生成bundle文件,就不需要這樣修改了。

2) npm start 和 react-native start(等同react-native run-android)命令

這兩個命令都可以啓動開發服務器,親測,但不知道有什麼區別。
react-native run-android有時候packager不能自動運行,此時可以手動啓動:
react-native start.

五、JSX語法

在 JavaScript 代碼裏寫着 XML 格式的代碼稱爲 JSX,HTML 語言直接寫在 JavaScript 語言之中,不加任何引號,這就是 JSX 的語法,它允許 HTML 與 JavaScript 的混寫。React 官方推薦使用JSX, 當然你想直接使用純Javascript代碼寫也是可以的,只是使用JSX,組件的結構和組件之間的關係看上去更加清晰。JSX 類似於 HTML,但不完全一樣
http://www.css88.com/react/docs/jsx-in-depth.html

js 代碼:
1.  //使用JSX
2.  React.render(
3.      <div>
4.          <div>
5.              <div>content</div>
6.          </div>
7.      </div>,
8.      document.getElementById('example')
9.  );
10.  
11. //不使用JSX
12. React.render(
13.     React.createElement('div', null,
14.         React.createElement('div', null,
15.             React.createElement('div', null, 'content')
16.         )
17.     ),
18.     document.getElementById('example')19.   );

render 是 React 的最基本方法,用於將模板轉爲 HTML 語言

六、React

1、react定義

React 允許將代碼封裝成組件(component),然後像插入普通 HTML 標籤一樣,在網頁中插入這個組件,所有組件類都必須有自己的 render 方法。注意,組件類的第一個字母必須大寫,否則會報錯。示例:

var HelloMessage = React.createClass({
  render: function() {
    return <h1>
      Hello {this.props.name}
    </h1><p>
      some text
    </p>;
  }
});

上面代碼會報錯,因爲HelloMessage組件包含了兩個頂層標籤:h1和p。組件的用法與原生的 HTML 標籤完全一致,可以任意加入屬性,比如 ,就是 HelloMessage 組件加入一個 name 屬性,值爲 John。組件的屬性可以在組件類的 this.props 對象上獲取,比如 name 屬性就可以通過 this.props.name 讀取。
生命週期
當首次使用一個組件類時,會看到如下這些方法依次被調用:

getDefaultProps
getInitialState
componentWillMount
render
ComponentDidMount

對於該組件類所有後續應用,將會看到下面的方法依次被調用:

getInitialState
componentWillMount
render
componentDidMout

(存在期)隨着應用狀態的改變,以及組件逐漸受到影響,下面的方法依次被調用:

componentwillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate

對於一個組件render是唯一一個必需的方法,並且有特定的規則:
只能通過this.props和this.state訪問數據;
可以返回null,false或者任何react組件;
只能出現一個定級組件(不能返回一組);
必需純淨,有位置不能改變組件狀態或者修改DOM輸出。

2、props的解釋

this.props 對象的屬性與組件的屬性一一對應,但是有一個例外,就是 this.props.children 屬性。它表示組件的所有子節點。這裏需要注意, this.props.children 的值有三種可能:如果當前組件沒有子節點,它就是 undefined ;如果有一個子節點,數據類型是 object ;如果有多個子節點,數據類型就是 array 。所以,處理 this.props.children 的時候要小心。
React 提供一個工具方法 React.Children 來處理 this.props.children 。我們可以用 React.Children.map 來遍歷子節點,而不用擔心 this.props.children 的數據類型是 undefined 還是 object。
組件的屬性可以接受任意值,字符串、對象、函數等等都可以。有時,我們需要一種機制,驗證別人使用組件時,提供的參數是否符合要求。組件類的PropTypes屬性,就是用來驗證組件實例的屬性是否符合要求,此外,getDefaultProps 方法可以用來設置組件屬性的默認值。
原生值傳遞到react native示例:

Bundle bundle = new Bundle();
bundle.putString("key01", "value01");
mReactRootView.startReactApplication(mReactInstanceManager, "MyToastAndroid", bundle);

在react native出取值的方法:

this.props.key01;

3、虛擬DOM

組件並不是真實的 DOM 節點,而是存在於內存之中的一種數據結構,叫做虛擬 DOM (virtual DOM)。只有當它插入文檔以後,纔會變成真實的 DOM 。根據 React 的設計,所有的 DOM 變動,都先在虛擬 DOM 上發生,然後再將實際發生變動的部分,反映在真實 DOM上,這種算法叫做 DOM diff ,它可以極大提高網頁的性能表現。
但是,有時需要從組件獲取真實 DOM 的節點,這時就要用到 ref 屬性

4、this.state

組件免不了要與用戶互動,React 的一大創新,就是將組件看成是一個狀態機,一開始有一個初始狀態,然後用戶互動,導致狀態變化,從而觸發重新渲染 UI。由於 this.props 和 this.state 都用於描述組件的特性,可能會產生混淆。一個簡單的區分方法是,this.props 表示那些一旦定義,就不再改變的特性,而 this.state 是會隨着用戶互動而產生變化的特性。
對於複雜的單頁面應用,狀態(state)管理很重要。
State可能包括:服務端的響應數據、本地對響應數據的緩存、本地創建的數據以及一些UI的狀態信息(比如,路由、選中的tab、是否顯示下來列表、頁碼控制等等)。如果state變化不可預測,就會難以調試(state不易重現,很難復現一些bug)和不易擴展(比如優化更新渲染、服務端渲染、路由切換時獲取數據等)。
Redux就是用來確保state變化的可預測性,主要的約束有:1)state以單一對象存儲在store對象中;2)state只讀;3)使用純函數reducer執行state更新。
store 是一個單一對象:1)管理應用的state;2)通過store.getState()可以獲取state;3)通過store.dispatch(action)來觸發state更新;4)通過store.subscribe(listener)來註冊state來註冊state變化監聽器;5)通過createStore(reducer,[initialState])創建

5、rn和原生通信

1)Android向JS傳遞事件

2)AndroidJS被動向Android詢問事件消息

示例:
定義:

@ReactMethod
public void callBackTime(String name, Callback callback) {
    callback.invoke("your name");
}

調用:

MyToastAndroid.callBackTime("Allure", (msg) => {
    console.log(msg);
    ToastAndroid.show(msg, ToastAndroid.SHORT);
});

3)promise

React Native的跨語言訪問是異步進行的,所以想要給JavaScript返回一個值的唯一辦法是使用回調函數或者發送事件。
Promise就是其中一個解決方法。
一個Promise對象可以理解爲一次將要執行的操作,使用了Promise對象之後可以用一種鏈式調用的方式來組織代碼,讓代碼更加直觀。類似android裏的RxAndriod,都是爲了使代碼美觀,鏈式請求不需要層層嵌套。Promise 對象有三種狀態:
1.Fulfilled 可以理解爲成功的狀態
2.Rejected 可以理解爲失敗的狀態
3.Pending 既不是 Fulfilld 也不是 Rejected 的狀態,可以理解爲 Promise 對象實例創建時候的初始狀態
示例:
定義:

@ReactMethod
public void getPromiseInfo(String name, Promise promise) {
    WritableMap writableMap=new WritableNativeMap();
    writableMap.putString("age","20");
    writableMap.putString("time","2016");
    promise.resolve(writableMap);
}
接收:
MyToastAndroid.getPromiseInfo("Allure").then(msg=> {
            console.log("年齡:" + msg.age + "/n" + "時間:" + msg.time);
            ToastAndroid.show("Promise收到消息:" + "\n" + "年齡:" + msg.age + "時間:" + msg.time, ToastAndroid.SHORT)

            this.setState({
                age: msg.age,
                time: msg.time,
            })
        }).catch(error=> {
            console.log(error);
        });

6、自定義原生模塊

1)寫源生模塊類,類似JS裏面的Component功能。需要繼承ReactContextBaseJavaModule。例如:MyToastModule;

2)寫包類,作用是把模塊類送到React Native中,實現 ReactPackage。例如:

ToastReactPackage
@Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new MyToastModule(reactContext));
        return modules;
    }

3)需要把這個包類放到需要引入的Activity裏面,因爲模塊類其實可以類比一個控件,最終需要顯示出來,相當於佈局吧。例如:

mReactRootView = new ReactRootView(this);
        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setBundleAssetName("index.android.bundle")
                .setJSMainModuleName("index.android")
                .addPackage(new MainReactPackage())
                .addPackage(new AnExampleReactPackage())
                .setUseDeveloperSupport(true)  //BuildConfig.DEBUG
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();
        Bundle bundle = new Bundle();
        bundle.putString("key", "來自原生代碼");
        mReactRootView.startReactApplication(mReactInstanceManager, "MyToastAndroid", bundle);
        setContentView(mReactRootView);

4)以上三步驟完成後就可以使用了。例如:

    var MyToastAndroid = NativeModules.MyToastAndroid;
    MyToastAndroid.show("sssss", MyToastAndroid.SHORT);

7、路由(route)

使用導航器經常會碰到“路由(route)”的概念。“路由”車廂自現實生活中的路牌,在RN中專指包含了場景信息的對象。renderScene方法是完全根據路由提供的信息來渲染場景的。

七、ES6語法

1、函數參數設置默認值

function log(x, y = 'World') {
  console.log(x, y);
}

2、typeof 運算符

typeof 運算符把類型信息當作字符串返回。typeof 返回值有六種可能: “number,” “string,” “boolean,” “object,” “function,” 和 “undefined.”
typeof 語法中的圓括號是可選項

最後存在的問題

1、在運行別人的代碼的時候執行:compile “com.facebook.react:react-native:+”

加載的包始終是react-native-0.20.1.
需要在package.json—> dependencies:react-native裏面修改

2、執行:

react-native start
react-natvie run-android
出現如下問題:Android project not found. Maybe run react-native android first?
原因是在原生app裏面集成js的命令應該是:
npm start
然後直接運行AS。
或者使用打包命令,生成的bundle到assets目錄下,再運行Android studio也行。

3、出現:cant find variable:Component

import React { Component } from ‘react’; 修改爲import React, { Component } from ‘react’;

4、出現的問題Could not connect to development server.

解決辦法:紅屏引進有提示了。最可能的問題是:adb reverse tcp:8081 tcp:8081

5、Your app does not have the latest code changes because it was restarted manually.Please run from IDE instead.

6、Application XXX has not been registered.This is either due to a require() error during initialization or failure to call AppRegistry.registerComponent.

解決辦法:定義的模塊名稱保存一致:
(1)AppRegistry.registerComponent
(‘MyToastAndroid’, ()=> HelloWorld);
2)mReactRootView.startReactApplication(mReactInstanceManager, “MyToastAndroid”, bundle);
setContentView(mReactRootView);
3)@Override
public String getName() {
return “MyToastAndroid”;
}
然後開啓服務:react-native start

7、自定義Style的時候就會報錯。

解決的辦法:首先導入包:StyleSheet
其次自定義的樣式需要放在自定義組件Component體外。

8、https://github.com/ldn0x7dc/react-native-media-kit/issues/27

沒有解決的問題:

1、 exports的使用

exports.displayName = ‘ButtonExample’;
exports.framework = ‘React’;
exports.title = ‘’;
http://facebook.github.io/react-native/docs/button.html

2、

發佈了82 篇原創文章 · 獲贊 33 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章