RN作為H5的下一代跨平臺(tái)方案衰粹,風(fēng)頭正勁背苦。用UC震驚部的話來(lái)說(shuō):沒(méi)有研究過(guò)RN,都不好意思說(shuō)自己做過(guò)客戶端『瞿酰現(xiàn)在還不了解學(xué)習(xí)React-Native甥材,你就要快老了盯另!
好了,拋開(kāi)標(biāo)題的噱頭洲赵。寫下這篇文章的主要目的,還是作為自己的一個(gè)學(xué)習(xí)記錄與匯總商蕴,同時(shí)對(duì)RN的踩坑與學(xué)習(xí)的經(jīng)驗(yàn)進(jìn)行分享叠萍,總結(jié)ES 6的常用語(yǔ)法,頁(yè)面的生命周期與相關(guān)的一些學(xué)習(xí)資料绪商。讓其他客戶端開(kāi)發(fā)者能快速的對(duì)React-Native進(jìn)行接入與學(xué)習(xí)苛谷。文章包含以下內(nèi)容:
- 為什么使用RN
- RN的簡(jiǎn)單用例
- ES 6語(yǔ)法
- RN的生命周期
- 坑點(diǎn)匯總
為什么選擇RN
其實(shí)在一開(kāi)始我也曾思考過(guò)這個(gè)問(wèn)題。既然現(xiàn)在已經(jīng)有成熟的H5方案了格郁,為什么還要選擇RN腹殿?
最根源的想法還是來(lái)自于H5的痛點(diǎn)。頁(yè)面的交互性差例书,數(shù)據(jù)緩存锣尉,回傳回調(diào)困難。在使用過(guò)程中也難以滿足用戶所需的多元化功能與快速響應(yīng)的速度决采。面對(duì)這種情況自沧,以往我們只能委屈求全,摘掉一些耗時(shí)長(zhǎng)树瞭,操作復(fù)雜的功能拇厢。亦或是舍棄使用H5爱谁,最后改由客戶端實(shí)現(xiàn),已達(dá)到最好的用戶體驗(yàn)孝偎。其次访敌,也是作為一個(gè)技術(shù)的自我學(xué)習(xí)心態(tài)的驅(qū)使。React-Native作為編寫Hybrid APP的新思路衣盾,不管項(xiàng)目需要接入與否捐顷,我們都有必要去學(xué)習(xí)與了解其中的原理和使用。即作為一個(gè)知識(shí)儲(chǔ)備雨效,也能更好的學(xué)習(xí)里面的新思想迅涮。作為一個(gè)客戶端開(kāi)發(fā)者,Redux的設(shè)計(jì)給我的思考與收獲也是很大的徽龟。
總結(jié)下來(lái)叮姑,RN具有以下優(yōu)勢(shì):
- 運(yùn)行性能更好,調(diào)用原生組件据悔。
- 采用了css传透,flexbox的布局模式,方便開(kāi)發(fā)极颓。
- 比起 Hybird 擴(kuò)展性更強(qiáng)朱盐,自由度更高,交互體驗(yàn)更好菠隆。
- 支持應(yīng)用熱更新與遠(yuǎn)程調(diào)試兵琳。
- 展示復(fù)雜的高階動(dòng)畫,能通過(guò)原生平臺(tái)編寫骇径,更少卡頓躯肌。
- 大部分代碼都可跨平臺(tái),易于維護(hù)破衔。
- 和weex相比清女,社區(qū)環(huán)境更優(yōu)秀,也有更多成熟的應(yīng)用使用RN方案晰筛。
RN的簡(jiǎn)單用例
以下是RN最簡(jiǎn)單的樣例嫡丙,分為三部分組成:
1.對(duì)象、組件的導(dǎo)入
2.樣式的聲明
3.組件的使用與樣式引入
4.注冊(cè)入口類读第。將與Native中代碼的設(shè)置對(duì)應(yīng)(這里為'rn'
)曙博。
// 1
import React, { Component } from 'react';
import {
AppRegistry,
Text,
Button,
View,
TouchableHighlight,
Image,
StyleSheet,
} from 'react-native';
// 2
const style = StyleSheet.create({
centerSelf:{
alignSelf: 'center',
},
centerJustify:{
justifyContent: 'center',
},
centerItems:{
alignItems: 'center',
},
red:{
backgroundColor: 'red',
},
});
// 3
class HelloWorldApp extends Component {
componentDidMount(){ // 頁(yè)面加載后
// do something...
}
render() {
return (
<View style={[{flex:1}, style.red, style.centerItems, style.justifyContent]}>
<Text style={[{fontSize: 20}, {fontWeight: "bold"}]}>Hello ,?? !</Text>
</View>
);
}
}
// 4 注意卦方,這里用引號(hào)括起來(lái)的'rn'必須和你init創(chuàng)建的項(xiàng)目名一致
AppRegistry.registerComponent('rn', () => HelloWorldApp);
可以看出其實(shí)RN的組件與樣式的使用與H5的標(biāo)簽的使用非常相似羊瘩。只要在給style傳入樣式,就可以實(shí)現(xiàn)布局了。上述代碼的效果也很簡(jiǎn)單尘吗,只有 View 與 Text 兩個(gè)組件逝她。
外層 View 大小隨外層大小變化并填充滿(flex:1
)。同時(shí) View 的背景為紅色(backgroundColor: 'red'
)睬捶。他的子組件將會(huì)水平居中(alignItems: 'center'
)與垂直居中(justifyContent: 'center'
)黔宛。嚴(yán)格說(shuō)來(lái)應(yīng)該是主軸與次軸居中,RN中組件可以設(shè)置豎直或水平方向?yàn)橹鬏S擒贸。
內(nèi)層為 Text 組件內(nèi)容顯示為 粗體臀晃,黃色 的 Hello ,?? !
介劫。
從上不難看出RN的樣式其實(shí)就是傳入配置json徽惋。使其使用起來(lái)和H5類似。因此座韵,當(dāng)多個(gè)樣式同時(shí)使用時(shí)需要用數(shù)組傳入险绘。
style = {style.red} // 傳入樣式對(duì)象
style = {{fontSize: 20}} // 編寫傳入對(duì)象
style = {[style.red, style.centerItems]} // 傳入多個(gè)樣式則通過(guò)數(shù)組配置
RN的頁(yè)面生命周期也與客戶端類似,有各種加載與渲染的過(guò)程誉碴,如// 3 中的componentDidMount宦棺,與iOS的 ViewDidLoad 用法類似。下面就讓我們來(lái)認(rèn)識(shí)一下React-Native組件的生命周期黔帕。
RN 的組件生命周期
主要分為 Mounting(加載)
, Updating(更新)
, Unmounting(卸載)
過(guò)程代咸。Mounting
操作在組件實(shí)例化的時(shí)候?qū)⒈徽{(diào)用,Updating
會(huì)在組件 props 或 state 變化的時(shí)候調(diào)用成黄,而Unmounting
則負(fù)責(zé)頁(yè)面銷毀呐芥。
Mounting
組件實(shí)例化時(shí)以下函數(shù)會(huì)被調(diào)用或插入 Dom 樹(shù)。加載過(guò)程如上述流程圖的最上側(cè)框圖慨默。
Updating
一般當(dāng)屬性或組件狀態(tài)變化的時(shí)候會(huì)觸發(fā)贩耐,以下方法會(huì)在組件重新渲染的時(shí)候調(diào)用。更新調(diào)用鏈如上述流程圖左下側(cè)框圖厦取。
- componentWillReceiveProps()
- shouldComponentUpdate()
- componentWillUpdate()
- render()
- componentDidUpdate()
Unmounting
在組件 Dom 樹(shù)刪除時(shí)調(diào)用。卸載調(diào)用鏈如上述流程圖右下側(cè)框圖管搪。
ES6 常用語(yǔ)法
定義組件
ES 6與ES 5定義組件的代碼有很大變化虾攻,ES 6終于有了類的聲明,也讓我們看順眼多了更鲁。為了代碼的維護(hù)和使用霎箍,已經(jīng)不再建議使用ES 5的語(yǔ)法了。
// ES5 的用法澡为,不建議
var View = React.createClass({
render(){
// 輸出變量
return (<Text>Hello漂坏,{this.props.name}!<Text />);
}
});
// ES6
class View extends React.Component{
render(){
// 跨組件傳值
return (<React.View title='頁(yè)面' state=...this.props.route />);
}
}
屬性初始化與校驗(yàn)
ES 6類屬性的初始化建議在構(gòu)造函數(shù)中constructor
實(shí)現(xiàn)顶别。其中也提供了一個(gè)屬性propTypes
谷徙,用以校驗(yàn)?zāi)硞€(gè)變量是否必要。使用如下:
class MyView extends React.Component{
constructor(props) {
super(props);
this.state = {
color: props.initialColor
};
}
// 默認(rèn)值(組件不傳遞值過(guò)來(lái)的時(shí)候)
static defaultProps = {
name: "bilibili",
nick: "2333",
}
// propTypes用于驗(yàn)證轉(zhuǎn)入的props驯绎,當(dāng)向 props 傳入無(wú)效數(shù)據(jù)時(shí)完慧,JavaScript 控制臺(tái)會(huì)拋出警告
static propTypes = {
name: React.PropTypes.string.isRequired,
nick: React.PropTypes.number.isRequired,
}
state = {
city: this.props.city,
index:this.props.index,
}
}
導(dǎo)入與導(dǎo)出
ES 6 另一個(gè)讓我覺(jué)得便利的地方就是,新的導(dǎo)出方式剩失。不再像以前一樣屈尼,需要用類似module.exports={xxx}
這樣一點(diǎn)也不美觀的方式導(dǎo)出。并且別名與通配符的使用也很好的防止了變量的沖突與導(dǎo)出拴孤。
//ES5
//接收對(duì)象
var o = require('../xx/xx.js'); // 模塊導(dǎo)入
module.exports={ // 模塊導(dǎo)出
add:add,
sub:sub
}
//ES6
// 按照變量名導(dǎo)入
import {* as all , userInfo脾歧,userToken as uToken,userXXX} form 'test.js';
// 默認(rèn)導(dǎo)入
import xxx form 'test.js'; // 導(dǎo)入用 default 修飾的默認(rèn)對(duì)象
export {
info as userInfo, // 別名
token as userToken,
xxx as userXXX
};
export default { abcde }; // 默認(rèn)導(dǎo)出
塊級(jí)變量let
在這里提出的主要原因是他和我們客戶端常見(jiàn)所理解的let不大一樣演熟,并不是代表常量的意思(swift鞭执,kotelin)。而是代表塊級(jí)變量(js里常量是const)绽媒,生命周期只在代碼塊里有效蚕冬。雖然塊級(jí)變量在很多語(yǔ)言中都已很常見(jiàn),但原js中并沒(méi)有提供塊級(jí)變量的使用是辕。而var是全局可用的囤热。這里提出避免誤用。
// var 容易造成的問(wèn)題
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10,如果把 i 聲明為 let获三,則輸出為6
其他
以下是一些曾令我疑惑的語(yǔ)法旁蔼,不一定屬于ES 6的新特性。但同屬js的語(yǔ)法疙教,因此放在ES 6常見(jiàn)特性的最后棺聊。
// 重點(diǎn)在于 state=...this.props.route,相當(dāng)于把所有命名一致的變量傳入
<React.View title='頁(yè)面' state=...this.props.route />
// 類似的
const {name,type} = this.items;
//等價(jià)于下面
const name = this.items.name;
const name = this.items.type;
坑點(diǎn)匯總
坑點(diǎn)的匯總主要分為兩各部分,框架踩坑與語(yǔ)言特性踩坑贞谓。
框架踩坑
1.接入坑點(diǎn)
因?yàn)镽N的迭代速度有點(diǎn)快限佩,官方文檔往往更不上代碼的變動(dòng)速度。Stack Overflow里面相對(duì)的解決方案也比較少裸弦,且適用性隨版本變化祟同。因此,很多坑不能在網(wǎng)絡(luò)資源中獲得答案理疙,而是要在GitHub里的issue晕城,在對(duì)應(yīng)問(wèn)題中一般都能找到前人的填坑記錄。
其中窖贤,最常見(jiàn)的問(wèn)題便是<jschelpers/...> not find!
砖顷。但在不同版本中贰锁,但原可能不同。
0.45版本
RN 需要引入 BatchedBridge
滤蝠,不然運(yùn)行會(huì)出現(xiàn)類似報(bào)錯(cuò)豌熄。而雖然 0.40版本
下也會(huì)出現(xiàn)這個(gè)問(wèn)題,但原因是 CocoaPod 需要升級(jí)到最新版几睛,把1.1版本升到1.2.1就可以解決了房轿。(其他版本未測(cè)試過(guò))
#根據(jù)實(shí)際路徑修改下面的`:path`
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'DevSupport', # 如果RN版本 >= 0.43,則需要加入此行才能開(kāi)啟開(kāi)發(fā)者菜單
'RCTText',
'RCTNetwork',
'RCTWebSocket',
'BatchedBridge', # 0.45 需要添加這行
'RCTImage',
]
# 如果你的RN版本 >= 0.42.0所森,請(qǐng)加入下面這行
pod "Yoga", :path => "../node_modules/react-native/ReactCommon/yoga"
2.第三方庫(kù)使用
類似的囱持,在使用第三方庫(kù)的時(shí)候,第三方庫(kù)不一定會(huì)隨著RN的跟新做出變化焕济。
React-Native link
這個(gè)方便的命令有時(shí)候用起來(lái)就會(huì)莫名的惱火纷妆。可以的話晴弃,第三方庫(kù)還是通過(guò) CocoaPod 手動(dòng)引入的好掩幢。React-Native link
容易遇到的問(wèn)題一般是找不到<React/xxx.h> not find!
。 遇到這類問(wèn)題上鞠,一些文章會(huì)建議把第三方尖括號(hào)<React/xxx.h>
引用改成引號(hào)"React/xxx.h"
际邻。但這樣改動(dòng)太大,且不便于操作芍阎。在我研究對(duì)比RN
0.39
和0.40
的配置區(qū)別后世曾,發(fā)現(xiàn)主要是xcode 項(xiàng)目配置Alaways Search User Paths
導(dǎo)致的。因此谴咸,在第三方庫(kù)的Header Search Paths
加入CocoaPod的Pod目錄Public里的React文件夾
作為搜索路徑就好了(0.45
在Development文件夾
里)轮听。同時(shí)需要把第三方庫(kù)的Alaways Search User Paths
設(shè)成 YES 。
</br>
語(yǔ)言特性踩坑
1.沒(méi)有宏定義
全局變量無(wú)法當(dāng)做宏定義使用岭佳,沒(méi)有預(yù)編譯血巍。全局變量需要運(yùn)行后才有值,無(wú)法在 import 里面當(dāng)路徑宏使用珊随。
2.引入的資源必須用靜態(tài)路徑
Require 圖片資源時(shí)必須為靜態(tài)路徑述寡,因?yàn)閞equire是在編譯時(shí)期執(zhí)行,而非運(yùn)行時(shí)期執(zhí)行叶洞。這個(gè)動(dòng)作會(huì)發(fā)生在運(yùn)行之前辨赐。如果你使用了變量,打包的時(shí)候變量并不會(huì)有值京办。RN會(huì)給你報(bào)錯(cuò)。
3.編譯檢錯(cuò)
只有語(yǔ)法補(bǔ)全的插件帆焕,即使語(yǔ)法錯(cuò)誤惭婿,運(yùn)行后才能得知運(yùn)行結(jié)果不恭。
例如:
if(...) A(); else (error code)...;
的代碼。測(cè)試時(shí)财饥,如果不走else的判斷换吧,連語(yǔ)法錯(cuò)誤都不會(huì)檢查。如果是夾雜多個(gè)的判斷钥星,進(jìn)行語(yǔ)法檢錯(cuò)也是需要全部走完沾瓦。
4.弱類型語(yǔ)言要注意語(yǔ)境
js為弱類型語(yǔ)言且空類型多(如下方第5點(diǎn)所示)。js 判斷時(shí)會(huì)出現(xiàn)強(qiáng)類型語(yǔ)言考慮之外的情況谦炒,編寫的代碼在類型轉(zhuǎn)換的思考上要更為周全贯莺。也會(huì)出現(xiàn)一些感官上覺(jué)得奇怪的代碼,如下所示:
return !!b; // 兩次強(qiáng)轉(zhuǎn),保證變量返回是 Boolean 類型`
return a==1?a:0 // 沒(méi)有寫成a==1?1:0宁改,可能業(yè)務(wù)上結(jié)果會(huì)返回字符串和數(shù)字類型缕探,不能確定
5.類型較多對(duì)比時(shí)容易弄混
容易弄混的有0、-0还蹲、null爹耗、""、false谜喊、undefined或者NaN潭兽。
其中:
- undefined:未定義或未賦值的變量
- null :特殊的object類型,為空對(duì)象斗遏,和swift 的拆包的空盒模型類似山卦。
- NaN:特殊的number,類似盒子模型拆盒后為空最易,而不為0怒坯。
以下為容易混淆的對(duì)比點(diǎn):
6.this的作用域問(wèn)題
this的指向場(chǎng)景會(huì)根據(jù)使用情況變化。而對(duì)于不熟悉語(yǔ)言的客戶端開(kāi)發(fā)者來(lái)說(shuō)藻懒,會(huì)經(jīng)常對(duì)指向?qū)ο箦e(cuò)誤的操作剔猿。this的指向場(chǎng)景分別四類:
- 有對(duì)象就指向調(diào)用對(duì)象
- 沒(méi)調(diào)用對(duì)象就指向全局對(duì)象
- 用new構(gòu)造就指向新對(duì)象
- 通過(guò) apply 或 call 或 bind 來(lái)改變 this 的所指。
這個(gè)問(wèn)題的解決嬉荆,最主要還是了解其中的機(jī)制归敬。可以參考這篇文章《作用域與閉包:this鄙早,var汪茧,(function () {})》。
附錄
數(shù)值的常見(jiàn)用法和結(jié)果限番。
用 法 結(jié) 果
Number(false) 0
Number(true) 1
Number(undefined) NaN
Number(null) 0
Number( "5.5 ") 5.5
Number( "56 ") 56
Number( "5.6.7 ") NaN
Number(new Object()) NaN
Number(100) 100
parseInt("AF", 16); //returns 175
parseInt("10", 2); //returns 2
parseInt("10", 8); //returns 8
parseInt("10", 10); //returns 10
Boolean(""); //false – empty string
Boolean("hi"); //true – non-empty string
Boolean(100); //true – non-zero number
Boolean(null); //false - null
Boolean(0); //false - zero
Boolean(new Object()); //true – object
參考資料:
[1] React-Native官方文檔