1. 屏幕適配
RN布局使用的單位是dp,而開發(fā)人員從設(shè)計(jì)稿最方便獲取的是px,所以需要一個(gè)工具類把px轉(zhuǎn)成dp,下面以寬度為375px的設(shè)計(jì)稿為例:
const deviceWidthDp = Dimensions.get('window').width;
const uiWidthPx = 375;
export default function pxtodp(uiElementPx) {
return Platform.OS === 'ios' ? uiElementPx * deviceWidthDp / uiWidthPx : Math.floor(uiElementPx * deviceWidthDp / uiWidthPx)
}
在bug修復(fù)階段,發(fā)現(xiàn)一個(gè)常見的bug,多組件傳值時(shí),出現(xiàn)了多次的p2dp嵌套,導(dǎo)致了值被轉(zhuǎn)換多次,不符預(yù)期,所以寫組件的時(shí),應(yīng)該規(guī)定好是最底層使用p2dp,還是傳入的參數(shù)使用p2dp。
2. 樣式管理
- RN的樣式可以是數(shù)組,類似css中定義多個(gè)class;
style={[styles.A,styles.B]}
- RN的樣式?jīng)]有繼承嵌套這類的功能,為了方便、高效使用樣式,我們使用了一個(gè)樣式的工具類,寫入常用的樣式、樣式組合,便于頁(yè)面調(diào)用。但每個(gè)頁(yè)面調(diào)用都要引入工具類太麻煩,考慮注冊(cè)到全局變量,這時(shí)候發(fā)現(xiàn)了一個(gè)問題,RN的
global(全局變量)只能作用于Component,在StyleSheet無法識(shí)別,難道是根據(jù)某種上下文關(guān)系存在的?最后找到一個(gè)解決方案是我們寫一個(gè)函數(shù),在函數(shù)內(nèi)是就能訪問全局變量,然后把StyleSheet在函數(shù)中return出來,代碼片段像這樣:
const styles = () => {
const {
paddingLarge, paddingSmall, paddingMedium,
fontSizeMedium, fontSizeSmall, fontWeightLight
} = theme
return StyleSheet.create({
wrapper: {
height: px2dp(106),
marginHorizontal: paddingLarge,
3. 平臺(tái)差異
使用react-native的Platform庫(kù)來控制android和ios的差異
Platform.OS === 'ios' ? doios : doandroid
新版api還可以這么寫:
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' +
'Cmd+D or shake for dev menu',
android: 'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});
另外如何需要根據(jù)平臺(tái)引入不同的組件,例如:
BigButton.ios.js
BigButton.android.js
你可以直接:
const BigButton = require('./BigButton');
React Native 會(huì)自動(dòng)識(shí)別。
4. 點(diǎn)擊
RN上除了Text組件(自帶onPress方法),其他組件默認(rèn)是不支持點(diǎn)擊事件。所以 RN 中提供了幾個(gè)直接處理響應(yīng)事件的組件,基本上能夠滿大部分的點(diǎn)擊處理需求TouchableHighlight, TouchableNativeFeedback, TouchableOpacity 和 TouchableWithoutFeedback。因?yàn)檫@幾個(gè)組件的功能和使用方法基本類似,只是 Touch 的反饋效果不一樣,所以根據(jù)需求選用合適的方法使用即可。
另外,如果在Touchable中onPress執(zhí)行了一個(gè)setState的操作,這個(gè)操作需要大量計(jì)算工作并且導(dǎo)致了掉幀,這時(shí)候可以將操作封裝到requestAnimationFrame中:
handleOnPress() {
// 謹(jǐn)記在使用requestAnimationFrame、setTimeout以及setInterval時(shí)
// 要使用TimerMixin(其作用是在組件unmount時(shí),清除所有定時(shí)器)
this.requestAnimationFrame(() => {
this.doExpensiveAction();
});
}
5. 手勢(shì)識(shí)別
RN 提供了內(nèi)置的手勢(shì)識(shí)別庫(kù)PanResponder,我們只需要?jiǎng)?chuàng)建一個(gè)實(shí)例,然后搭載在任意的區(qū)域,就能監(jiān)聽到這塊區(qū)域的手勢(shì)變化,代碼片段如下:
componentWillMount: function() {
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onPanResponderGrant: this._handlePanResponderGrant,
onPanResponderMove: this._handlePanResponderMove,
onPanResponderRelease: this._handlePanResponderEnd,
onPanResponderTerminate: this._handlePanResponderEnd,
});
}
<View
{...this._panResponder.panHandlers}
/>
但這個(gè)手勢(shì)庫(kù)PanResponder有個(gè)bug,會(huì)blockTouchableWithoutFeedback/Highlight等的點(diǎn)擊操作,解決方案是:
onMoveShouldSetPanResponderCapture: (evt, gestureState) => {
return Math.abs(gestureState.dx) > 5;
},
具體可以看這個(gè)issue。
6. 文字行數(shù)控制
RN提供了numberOfLines方法實(shí)現(xiàn)行數(shù)控制,以及溢出部分的處理,同css的text-overflow。
7. 樣式表
RN的布局主要使用flex,而且是閹割版的flex,樣式表大致只有如下屬性:
"alignItems",
"alignSelf",
"backfaceVisibility",
"backgroundColor",
"borderBottomColor",
"borderBottomLeftRadius",
"borderBottomRightRadius",
"borderBottomWidth",
"borderColor",
"borderLeftColor",
"borderLeftWidth",
"borderRadius",
"borderRightColor",
"borderRightWidth",
"borderStyle",
"borderTopColor",
"borderTopLeftRadius",
"borderTopRightRadius",
"borderTopWidth",
"borderWidth",
"bottom",
"color",
"flex",
"flexDirection",
"flexWrap",
"fontFamily",
"fontSize",
"fontStyle",
"fontWeight",
"height",
"justifyContent",
"left",
"letterSpacing",
"lineHeight",
"margin",
"marginBottom",
"marginHorizontal",
"marginLeft",
"marginRight",
"marginTop",
"marginVertical",
"opacity",
"overflow",
"padding",
"paddingBottom",
"paddingHorizontal",
"paddingLeft",
"paddingRight",
"paddingTop",
"paddingVertical",
"position",
"resizeMode",
"right",
"rotation",
"scaleX",
"scaleY",
"shadowColor",
"shadowOffset",
"shadowOpacity",
"shadowRadius",
"textAlign",
"textDecorationColor",
"textDecorationLine",
"textDecorationStyle",
"tintColor",
"top",
"transform",
"transformMatrix",
"translateX",
"translateY",
"width",
"writingDirection"
8. 警告信息
在開發(fā)過程中,如果我們需要在界面中打印出信息,可以借助console.warn打印出警告信息,而console.log的信息需要開啟debug模式,在控制臺(tái)可見。
另外最常見的一個(gè)警告信息是提示你加上key屬性,當(dāng)我們遍歷輸出組件時(shí),組件一定記得加上key屬性,這樣做能提高虛擬DOM Diff的效率。
9. 解決緩慢的導(dǎo)航器(Navigator)切換
Navigator的動(dòng)畫是由JavaScript線程所控制的。想象一下“從右邊推入”這個(gè)場(chǎng)景的切換:每一幀中,新的場(chǎng)景從右向左移動(dòng),從屏幕右邊緣開始(不妨認(rèn)為是320單位寬的的x軸偏移),最終移動(dòng)到x軸偏移為0的屏幕位置。切換過程中的每一幀,JavaScript線程都需要發(fā)送一個(gè)新的x軸偏移量給主線程。如果JavaScript線程卡住了,它就無法處理這項(xiàng)事情,因而這一幀就無法更新,動(dòng)畫就被卡住了。
長(zhǎng)遠(yuǎn)的解決方法,其中一部分是要允許基于JavaScript的動(dòng)畫從主線程分離。同樣是上面的例子,我們可以在切換動(dòng)畫開始的時(shí)候計(jì)算出一個(gè)列表,其中包含所有的新的場(chǎng)景需要的x軸偏移量,然后一次發(fā)送到主線程以某種優(yōu)化的方式執(zhí)行。由于JavaScript線程已經(jīng)從更新x軸偏移量給主線程這個(gè)職責(zé)中解脫了出來,因此JavaScript線程中的掉幀就不是什么大問題了 —— 用戶將基本上不會(huì)意識(shí)到這個(gè)問題,因?yàn)橛脩舻淖⒁饬?huì)被流暢的切換動(dòng)作所吸引。
不幸的是,這個(gè)方案還沒有被實(shí)現(xiàn)。所以當(dāng)前的解決方案是,在動(dòng)畫的進(jìn)行過程中,利用InteractionManager來選擇性的渲染新場(chǎng)景所需的最小限度的內(nèi)容。
InteractionManager.runAfterInteractions的參數(shù)中包含一個(gè)回調(diào),這個(gè)回調(diào)會(huì)在navigator切換動(dòng)畫結(jié)束的時(shí)候被觸發(fā)。
componentDidMount() {
InteractionManager.runAfterInteractions(() => {
this.setState({renderPlaceholderOnly: false});
});
}
10. 全局變量
RN可以通過global來設(shè)置全局變量,例如我們要把本地存儲(chǔ)的方法掛載到全局:
global.storage = storage
之后直接使用storage即可。
11. 調(diào)試
RN在開發(fā)菜單里提供了Debug JS Remotely的選項(xiàng),點(diǎn)擊后會(huì)打開chrome,可以查看日志,斷點(diǎn)調(diào)試。
另外還可以安裝react-devtools進(jìn)行樣式調(diào)試。
更詳細(xì)的調(diào)試文檔,可以看這里。
12. WebView
RN自帶了WebView的支持,我們可以通過簡(jiǎn)單的封裝,讓它更易用,另外它除了支持url,還支持自定義的html。
13. 鏈接原生庫(kù)
有一些庫(kù)基于一些原生代碼實(shí)現(xiàn),你必須把這些文件添加到你的應(yīng)用,否則應(yīng)用會(huì)在你使用這些庫(kù)的時(shí)候產(chǎn)生報(bào)錯(cuò)。
我們無需手動(dòng)添加,通過react-native link命令即可完成鏈接原生庫(kù)。
14. Component命名
react聲明組件時(shí),第一個(gè)字母必須大寫。
15. 字體引入
IOS上要使用自定義的字體,必須把字體文件拖到對(duì)應(yīng)的Xcode工程里面,勾選Add to targets和Create groups,修改Info.plist文件,添加屬性Fonts provided by application;
安卓上要使用自定義的字體,必須要把字體文件放在[project root]/android/app/src/main/assets/fonts/目錄下才能生效
16. icon解決方案
我們使用iconfont,然后進(jìn)行了簡(jiǎn)單的封裝,詳細(xì)見此。
17. 使用ListView的正確姿勢(shì)
我們?cè)谝淮问褂?code>ListView過程中,發(fā)現(xiàn)state不會(huì)改變,在GitHub上找到了同樣問題的issues:this.state does't work at listView's renderRow。進(jìn)而獲得了一些使用ListView的正確姿勢(shì):適合動(dòng)態(tài)列表數(shù)據(jù),固化數(shù)據(jù)盡量不用,renderRow里盡量傳數(shù)據(jù),避免state判斷,如需state,應(yīng)該付給參數(shù)傳入。
18. ScrollView
我們?cè)谑褂?code>ScrollView的onScroll方法的時(shí)候,有時(shí)會(huì)發(fā)現(xiàn)獲取的值和我們的預(yù)期不一致,是因?yàn)?code>ScrollView默認(rèn)每幀最多調(diào)用一次此回調(diào)函數(shù),如果要增大調(diào)用的頻率,可以用scrollEventThrottle屬性來控制。
19. 陰影
iOS上的陰影使用以下的屬性:
shadowColor Sets the drop shadow color
shadowOffset {width: number, height: number}Sets the drop shadow offset
shadowOpacity numberSets the drop shadow opacity (multiplied by the color's alpha component)
shadowRadius numberSets the drop shadow blur radius
但注意如果給Image組件添加陰影,不能把樣式寫在Image的style,而需要包裹一層View來添加陰影樣式。
Android上則不支持shadow*的樣式,只有elevation仰角的屬性來替代,但效果不太好,如果需要實(shí)現(xiàn)一致的效果,需要自己實(shí)現(xiàn)或者引入相關(guān)的庫(kù)。
20. FlatList
FlatList號(hào)稱是ListView的升級(jí)版,會(huì)有更好的體驗(yàn)、更高的效率,但目前這個(gè)組件還不穩(wěn)定。使用過程有很多問題,例如首次加載會(huì)觸發(fā)兩次onEndReached、必須設(shè)置height屬性,不然onEndReached無法觸發(fā)、下拉到底仍可下拉,并出現(xiàn)大片白屏等。
注:官方在0.48版本開始廢棄
ListView,推薦使用FlatList或SectionList,看來應(yīng)該比較穩(wěn)定了。
21. ref
任何組件都用一個(gè)ref的屬性,ref是組件實(shí)例的引用,通過復(fù)制給this變量,可以在任意位置操作組件。
22. PureComponent
當(dāng)props或者state改變的時(shí)候,會(huì)執(zhí)行shouldComponentUpdate方法來判斷是否需要重新render組建,我們平時(shí)在做頁(yè)面的性能優(yōu)化的時(shí)候,往往也是通過這一步來判斷的。Component默認(rèn)的shouldComponentUpdate返回的是true,如下:
shouldComponentUpdate(nextProps, nextState) {
return true;
}
而PureComponent的shouldComponentUpdate是這樣的:
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps) || ! shallowEqual(inst.state, nextState);
}
相當(dāng)于PureComponent幫我們判斷如果props或者state沒有改變的時(shí)候,就不重復(fù)render,這對(duì)于純展示組件,能節(jié)省不少比較的工作。
23. babelHelpers.objectDestructuringEmpty is not a function
在某些機(jī)子上遇到過一個(gè)如題的報(bào)錯(cuò),具體看issue
原因是使用了如下的語(yǔ)法:
const {} = result
開發(fā)時(shí)盡量規(guī)范語(yǔ)法,避免寫些無意義的語(yǔ)法。
24. IOS模擬器卡頓
別當(dāng)心,很可能是你按到了快捷鍵,打開了慢動(dòng)畫的選項(xiàng),關(guān)掉它就行了。

