上個(gè)迭代用React-native做了一個(gè)一期新的需求,在開的過程中舉步維艱,如履薄冰啊,做過react的同學(xué)也知道,現(xiàn)在React-native 版本才到0.43,還不算一個(gè)成熟的技術(shù),開發(fā)的過程中就是不確定性,開發(fā)一個(gè)新需求用著一個(gè)心里沒底的技術(shù),還是真是有點(diǎn)小壓力,不過還好,問題歸問題,但都不是什么大問題,想必一個(gè)只有0.43 的版本到現(xiàn)在 其實(shí)已經(jīng)不錯(cuò)了,我相信React-native 以后會(huì)有很大的潛力的,廢話不多說,說一下自己的心得,供大家參考,如果有什么不對(duì)的,還請(qǐng)輕噴,然后咱們?cè)俟餐逃懀黄饘W(xué)習(xí)。
一、調(diào)試
1.我開發(fā)Rn喜歡打log,項(xiàng)目里每個(gè)方法,每個(gè)生命周期 數(shù)據(jù)回調(diào)之類的通過log去看,執(zhí)行的順序一幕了然,我不用Debug 模式調(diào)試是一因?yàn)閐ebug 速度慢,然后還得一層一層打開去找源碼打斷點(diǎn),二是因?yàn)?Debug是個(gè)終極武器似的,現(xiàn)在的程度還用不到打斷點(diǎn)的方式,除非出什么詭異的問題你看不到log ,這個(gè)時(shí)候Debug也是個(gè)很好的工具。關(guān)于打log 請(qǐng)看我的這篇文章React-native調(diào)試小技巧,在Logcat輸出console的log,不用搖晃也能彈出Debug 彈窗
2.用數(shù)據(jù)線連接著真機(jī)去調(diào)試,而不是用無線。有人問 既然有無線了為什么還用數(shù)據(jù)線,這您有所不知,在我們公司,不知怎么得,公司的公共WiFi(集團(tuán)級(jí)的無線網(wǎng)絡(luò))不知道封了什么東西,導(dǎo)致在同一個(gè)局域網(wǎng)下邊的機(jī)器找不到另一個(gè)機(jī)器端口8081下的bundle包,Charles也不管用了,坑爹的WiFi啊,我們采取了用網(wǎng)線分一個(gè)自己的無線熱點(diǎn),然后這樣就可以了,不過這兩個(gè)各有優(yōu)點(diǎn),無線調(diào)試就是方便,不用限定在自己的工位,自由拿著手機(jī)隨便跑,但是每次都得去debug彈窗輸入ip:port,累覺不愛,但是基于自己開發(fā)的情況,在工位開發(fā)不用隨便亂跑,然后數(shù)據(jù)線開發(fā)在終端輸入一條命令就搞定
adb reverse tcp:8081 tcp:8081
不過這個(gè)也有缺點(diǎn),就是拔掉了數(shù)據(jù)線 然后再重新按上的時(shí)候還得重新adb reverse tcp:8081 tcp:8081
,插入別的iPhone手機(jī)也會(huì)導(dǎo)致 必須重新reverse,總之要不是因?yàn)榫W(wǎng)絡(luò)的原因 我也不會(huì)選擇用數(shù)據(jù)線調(diào)試。
二、繼承。
個(gè)人認(rèn)為js也是面向?qū)ο笮偷木幊陶Z言,只不過不用提前編譯運(yùn)行。所以就大膽的想出了繼承的想法。在js中 繼承的目的主要是為了復(fù)用UI,并且邏輯性不大的UI,純粹只是為了展示,并且UI差異不大的話,可以用繼承的方式去減少代碼的編寫(ps:減少的那些寫代碼的時(shí)間還不夠出問題調(diào)試的時(shí)間呢,唉),比如 我有個(gè)詳情頁的展示,只不過有個(gè)按鈕的展示的ui和邏輯是不同的,大部分是可以復(fù)用的,所以父類的寫法應(yīng)該是
export default class BaseDetailPage extends Component {
// 構(gòu)造
constructor(props) {
super(props);
// 初始狀態(tài)
this.state = {};
}
componentDidMount() {
this.getData();//去獲取詳情頁數(shù)據(jù),每個(gè)詳情頁的接口是不一樣的,所以這個(gè)方法應(yīng)該是在子類里面
}
componentWillUnmount() {
}
/**
* 數(shù)據(jù)成功的回調(diào)
* @param response
*/
handleAppDetailSuccess(response) {
this.setState({
appDetail: response.data,
});
}
/**
* 數(shù)據(jù)獲取失敗的回調(diào)
* @param response
*/
handleAppDetailFailed(response) {
//做些失敗的處理
}
render() {
return (
<View style={styles.container}>
{this.renderLogo()}
{this.renderSizeInfo()}
{this.renderButton()}//這一塊是調(diào)用的子類的方法
</View>)
}
/**
* 自己去寫Ui
*/
renderLogo() {
}
/**
* 自己去寫Ui
*/
renderSizeInfo() {
}
}
子類A的代碼:
export default class DetailA extends BaseDetailPage {
// 構(gòu)造
constructor(props) {
super(props);
// 初始狀態(tài)
this.state = {};
}
/**
* 如果子類也要用這個(gè)生命周期,必須先調(diào)用父類的
* 不然就不會(huì)執(zhí)行了
*/
componentDidMount() {
super.componentDidMount();
}
/**
* 子類獲取詳情頁信息,但是最終的數(shù)據(jù)還是要回傳給父類,所以調(diào)用父類的回調(diào)方法
* 比如
*/
getData() {
NetUtil.get("http://www.baidu.com", this.handleAppDetailSuccess.bind(this),
this.handleAppDetailFailed.bind(this))
}
/**
* 每個(gè)子類必須重寫這個(gè)方法,因?yàn)楦割悤?huì)調(diào)用
* 重寫這個(gè)空方法表明這個(gè)子類什么都不做
* @returns {XML}
*/
renderButton() {
return <TouchableHighlight onPress={()=> {
}
}>
<Text> 我是子類A 的button</Text>
</TouchableHighlight>
}
}
子類B的實(shí)現(xiàn):
export default class DetailB extends BaseDetailPage {
// 構(gòu)造
constructor(props) {
super(props);
// 初始狀態(tài)
this.state = {};
}
/**
* 如果子類也要用這個(gè)生命周期,必須先調(diào)用父類的
* 不然就不會(huì)執(zhí)行了
*/
componentDidMount() {
super.componentDidMount();
}
/**
* 子類獲取詳情頁信息,但是最終的數(shù)據(jù)還是要回傳給父類,所以調(diào)用父類的回調(diào)方法
* 比如
*/
getData() {
NetUtil.get("http://www.baidu.com", this.handleAppDetailSuccess.bind(this),
this.handleAppDetailFailed.bind(this))
}
/**
* 每個(gè)子類必須重寫這個(gè)方法,因?yàn)楦割悤?huì)調(diào)用
* 重寫這個(gè)空方法表明這個(gè)子類什么都不做
* @returns {XML}
*/
renderButton() {
return <TouchableHighlight onPress={()=> {
}
}>
<View>
<Text> 我是子類B 的button</Text>
<Text> 我比A多了好幾個(gè)view</Text>
<Text> 我們都復(fù)用這個(gè)方法</Text>
</View>
</TouchableHighlight>
}
}
總結(jié):
1.注釋大體說了一下子類父類之間的關(guān)系,其實(shí)這樣寫還是有很對(duì)優(yōu)點(diǎn)的,比如在一個(gè)很復(fù)雜的頁面中,復(fù)用父類是很簡單的一個(gè)事情,每塊代碼邏輯簡單明了,沒有業(yè)務(wù)的耦合,不用標(biāo)志位字段判斷 。
2.復(fù)用父類會(huì)出現(xiàn)一些問題,比如那個(gè)生命周期的復(fù)用,必須調(diào)用父類。其實(shí)和android中的生命周期有點(diǎn)類似。特別說明一些state
,子類和父類的state
都是相互可見的,但是必須先定義才能調(diào)用,比如父類定義this.setState({appDetail:null})
,子類如果想用這個(gè)父類的字段,必須先定義這個(gè)字段。賦值的事情就交給父類就行了。
3.本人親測,在用父類的情況下,Rn的hot-loading 貌似失效了,修改了子類只會(huì)彈出toast,然后頁面沒有變化,必須重新退出之后然后在進(jìn)入,才能看到。如果修改了父類會(huì)報(bào)錯(cuò)
undefined is not an object (evaluating ‘internallnstance._pendingForceUpdate’)
解決辦法 重新reload就行了,這個(gè)我不能解釋。
三、DeviceEventEmitter的正確用法
說DeviceEventEmitter 這個(gè)類之前先介紹一些這個(gè)類是干嘛的。DeviceEventEmitter類似于android的廣播,只要注冊(cè)了要監(jiān)聽的東西,并且有相應(yīng)的發(fā)送消息的地方,就會(huì)收到這個(gè)廣播。
主要用在兩個(gè)地方:
- native 像js發(fā)送信息
- js 向js發(fā)送消息
一般我們會(huì)在js初始化的函數(shù)componenDidMount()
去注冊(cè)監(jiān)聽
//第一步
componentDidMount() {
this.addProgressListener();
}
//第二步
addProgressListener() {
this.progerss = DeviceEventEmitter.addListener('onProgress', this.progressListener.bind(this));
}
//第三步
progressListener(params) {
}
當(dāng)然你也可以一氣呵成的去完成這個(gè)注冊(cè)監(jiān)聽的方法,看方法名也知道這個(gè)是個(gè)下載的過程,我把下載的任務(wù)放到了 native層面去做,需要rn實(shí)時(shí)的去展示,
看第三部的param,這個(gè)數(shù)據(jù)結(jié)構(gòu)是jsonObject還是jsonOBjectArray 還是要看native怎么拼裝的。
public void sendProgress(String s, int progress) {
// 發(fā)送參數(shù)
WritableMap map = Arguments.createMap();
map.putInt(PARAM_PROGRESS, progress);
map.putString(APP_ID, s);
//{"appId":124,"progerss":30%} 這是一個(gè)jsonObject 當(dāng)然你可以根據(jù)業(yè)務(wù)拼接 //Arguments.createArray()
getReactApplicationContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("onProgress", map);//這個(gè)消息發(fā)送只會(huì)發(fā)送到監(jiān)聽`onProgress `這個(gè)字段的地方
}
個(gè)人感覺Rn的這個(gè)消息機(jī)制還是不錯(cuò)的,但是如果你感到滿足了而不繼續(xù)學(xué)習(xí),接下來會(huì)讓你很痛苦的。
為什么呢?因?yàn)槟銓W(xué)會(huì)了注冊(cè),但是沒學(xué)會(huì)移除監(jiān)聽,會(huì)引發(fā)很多的問題。
比如這個(gè)
setState(…): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op
其實(shí) 這個(gè)不算問題,只能算一個(gè)warning,但是有warning 了那么離error還遠(yuǎn)嗎。其實(shí)這個(gè)warning是因?yàn)槟沩撁娑夹遁d了,但是這個(gè)監(jiān)聽還存在著,就和android的內(nèi)存泄露一樣,一直刷著沒有mount的組件。那么只需要正確的移除就行了,樓主當(dāng)你就是沒有用到正確的移除方法,導(dǎo)致遇到了一個(gè)問題查了兩天,竟然沒查到,最后才想到是移除監(jiān)聽的方法寫錯(cuò)了。
正確的移除方式
this.progress.remove();//這個(gè)值請(qǐng)看第二步
也可以這樣
DeviceEventEmitter.removeAllListeners();// 如果這個(gè)有back的監(jiān)聽,
//也會(huì)把 back健的監(jiān)聽也除去,不建議用這個(gè)方法
四、小知識(shí)
1.項(xiàng)目中用了一個(gè)第三方庫react-native-progress這個(gè)庫的star有700多

