react-native开发经验
# **RN開(kāi)發(fā)經(jīng)驗(yàn)**
## 一、環(huán)境配置
關(guān)于環(huán)境配置,前輩已有完整的總結(jié):http://tvrn.devops.letv.com/docs/Environment.html
**IDE準(zhǔn)備:** [AndroidStudio](https://developer.android.com/studio/index.html),[VisualStudioCode](https://code.visualstudio.com/)
如何打造現(xiàn)代化RN開(kāi)發(fā)環(huán)境:http://tvrn.devops.letv.com/docs/tools/vscode.html
## 二、TVRN Demo 運(yùn)行
TVRN是為了方便TV上的開(kāi)發(fā)者使用的組件庫(kù)。詳見(jiàn):http://tvrn.devops.letv.com/ (每個(gè)人都可以修改更新文檔,入庫(kù)后第二天早上10點(diǎn)更新)
git地址如下:
`git clone ssh://username@athena.devops.letv.com:29418/LETVRD/tvrn-libs`
playground項(xiàng)目是一個(gè)標(biāo)準(zhǔn)的RN-app工程,只不過(guò)是嵌在tvrn-libs里面,同時(shí)直接引用了tvrn-libs。 用途:
??? 1、開(kāi)發(fā)時(shí)作為組件的host,能直接看到組件的運(yùn)行效果
??? 2、作為tvrn組件庫(kù)的演示examples-app
###### **運(yùn)行playground**
??? 1、在根目錄執(zhí)行 npm install(package.json中列出了項(xiàng)目所有依賴);
??? 2、修改vi playground/android/gradle/wrapper/gradle-wrapper.properties,
????? 注釋掉從網(wǎng)絡(luò)下載,使用本地gradle-2.10-all.zip;
??? 3、拷貝一個(gè)gradle包到playground/android/gradle/wrapper/目錄下:
????? cp ../*****/gradle-2.10-all.zip playground/android/gradle/wrapper/;
??? 4、根目錄下執(zhí)行tvrn run-android --root playground(--root指定目錄);
??? 注:此Demo目前暫只能在EUI6.0上跑通。
## 三、使用組件
```JavaScript
import React, { Component } from 'react';
import {
??? AppRegistry,
??? Navigator,
} from 'react-native';
import { FocusableView } from 'tvrn-libs';
import AccountCenter from './src/ui/AccountCenter';
```
如果export default class AccountCenter,則AccountCenter不需要大括號(hào),如import AccountCenter from './AccountCenter';
如果export class AccountCenter則不需要大括號(hào),如import {AccountCenter} from './AccountCenter';
可以export出多個(gè)組件。
## 四、使用導(dǎo)航器跳轉(zhuǎn)頁(yè)面
Navigator使用純JavaScript實(shí)現(xiàn)了一個(gè)導(dǎo)航棧,因此可以跨平臺(tái)工作。
Navigator可以在renderScene方法中根據(jù)當(dāng)前路由渲染不同的組件。默認(rèn)情況下新的場(chǎng)景會(huì)從屏幕右側(cè)滑進(jìn)來(lái),但你也可以通過(guò)configureScene方法來(lái)管理這一行為。
```JavaScript
<Navigator
??? initialRoute={{ component: AccountLogin }} //這個(gè)指定了默認(rèn)的頁(yè)面
??? configureScene={this.configureScene} //跳轉(zhuǎn)動(dòng)畫(huà)
??? renderScene={this.renderScene} /> //加載頁(yè)面
```
跳轉(zhuǎn):
```JavaScript
_gotoRegister() {
? this.props.navigator.push({
??? title: 'RegisterView1',
??? component: RegisterView1,
??? params: {//跳轉(zhuǎn)攜帶參數(shù)
????? name: 'test'
??? },
??? // type: 'Bottom'
? });
}
```
## 五、component生命周期
**RN中的component跟Android中的activity,fragment等一樣,存在生命周期。**

如圖,可以把組件生命周期大致分為三個(gè)階段:
* 第一階段:是組件第一次繪制階段,如圖中的上面虛線框內(nèi),在這里完成了組件的加載和初始化;
* 第二階段:是組件在運(yùn)行和交互階段,如圖中左下角虛線框,這個(gè)階段組件可以處理用戶交互,或者接收事件更新界面;
* 第三階段:是組件卸載消亡的階段,如圖中右下角的虛線框中,這里做一些組件的清理工作。
**下面來(lái)詳細(xì)介紹生命周期中的各回調(diào)函數(shù)。**
#### **getDefaultProps**
在組件創(chuàng)建之前,會(huì)先調(diào)用 getDefaultProps(),這是全局調(diào)用一次,嚴(yán)格地來(lái)說(shuō),這不是組件的生命周期的一部分。
#### **getInitialState**
在組件被創(chuàng)建并加載候,首先調(diào)用 getInitialState(),來(lái)初始化組件的狀態(tài)。
#### **componentWillMount**
這個(gè)函數(shù)調(diào)用時(shí)機(jī)是在組件創(chuàng)建,并初始化了狀態(tài)之后,在第一次繪制 render() 之前??梢栽谶@里做一些業(yè)務(wù)初始化操作,也可以設(shè)置組件狀態(tài)。這個(gè)函數(shù)在整個(gè)生命周期中只被調(diào)用一次。
#### **render**
render繪制組件到界面上
#### **componentDidMount**
在組件第一次繪制之后,會(huì)調(diào)用 componentDidMount(),通知組件已經(jīng)加載完成。從這個(gè)函數(shù)開(kāi)始,就可以和 JS 其他框架交互了,例如設(shè)置計(jì)時(shí) setTimeout 或者 setInterval,或者發(fā)起網(wǎng)絡(luò)請(qǐng)求。這個(gè)函數(shù)也是只被調(diào)用一次。
#### **componentWillReceiveProps**
如果組件收到新的屬性(props),就會(huì)調(diào)用 componentWillReceiveProps()。在這個(gè)回調(diào)函數(shù)里面,你可以根據(jù)屬性的變化,通過(guò)調(diào)用 this.setState() 來(lái)更新你的組件狀態(tài),這里調(diào)用更新?tīng)顟B(tài)是安全的,并不會(huì)觸發(fā)額外的 render() 調(diào)用。
#### **shouldComponentUpdate**
當(dāng)組件接收到新的屬性和狀態(tài)改變的話,都會(huì)觸發(fā)調(diào)用 shouldComponentUpdate(...)。這個(gè)函數(shù)的返回值決定是否需要更新組件,如果 true 表示需要更新,繼續(xù)走后面的更新流程。否者,則不更新,直接進(jìn)入等待狀態(tài)。
默認(rèn)情況下,這個(gè)函數(shù)永遠(yuǎn)返回 true 用來(lái)保證數(shù)據(jù)變化的時(shí)候 UI 能夠同步更新。你可以自己重載這個(gè)函數(shù),通過(guò)檢查變化前后屬性和狀態(tài),來(lái)決定 UI 是否需要更新,能有效提高應(yīng)用性能。
#### **componentWillUpdate**
如果組件狀態(tài)或者屬性改變,并且上面的 shouldComponentUpdate(...) 返回為 true,就會(huì)開(kāi)始準(zhǔn)更新組件,并調(diào)用 componentWillUpdate()。
需要特別注意的是,在這個(gè)函數(shù)里面,你就不能使用 this.setState 來(lái)修改狀態(tài)。
緊接著這個(gè)函數(shù),就會(huì)調(diào)用 render() 來(lái)更新界面了。
#### **componentDidUpdate**
調(diào)用了 render() 更新完成界面之后,會(huì)調(diào)用 componentDidUpdate() 來(lái)得到通知,
#### **componentWillUnmount**
當(dāng)組件要被從界面上移除的時(shí)候,就會(huì)調(diào)用 componentWillUnmount()。在這個(gè)函數(shù)中,可以做一些組件相關(guān)的清理工作,例如取消計(jì)時(shí)器、網(wǎng)絡(luò)請(qǐng)求等。
|生命周期|調(diào)用次數(shù)|能否使用setState()|
|--|--|--|
|getDefaultProps|1|否|
|getInitialState|1|否|
|componentWillMount|1|是|
|render|>=1|否|
|componentDidMount|1|是|
|componentWillReceiveProps|>=0|是|
|shouldComponentUpdate|>=0|否|
|componentWillUpdate|>=0|否|
|componentDidUpdate|>=0|否|
|componentWillUnmount|1|否|
## 六、back鍵處理
**安卓back鍵的處理主要就是一個(gè)事件監(jiān)聽(tīng):**
```JavaScript
BackAndroid.addEventListener('hardwareBackPress', this.onBackPressed);
BackAndroid.removeEventListener('hardwareBackPress', this.onBackPressed);
```
**根據(jù)當(dāng)前界面決定作何動(dòng)作**
有時(shí)候我們有這樣的需求:當(dāng)用戶處于某些界面下時(shí),back鍵要做特殊的動(dòng)作,如:提示用戶是否要保存數(shù)據(jù),或者解鎖界面禁止back鍵返回等等。此時(shí),最佳實(shí)踐是在route或route中對(duì)應(yīng)的Component上保存關(guān)于如何處理back鍵的信息:
```JavaScript
onBackAndroid = () => {
?? const nav = this.navigator;
?? const routers = nav.getCurrentRoutes();
?? if (routers.length > 1) {
???? const top = routers[routers.length - 1];
???? if (top.ignoreBack || top.component.ignoreBack){
?????? // 路由或組件上決定這個(gè)界面忽略back鍵
?????? return true;
???? }
???? const handleBack = top.handleBack || top.component.handleBack;
???? if (handleBack) {
?????? // 路由或組件上決定這個(gè)界面自行處理back鍵
?????? return handleBack();
???? }
???? // 默認(rèn)行為: 退出當(dāng)前界面。
???? nav.pop();
???? return true;
?? }
?? return false;
?};
```
## 七、RN中JS如何調(diào)用Java代碼
1. 首先新建一個(gè)類繼承自ReactContextBaseJavaModule這個(gè)抽象類
```java
public class LetvAccountManager extends ReactContextBaseJavaModule {
```
2. 重寫(xiě)getName方法,命名一下擴(kuò)展。以后可以在js里面按照名字找到這個(gè)擴(kuò)展。
```java
@Override
public String getName() {
??? return "LetvAccountManager";
}
```
3. 實(shí)現(xiàn)暴露給js的接口
```java
@ReactMethod
public void removeLocalAccount(String userName, Promise promise) {
??? Log.d(TAG, "do removeLocalAccount");
??? LocalAccountConfig.removeAccoutRecord(mContext, userName);
}
```
4. 書(shū)寫(xiě)注冊(cè)接口待用。新建一個(gè)MyPackage,在createNativeModules中完成注冊(cè)接口。
```Java
public class MyPackage implements ReactPackage {
??? @Override
??? public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
??????? List<NativeModule> modules = new ArrayList<NativeModule>();
??????? modules.add(new LetvAccountManager(reactContext));
??????? return modules;
??? }
```
5. 將寫(xiě)好的接口注冊(cè)到MainActivity中去,其中有一個(gè)已經(jīng)寫(xiě)好的方法 --- getPackages,們?cè)谄渲?#xff0c;加入我們寫(xiě)好的接口(MyPackage)。
```java
??? @Override
??? protected List<ReactPackage> getPackages() {
??????? return Arrays.<ReactPackage>asList(
??????????? new MainReactPackage(),
??????????? new TvrnLibsPackage(),
??????????? new com.letv.tvrn.overseaaccountdemo.MyPackage()
??????? );
??? }
```
6. 到這一步終于可以js調(diào)用了
?```JavaScript
? import { NativeModules } from 'react-native';
? const LetvAccountManager = NativeModules.LetvAccountManager;
? LetvAccountManager.removeLocalAccount;
?```
## 八、國(guó)際化 I18N
?**先來(lái)個(gè)名詞解釋:**
?I18N,其來(lái)源是英文單詞 internationalization的首末字符i和n,18為中間的字符數(shù),是“國(guó)際化”的簡(jiǎn)稱。
I18N是使用native監(jiān)聽(tīng)系統(tǒng)語(yǔ)言變化后,JS端自動(dòng)刷新的一套國(guó)際化方案,用法如下:
1、在I18N下添加配置文件,格式如下:
? ```JavaScript
? const zh_CN = {
??? app_name: '樂(lè)視賬號(hào)',
??? account_login: '立即登錄',
??? account_find_password: '找回密碼',
? };
? export default zh_CN;
? ```
2、 在入口js中引用多語(yǔ)言組件,在constructor()中調(diào)用init()方法,再在componentWillMount()中使用timer延遲50ms后調(diào)用getLang()方法,最后聲明ContextProps,向子組件傳遞數(shù)據(jù)。
```JavaScript
constructor(props){
? super(props);
? ....
? Language.init();
}
componentWillMount() {
??? this.timer = setTimeout(() => {
????? lang = Language.getLang();
????? this.setState({
????????? ready:true,
????? });
????? },50
??? );
? }
Main.childContextTypes = {
? language: React.PropTypes.object
};
```
3、子組件使用國(guó)際化組件
```JavaScript
AccountLogin.contextTypes = {
? language: React.PropTypes.object,
};
<Text style={styles.title_text}>
?{this.context.language.app_name}
</Text>
```
## 九、集成方案
把react-native中公共的so和jar集成到系統(tǒng)中,以減小APP的空間占用,使用gradle編譯,引用系統(tǒng)中的reactnative so和jar。
在項(xiàng)目根目錄下執(zhí)行:
```
cd android && ./gradlew assembleRelease
```
會(huì)在android/app/build/outputs/apk目錄下會(huì)有app-release-unsigned.apk,然后把此apk進(jìn)行對(duì)應(yīng)系統(tǒng)簽名即可。
## 十、常見(jiàn)錯(cuò)誤
1.invariant violation:expected a component class,got[object object]
? 創(chuàng)建自定義組件首字母要大寫(xiě),否則會(huì)報(bào)錯(cuò).比如<login/>應(yīng)該寫(xiě)成<Login/>
2.Module 0 is not a registered callable module.
將gradle升級(jí)成最新版本(cd Android 進(jìn)入android目錄執(zhí)行:sudo ./gradlew clean) 或者通過(guò)android studio工具升級(jí).
3.Element type is invalid: expected a string (for built-in components) or a class/function but got: object
?? 一般是你引用了無(wú)效的組件,如果組件確實(shí)正確,看下引用的組件是否正常導(dǎo)出:(export defalut)
4.react native? undefined is not an object (evaluating this....
? 發(fā)生該錯(cuò)誤的一般是忘記bind(this),只要回調(diào)函數(shù)中需要用到this的,一般都需要bind.
? 如:onPress={this.doRegister()} ==》 onPress={this.doRegister.bind(this)}
5.Could not get BatchedBridge, make sure your bundle is packaged corrrectly
adb reverse tcp:8081 tcp:8081
6.await is a reserved word
函數(shù)中有異步操作的時(shí)候,函數(shù)名前要加async,如async function getSystemAccount() {}
7.The android gradle plugin version 2.3.0-beta1 is too old, please update to the latest version.
修改playground/android/gradle/wrapper/gradle-wrapper.properties,注釋掉從網(wǎng)絡(luò)下載,使用本地gradle-2.10-all.zip;
```
# distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
? distributionUrl=./gradle-2.10-all.zip
```
8.Unexpected character ‘�’ (1:0) 圖片加載錯(cuò)誤
根目錄下重啟服務(wù) tvrn start
## 圖書(shū)分享
《React Native開(kāi)發(fā)指南》
《React Native入門與實(shí)戰(zhàn)》
鏈接: https://pan.baidu.com/s/1pLqFr2z 密碼: pp65
?
轉(zhuǎn)載請(qǐng)注明出處!
轉(zhuǎn)載于:https://www.cnblogs.com/qinaidexin/p/6655755.html
總結(jié)
以上是生活随笔為你收集整理的react-native开发经验的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Django--ORM操作
- 下一篇: 2017阿里云代码管理服务公测上线