25. 快捷方式
IOS喚起調(diào)試菜單是? + D,刷新是? + R;
Android喚起調(diào)試菜單是? + M ,刷新是R+ R;
在真機(jī)上可以通過搖一搖喚起調(diào)試菜單。
26. LayoutAnimation
Animated的接口一般會(huì)在JavaScript線程中計(jì)算出所需要的每一個(gè)關(guān)鍵幀,而LayoutAnimation則利用了Core Animation,使動(dòng)畫不會(huì)被JS線程和主線程的掉幀所影響。
注意:
LayoutAnimation只工作在“一次性”的動(dòng)畫上("靜態(tài)"動(dòng)畫) -- 如果動(dòng)畫可能會(huì)被中途取消,你還是需要使用Animated。
27.本地存儲(chǔ)的使用
這個(gè)問題琢磨了一段時(shí)間,還沒有找到我想要的答案。情景大致是這樣,一次訪問某頁(yè)面,通過AsyncStorage保存了數(shù)據(jù),第二次進(jìn)入頁(yè)面肯定希望render中直接用AsyncStorage中的本地?cái)?shù)據(jù),無需二次render。但是AsyncStorage是個(gè)異步函數(shù),所以你即便在componentWillMount調(diào)用,還是需要在render后才能拿到數(shù)據(jù),所以就會(huì)出現(xiàn)二次render,即便componentWillMount中用await也無效,認(rèn)真看了遍官方生命周期的文檔,但并沒有什么收獲。目前的解決方案是用一個(gè)標(biāo)志位控制,標(biāo)志位為false時(shí)出loading,只有當(dāng)拿到數(shù)據(jù)標(biāo)志位為true時(shí)才切真正的render,但這種方案其實(shí)還是執(zhí)行了兩次render,不過意外的是效果不錯(cuò),看不出有閃動(dòng),甚至看不出有loading過程。但如果你把關(guān)于本地存儲(chǔ)的一系列判斷邏輯是寫在InteractionManager.runAfterInteractions中,就會(huì)明顯的看到loading,打斷點(diǎn)看了下,發(fā)現(xiàn)即便是兩次render,都發(fā)生在頁(yè)面過場(chǎng)前,也就是屏幕還在上一頁(yè)面的時(shí)候就在render,而寫在InteractionManager.runAfterInteractions里,正是在執(zhí)行過場(chǎng)或者過場(chǎng)執(zhí)行完時(shí)發(fā)現(xiàn),這里面的 state變化反應(yīng)到render中就會(huì)在屏幕中被看到。當(dāng)然這個(gè)問題我還是想繼續(xù)關(guān)注下去,react-native也有不少類似的issue,最終還是希望能找到只需要一次render的辦法。
注:思路1(Redux是無視生命周期的)
28.從原生頁(yè)面如何跳轉(zhuǎn)到指定RN頁(yè)面
這里用到方法就是發(fā)送事件到JavaScript,然后根據(jù)獲取的參數(shù),跳轉(zhuǎn)相應(yīng)的路由。
原生模塊可以在沒有被調(diào)用的情況下往JavaScript發(fā)送事件通知。最簡(jiǎn)單的辦法就是通過RCTDeviceEventEmitter,這可以通過ReactContext來獲得對(duì)應(yīng)的引用,像這樣:
@ReactMethod
public void goPage(int pageid) {
System.out.println("########"+pageid+"########");
// failedCallback.invoke();
WritableMap params = Arguments.createMap();
params.putInt("name", pageid);
reactApplicationContextAction
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("test", params);
}
Javascript通過DeviceEventEmitter模塊來監(jiān)聽事件,獲取跳轉(zhuǎn)信息,跳轉(zhuǎn)至相應(yīng)路由:
import {DeviceEventEmitter} from 'react-native'
componentWillMount(){
DeviceEventEmitter.addListener("test", (result) => {
let mainComponent = require(result.name);
this.setState({
content:mainComponent,
showModule:true
})
})
}
render(){
if(this.state.content){
route.push(this.state.content)
return null
}else{
return null
}
}
29.字體背景色
字體的背景色是會(huì)繼承它父級(jí)的backgroundColor,通常我們沒有在意。但當(dāng)你如果需要在字體上疊加一層蒙版也好、漸變也好,它們的顏色又恰好與你之前的背景色不一致,你就會(huì)發(fā)現(xiàn)字體的背景色凸顯出來了,這時(shí)需要把字體的backgroundColor設(shè)置為transparent,這樣才不會(huì)影響蓋在它上面的層,當(dāng)然遇到這樣的問題還可能和你布局的先后順序有關(guān),通常使用absolute應(yīng)該排在后面,避免被后面的元素覆蓋,zIndex好像是只作用于同樣是absolute定義,absolute和flex之間無法使用zIndex。
30.擴(kuò)展性
使用原生方法(NativeModules)
- IOS
想要?jiǎng)?chuàng)建一個(gè)iOS模塊,只需要?jiǎng)?chuàng)建一個(gè)接口,實(shí)現(xiàn)RCTBridgeModule協(xié)議,然后把你想在Javascript中使用的任何方法用RCT_EXPORT_METHOD包裝。最后,再用RCT_EXPORT_MODULE導(dǎo)出整個(gè)模塊即可。
// Objective-C
#import "RCTBridgeModule.h"
@interface MyCustomModule : NSObject <RCTBridgeModule>
@end
@implementation MyCustomModule
RCT_EXPORT_MODULE();
// Available as NativeModules.MyCustomModule.processString
RCT_EXPORT_METHOD(processString:(NSString *)input callback:(RCTResponseSenderBlock)callback)
{
callback(@[[input stringByReplacingOccurrencesOfString:@"Goodbye" withString:@"Hello"]]);
}
@end
// JavaScript
import React, {
Component,
} from 'react';
import {
NativeModules,
Text
} from 'react-native';
class Message extends Component {
constructor(props) {
super(props);
this.state = { text: 'Goodbye World.' };
}
componentDidMount() {
NativeModules.MyCustomModule.processString(this.state.text, (text) => {
this.setState({text});
});
}
render() {
return (
<Text>{this.state.text}</Text>
);
}
}
- Android
同樣的,Android也支持自定義擴(kuò)展。僅僅是方法略有差異。
創(chuàng)建一個(gè)基礎(chǔ)的安卓模塊,需要先創(chuàng)建一個(gè)繼承自ReactContentBaseJavaModule的類,然后使用@ReactMethod標(biāo)注(Annotation)來標(biāo)記那些你希望通過Javascript來訪問的方法。最后,需要在ReactPackage中注冊(cè)這個(gè)模塊。
// Java
public class MyCustomModule extends ReactContextBaseJavaModule {
// Available as NativeModules.MyCustomModule.processString
@ReactMethod
public void processString(String input, Callback callback) {
callback.invoke(input.replace("Goodbye", "Hello"));
}
}
// JavaScript
import React, {
Component,
} from 'react';
import {
NativeModules,
Text
} from 'react-native';
class Message extends Component {
constructor(props) {
super(props);
this.state = { text: 'Goodbye World.' };
},
componentDidMount() {
NativeModules.MyCustomModule.processString(this.state.text, (text) => {
this.setState({text});
});
}
render() {
return (
<Text>{this.state.text}</Text>
);
}
}
使用原生頁(yè)面(requireNativeComponent)
- IOS
若想自定義iOS View,可以這樣來做:首先繼承RCTViewManager類,然后實(shí)現(xiàn)一個(gè)-(UIView *)view方法,并且使用RCT_EXPORT_VIEW_PROPERTY宏導(dǎo)出屬性。最后用一個(gè)Javascript文件連接并進(jìn)行包裝。
// Objective-C
#import "RCTViewManager.h"
@interface MyCustomViewManager : RCTViewManager
@end
@implementation MyCustomViewManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [[MyCustomView alloc] init];
}
RCT_EXPORT_VIEW_PROPERTY(myCustomProperty, NSString);
@end
// JavaScript
import React, {
Component,
} from 'react';
import PropTypes from 'prop-types';
import { requireNativeComponent } from 'react-native';
var NativeMyCustomView = requireNativeComponent('MyCustomView', MyCustomView);
export default class MyCustomView extends Component {
static propTypes = {
myCustomProperty: PropTypes.oneOf(['a', 'b']),
};
render() {
return <NativeMyCustomView {...this.props} />;
}
}
- Android
創(chuàng)建自定義的Android View,首先定義一個(gè)繼承自SimpleViewManager的類,并實(shí)現(xiàn)createViewInstance和getName方法,然后使用@ReactProp標(biāo)注導(dǎo)出屬性,最后用一個(gè)Javascript文件連接并進(jìn)行包裝。
// Java
public class MyCustomViewManager extends SimpleViewManager<MyCustomView> {
@Override
public String getName() {
return "MyCustomView";
}
@Override
protected MyCustomView createViewInstance(ThemedReactContext reactContext) {
return new MyCustomView(reactContext);
}
@ReactProp(name = "myCustomProperty")
public void setMyCustomProperty(MyCustomView view, String value) {
view.setMyCustomProperty(value);
}
}
// JavaScript
import React, {
Component,
requireNativeComponent
} from 'react-native';
var NativeMyCustomView = requireNativeComponent('MyCustomView', MyCustomView);
export default class MyCustomView extends Component {
static propTypes = {
myCustomProperty: React.PropTypes.oneOf(['a', 'b']),
};
render() {
return <NativeMyCustomView {...this.props} />;
}
}
更多(使用原生UI、同個(gè)頁(yè)面RN與Native的相互嵌套等)
更多和原生通信的內(nèi)容可以看官網(wǎng)文檔:英文、中文
31.IOS真機(jī)打包
如何沒有IOS開發(fā)者賬號(hào),一個(gè)項(xiàng)目只允許最多在三臺(tái)設(shè)備上打包,而且過期時(shí)間只有7天,另外無法移除打包過的機(jī)子的mac地址,意思就是我在A機(jī)子裝過,就用掉一個(gè)名額,沒法把這個(gè)名額讓出來了。這樣導(dǎo)致我們?cè)阡侀_測(cè)試、給大家體驗(yàn)時(shí)遇到了瓶頸。這時(shí)候最好的方案是有一個(gè)企業(yè)賬號(hào),可以打出一個(gè)企業(yè)包,在任何機(jī)子安裝,如果只有開發(fā)者賬號(hào),那也只能在100臺(tái)設(shè)備安裝,開啟和關(guān)閉權(quán)限都需要到開發(fā)者網(wǎng)站操作,收回權(quán)限還需要給Apple發(fā)郵件。打完包,我推薦用fir平臺(tái)托管應(yīng)用,只要把生成的頁(yè)面或者二維碼發(fā)給大家即可,方便、快捷,另外還支持權(quán)限、密碼的設(shè)置,實(shí)名制后每天有一百次的下載額度,其實(shí)也是足夠用了。
32.如何實(shí)現(xiàn)回退后刷新上個(gè)頁(yè)面
刷新上個(gè)頁(yè)面,說白了就是傳參。目前用的navigator,只有push能傳參,pop并沒有,這樣如何做到頁(yè)面回退能讓上一個(gè)頁(yè)面感知呢?我嘗試了幾個(gè)辦法:
-
Redux
通過redux的store,簡(jiǎn)單粗暴,沒啥好說 -
DeviceEventEmitter
第一個(gè)頁(yè)面監(jiān)聽,回退的時(shí)候觸發(fā),其實(shí)就是個(gè)簡(jiǎn)單的觀察者模式,代碼大致如下:
//A頁(yè)面
import {
AppRegistry,
StyleSheet,
Text,
View,
DeviceEventEmitter
} form 'react-native';
componentDidMount() {
this.subscription = DeviceEventEmitter.addListener('userNameDidChange',(userName) =>{
console.warn(userName);
})
}
componentWillUnmount() {
// 移除
this.subscription.remove();
}
//B頁(yè)面,在回退前
DeviceEventEmitter.emit('userNameDidChange', '通知來了');
-
callback
在A界面跳到B界面時(shí),帶上回調(diào)參數(shù),如:
this.props.navigator.push({‘id’:’b’,’callback’:this.refreshAAvatar}
然后在你回退前執(zhí)行callback即可
33. 在初始化bundle時(shí)如何傳參
在注冊(cè)bundle時(shí)傳參有什么用呢?可以實(shí)現(xiàn)跳轉(zhuǎn)到特定頁(yè)面。
來看看IOS和Android分別是怎么實(shí)現(xiàn):
//IOS initialProps就是給RN的參數(shù)
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"MyAwesomeApp"
initialProperties:initialProps
launchOptions:launchOptions];
//Android
Bundle initialProps = new Bundle();
initialProps.putString("myKey", "myValue");
mReactRootView.startReactApplication(mReactInstanceManager, "MyAwesomeApp", initialProps);