但是這個(gè)項(xiàng)目有個(gè)問題 ,就是在debug的模式下 會(huì)使用indeterminate
屬性為true
會(huì)崩潰,所以 就考慮到了有沒有一個(gè)全局的變量能判斷是在release模式還是在debug模式。其實(shí)是有的 這個(gè)值就是__DEV__
if(__DEV__){
//Debug 模式
}else{
//Release模式
}
2.如果在一個(gè)方法中調(diào)用this.setState()
兩次,那么整個(gè)頁面也會(huì)render兩次。
五、工具
作為開發(fā),各種工具必須的玩的666的,如果我作為面試官,對(duì)于工具的使用必須會(huì)在我的面試范圍之內(nèi),一個(gè)不愛瞎折騰,一個(gè)不愿接受新事物的人談什么創(chuàng)造力。
1.Charles 一個(gè)很牛逼的抓包神器。這次主要用了他的抓Https、模擬慢網(wǎng)速、
模擬網(wǎng)速
1、入口:Proxy--Throttle Settings
2、可在“Throttle Preset”下選擇 預(yù)置的網(wǎng)絡(luò)配置(28.8~256kpbs、3G等)
3、可調(diào)節(jié)帶寬、利用%比、延遲,來模擬網(wǎng)絡(luò)環(huán)境。
備注:用的多的就是可用Throttle Preset設(shè)置2G 3G 4G,模擬不同網(wǎng)速的移動(dòng)網(wǎng)絡(luò)下app運(yùn)行情況; 設(shè)置延遲,查看高延遲情況下,app提示及處理是否正常。
Https抓包
- charles的設(shè)置
1.1 選擇 help | Install Charles CA SSL Certificate
1.2 然后會(huì)密碼輸入框,然后輸入點(diǎn)擊確定,Charles會(huì)把證書寫進(jìn)你的鑰匙串了
1.3 選擇Proxy | Proxy Settings,彈出proxy設(shè)置選項(xiàng)卡,勾選Enabling transparent HTTP proxying
1.4 選擇ssl,勾選Enable SSL Proxying,在Location部份選擇add,按如下圖添加,抓取任意站點(diǎn)、443端口的數(shù)據(jù)
1.手機(jī)安裝證書。
下載證書 http://www.charlesproxy.com/documentation/additional/legacy-ssl-proxying/
2.安裝證書
2.1、Android:把證書放到儲(chǔ)存設(shè)備上,然后 設(shè)置-安全-從SD卡安裝
怎么把證書放到存儲(chǔ)設(shè)備上,這個(gè)是我要說的,看著測試沒還要打開文件存儲(chǔ),然后復(fù)制粘貼真是費(fèi)勁
發(fā)送文件
adb push charles-proxy-ssl-proxying-certificate.crt /sdcard/
[100%] /sdcard/charles-proxy-ssl-proxying-certificate.crt
支持重命名
拷貝文件
adb pull /sdcard/charles-proxy-ssl-proxying-certificate.crt
[100%] /sdcard/charles-proxy-ssl-proxying-certificate.crt
2.2、ios:先用郵件把證書發(fā)到IPHONE上的郵箱,用iFile打開安裝
其實(shí)Charles還有很多強(qiáng)大的功能,比如Map、Rewrite等功能,其實(shí)還都用過,但是在這就不多說了,要說的話就要另開新帖了,這兒重點(diǎn)是講rn
最后提醒大家一定要仔細(xì),往往稍微一不留神就會(huì)造成一個(gè)很小的bug但是你不得不花費(fèi)時(shí)間去調(diào)試bug,到時(shí)候回過頭來你會(huì)恨自己為什么當(dāng)初不仔細(xì)一點(diǎn),這也證明了很多bug就是因?yàn)殚_發(fā)的時(shí)候不仔細(xì)造成的。
今天是五一假期啊,我卻在這碼字~祝大家有個(gè)好的假期。