React Native的極簡手冊(cè)

安裝入門

安裝入門可以參考:React Native官方文檔氓英。

NodeJS知識(shí)儲(chǔ)備:參考《NodeJS入門》。(尊重知識(shí)鹦筹,請(qǐng)購買原版)铝阐。

書籍:《React Native入門與實(shí)戰(zhàn)》

代碼示例:30天學(xué)習(xí)React Native教程

看到這里,對(duì)React Native的使用就有了一些認(rèn)識(shí)了铐拐。

React and React Native and NodeJS

React是由Facebook開發(fā)出來的用于開發(fā)用戶交互界面的JS庫徘键。其源碼由Facebook和社區(qū)優(yōu)秀的程序員維護(hù)。React帶來了很多新的東西遍蟋,例如組件化吹害、JSX、虛擬DOM等虚青。其提供的虛擬DOM使得我們渲染組件呈現(xiàn)非常之快它呀,讓我們從頻繁操作DOM的繁重工作之中解脫。它做的工作更多偏重于MVC中的V層棒厘,結(jié)合其它如Flux等一起纵穿,你可以非常容易構(gòu)建強(qiáng)大的應(yīng)用。

React的世界里绊谭,一切都是組件政恍。你可以構(gòu)建任何直接的HTML沒有的組件,例如下拉菜單达传、導(dǎo)航菜單等篙耗。同時(shí),組件里也可以包含其它組件宪赶。每一個(gè)組件都有一個(gè)render方法宗弯,用于呈現(xiàn)該組件。同時(shí)搂妻,每一個(gè)組件都有屬于自己的scope蒙保,從而與其它的組件界定開來,用于構(gòu)建屬于該組件的方法欲主,以方便復(fù)用邓厕。JSX是基于JS的擴(kuò)展,它允許你在JS里直接寫HTML的代碼扁瓢,而不用像我們過去一樣要想在JS里寫HTML不得不拼接一大堆的字符串详恼。React不直接操作DOM,頻繁的操作DOM會(huì)非常影響性能和體驗(yàn)引几。React將DOM結(jié)構(gòu)儲(chǔ)存在內(nèi)存中昧互,與render方法的返回值進(jìn)行比較,通過其自由的diff算法計(jì)算出不同的地方,然后反應(yīng)到真實(shí)的DOM當(dāng)中敞掘。也就是說叽掘,大多數(shù)情況我們渲染組件、更改組件狀態(tài)等都是操作的虛擬DOM玖雁,只有在有所改變的情況下更扁,才會(huì)反應(yīng)到真實(shí)的DOM當(dāng)中。React Native基于ReacJS赫冬,把 React 編程模式的能力帶到移動(dòng)開發(fā),用來開發(fā)iOS和Android原生應(yīng)用.

NodeJs 是基于JavaScript的,可以做為后臺(tái)開發(fā)的語言. 提供了很多系統(tǒng)級(jí)的API疯潭,如文件操作、網(wǎng)絡(luò)編程等. 用事件驅(qū)動(dòng), 異步編程,主要是為后臺(tái)網(wǎng)絡(luò)服務(wù)設(shè)計(jì).React Native 借助 Node.js面殖,即 JavaScript 運(yùn)行時(shí)來創(chuàng)建 JavaScript 代碼竖哩。

總結(jié)來說,React Native使用NodeJS來做系統(tǒng)處理脊僚,使用React來渲染相叁。

構(gòu)建原理

在AppDelegate.m里,找到

application:didFinishLaunchingWithOptions:

在這個(gè)方法中辽幌,主要做了幾件事:

  • 定義了 JS 代碼所在的位置增淹,它在 dev 環(huán)境下是一個(gè) URL,通過 development server 訪問乌企;在生產(chǎn)環(huán)境下則從磁盤讀取虑润,當(dāng)然前提是已經(jīng)手動(dòng)生成過了 bundle 文件;
  • 創(chuàng)建了一個(gè) RCTRootView 對(duì)象加酵,該類繼承于 UIView拳喻,處理程序所有 View 的最外層;
  • 調(diào)用 RCTRootView 的 initWithBundleURL 方法猪腕。在該方法中冗澈,創(chuàng)建了 bridge 對(duì)象。顧名思義陋葡,bridge 起著兩個(gè)端之間的橋接作用亚亲,其中真正工作的是類就是大名鼎鼎的 RCTBatchedBridge。RCTBatchedBridge 是初始化時(shí)通信的核心腐缤,我們重點(diǎn)關(guān)注的是 start 方法捌归。在 start 方法中,會(huì)創(chuàng)建一個(gè) GCD 線程岭粤,該線程通過串行隊(duì)列調(diào)度了以下幾個(gè)關(guān)鍵的任務(wù)惜索。

RCTRootView 用于加載 JavaScript 應(yīng)用以及渲染最后的視圖的。當(dāng)應(yīng)用開始運(yùn)行的時(shí)候绍在,RCTRootView將會(huì)從以下的URL中加載應(yīng)用:

http://localhost:8081/index.ios.bundle

重新調(diào)用了你運(yùn)行這個(gè)App時(shí)打開的終端窗口门扇,它開啟了一個(gè) packager 和 server 來處理上面的請(qǐng)求。在 Safari 中打開那個(gè) URL偿渡;你將會(huì)看到這個(gè) App 的 JavaScript 代碼臼寄。你也可以在 React Native 框架中找到你的代碼。當(dāng)你的App開始運(yùn)行了以后溜宽,這段代碼將會(huì)被加載進(jìn)來吉拳,然后 JavaScriptCore 框架將會(huì)執(zhí)行它。在程序里适揉,它將會(huì)加載 功能 組件留攒,然后構(gòu)建出原生的 UIKit 視圖。JavaScript應(yīng)用運(yùn)行在模擬器上嫉嘀,使用的是原生UI炼邀,沒有任何內(nèi)嵌的瀏覽器。應(yīng)用程序會(huì)使用 React.createElement 來構(gòu)建應(yīng)用 UI ,React會(huì)將其轉(zhuǎn)換到原生環(huán)境中剪侮。

當(dāng) UI 渲染出來后,render 方法會(huì)返回一顆視圖渲染樹,并與當(dāng)前的 UIKit 視圖進(jìn)行比較拭宁。這個(gè)稱之為 reconciliation 的過程的輸出是一個(gè)簡單的更新列表, React 會(huì)將這個(gè)列表應(yīng)用到當(dāng)前視圖。只有實(shí)際改變了的部分才會(huì)重新繪制瓣俯。即ReactJS獨(dú)特的——virtual-DOM(文檔對(duì)象模型,一個(gè)web文檔的視圖樹)和 reconciliation概念杰标。

組件的生命周期

組件的生命周期分成三個(gè)狀態(tài):

Mounting:已插入真實(shí) DOM
Updating:正在被重新渲染
Unmounting:已移出真實(shí) DOM

React 為每個(gè)狀態(tài)都提供了兩種處理函數(shù),will 函數(shù)在進(jìn)入狀態(tài)之前調(diào)用彩匕,did 函數(shù)在進(jìn)入狀態(tài)之后調(diào)用腔剂,三種狀態(tài)共計(jì)五種處理函數(shù)。

componentWillMount()
componentDidMount()
componentWillUpdate(object nextProps, object nextState)
componentDidUpdate(object prevProps, object prevState)
componentWillUnmount()

此外驼仪,React 還提供兩種特殊狀態(tài)的處理函數(shù)掸犬。

componentWillReceiveProps(object nextProps):已加載組件收到新的參數(shù)時(shí)調(diào)用
shouldComponentUpdate(object nextProps, object nextState):組件判斷是否重新渲染時(shí)調(diào)用

這些方法的詳細(xì)說明,可以參考官方文檔绪爸。

另外一個(gè)需要關(guān)注的點(diǎn)是登渣,組件的style屬性的設(shè)置方式不能寫成

style="opacity:{this.state.opacity};"

而要寫成

style={{opacity: this.state.opacity}}

這是因?yàn)?React 組件樣式是一個(gè)對(duì)象,所以第一重大括號(hào)表示這是 JavaScript 語法毡泻,第二重大括號(hào)表示樣式對(duì)象胜茧。

JS 和 Native 交互

xcode啟動(dòng)后會(huì)執(zhí)行 ../node_modules/react-native/packager/react-native-xcode.sh文件。腳本中主要是讀取 Xcode 帶過來的環(huán)境變量仇味,同時(shí)加載 nvm 包使得 Node.js 環(huán)境可用呻顽,最后執(zhí)行 react-native-cli 的命令:

$NODE_BINARY "$REACT_NATIVE_DIR/local-cli/cli.js" bundle \\
  --entry-file index.ios.js \\
  --platform ios \\
  --dev $DEV \\
  --bundle-output "$DEST/main.jsbundle" \\
  --assets-dest "$DEST"

通過此處,index.ios.js和main.jsbundle就可以使用了丹墨。

通過../react-native/local-cli/cli.js 中的 run 方法廊遍,進(jìn)入/bundle/bundle.js ,由此進(jìn)入了 /bundle/buildBundle.js。從js腳本中可以看出大體做了下面的工作:

  • 從入口文件開始分析模塊之間的依賴關(guān)系贩挣;
  • 對(duì) JS 文件轉(zhuǎn)化喉前,比如 JSX 語法的轉(zhuǎn)化等没酣;
  • 把轉(zhuǎn)化后的各個(gè)模塊一起合并為一個(gè) bundle.js。

React Native對(duì)模塊的分析和編譯做了不少優(yōu)化卵迂,大大提升了打包的速度裕便,這樣能夠保證在 liveReload 時(shí)用戶及時(shí)得到響應(yīng)。

在應(yīng)用程序啟動(dòng)之后见咒,其中的 didFinishLaunchingWithOptions 方法會(huì)被調(diào)用偿衰,通過上面的分析,我們可以看到自己實(shí)現(xiàn)的頁面就被加入到應(yīng)用程序中了改览。JS 引擎下翎,在調(diào)試環(huán)境下,對(duì)應(yīng)的 Executor 為 RCTWebSocketExecutor宝当,它通過 WebSocket 連接到 Chrome 中视事,在 Chrome 里運(yùn)行 JS;在生產(chǎn)環(huán)境下,對(duì)應(yīng)的 Executor 為 RCTContextExecutor庆揩,這應(yīng)該就是傳說中的 javascriptcore郑口。

Native 調(diào)用 JS 是通過發(fā)送消息到 Chrome 觸發(fā)執(zhí)行、或者直接通過 javascriptcore 執(zhí)行 JS 代碼的盾鳞。在 JS 端調(diào)用 Native 一般都是直接通過引用模塊名犬性,JS 把(調(diào)用模塊、調(diào)用方法腾仅、調(diào)用參數(shù)) 保存到隊(duì)列中乒裆;Native 調(diào)用 JS 時(shí),順便把隊(duì)列返回過來推励;Native 處理隊(duì)列中的參數(shù)鹤耍,同樣解析出(模塊、方法验辞、參數(shù))稿黄,并通過 NSInvocation 動(dòng)態(tài)調(diào)用;Native方法調(diào)用完畢后跌造,再次主動(dòng)調(diào)用 JS杆怕。JS 端通過 callbackID,找到對(duì)應(yīng)JS端的 callback壳贪,進(jìn)行一次調(diào)用陵珍。兩端都保存了所有暴露的 Native 模塊信息表作為通信的基礎(chǔ)。

JS不會(huì)主動(dòng)傳遞數(shù)據(jù)給OC违施,在調(diào)OC方法時(shí)互纯,會(huì)把ModuleID,MethodID等數(shù)據(jù)加到一個(gè)隊(duì)列里,等OC過來調(diào)JS的任意方法時(shí)磕蒲,再把這個(gè)隊(duì)列返回給OC留潦,此時(shí)OC再執(zhí)行這個(gè)隊(duì)列里要調(diào)用的方法只盹。native開發(fā)里,只在有事件觸發(fā)的時(shí)候才執(zhí)行代碼兔院。在React Native里殖卑,事件發(fā)生時(shí)OC都會(huì)調(diào)用JS相應(yīng)的模塊方法去處理,處理完這些事件后再執(zhí)行JS想讓OC執(zhí)行的方法秆乳,而沒有事件發(fā)生的時(shí)候,是不會(huì)執(zhí)行任何代碼的钻哩。

另外屹堰,一個(gè) Native 模塊如果想要暴露給 JS,需要在聲明時(shí)顯示地調(diào)用 RCT_EXPORT_MODULE街氢。宏定義了 load 方法扯键,該方法會(huì)自動(dòng)被調(diào)用,在方法中對(duì)當(dāng)前類進(jìn)行注冊(cè)珊肃。模塊如果要暴露出指定的方法荣刑,需要通過 RCT_EXPORT_METHOD 宏進(jìn)行聲明。

總結(jié):整個(gè)啟動(dòng)過程就是伦乔,JS端先把代碼大包成bundle.js傳到Native端的主函數(shù)厉亏,主函數(shù)創(chuàng)建RCTRootView.在RCTRootView里使用GCD掃描暴露的模塊,創(chuàng)建JS引擎烈和,將模塊信息序列化為json.此時(shí)加載JS代碼爱只,在JS引擎中執(zhí)行bundle.js,將json對(duì)象反序列化保存為NativeModules對(duì)象。


JS 和 Native 的交互過程中招刹, RCTBatchedBridge 在兩端通信過程中扮演了重要的角色恬试。

//RCTBatchedBridge.m
- (void)start
{
  dispatch_queue_t bridgeQueue = dispatch_queue_create("com.facebook.react.RCTBridgeQueue", DISPATCH_QUEUE_CONCURRENT);

  // 異步的加載打包完成的js文件,也就是main.jsbundle疯暑,如果包文件在本地則直接加載训柴,否則根據(jù)URL通過NSURLSession方式去下載
  [self loadSource:^(NSError *error, NSData *source) {}];

  // 同步初始化需要暴露給給js層的native模塊
  [self initModules];

  //異步初始化JS Executor,也就是js引擎
  dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
[weakSelf setUpExecutor];
  });

  //異步獲取各個(gè)模塊的配置信息
  dispatch_group_async(setupJSExecutorAndModuleConfig, bridgeQueue, ^{
config = [weakSelf moduleConfig];
  });

  //獲取各模塊的配置信息后妇拯,將這些信息注入到JS環(huán)境中
  [self injectJSONConfiguration:config onComplete:^(NSError *error) {}];

  //開始執(zhí)行main.jsbundle
  [self executeSourceCode:sourceCode];
}

js為了橋接native層也引入了BatchedBridge幻馁,BatchedBridge是MessageQueue的一個(gè)實(shí)例,而且是全局唯一的一個(gè)實(shí)例越锈。:

//BatchedBridge.js
const MessageQueue = require('MessageQueue');
const BatchedBridge = new MessageQueue(
  __fbBatchedBridgeConfig.remoteModuleConfig,
  __fbBatchedBridgeConfig.localModulesConfig,
);
//將BatchedBridge添加到j(luò)s的全局global對(duì)象中宣赔,
Object.defineProperty(global, '__fbBatchedBridge', { value: BatchedBridge });
module.exports = BatchedBridge;

__fbBatchedBridgeConfig是一個(gè)全局的js變量,__fbBatchedBridgeConfig.remoteModuleConfig就是之前我們?cè)趎ative層導(dǎo)出的模塊配置表.

messageQueue保存著js跟native的模塊交互的所有信息瞪浸。<code>_genModules</code>方法儒将,該方法會(huì)根據(jù)config解析每個(gè)模塊的信息并保存到this.RemoteModules中.<code>_genModules</code>會(huì)歷遍所有的remoteModules,根據(jù)每個(gè)模塊的配置信息(如何生成配置信息下面會(huì)提到)和module索引ID來創(chuàng)建每個(gè)模塊.

react為了性能的優(yōu)化,當(dāng)js兩次調(diào)用方法的間隔小于MIN_TIME_BETWEEN_FLUSHES_MS(5ms)時(shí)間对蒲,會(huì)將調(diào)用信息先緩存到_queue中钩蚊,等待下次在一并提交給native層執(zhí)行.

Navigator組件到NavigationExperimental組件

Navigator and NavigatorIOS兩個(gè)都是有狀態(tài)(即保存各個(gè)導(dǎo)航的序順)的組件拢蛋,允許你的APP在多個(gè)不同的場(chǎng)景(屏幕)之間管理你的導(dǎo)航。這兩個(gè)導(dǎo)航管理了一個(gè)路由棧(route stack)技俐,這樣就允許我們使用pop(), push()和replace()來管理狀態(tài)昼牛。NavigatorIOS是使用了iOS的 UINavigationController類,而Navigator都是基于Javascript蝠咆。 Navigator適用于兩個(gè)平臺(tái)踊东,而NavigatorIOS只能適用于iOS. 如果在一個(gè)APP中應(yīng)用了多個(gè)導(dǎo)航組件(Navigator and NavigatorIOS一起使用). 那么在兩者之間進(jìn)行導(dǎo)航過渡,會(huì)變得非常困難.

NavigationExperimental以一種新的方法實(shí)現(xiàn)導(dǎo)航邏輯刚操,這樣允許任何的視圖都可以作為導(dǎo)航的視圖 闸翅。它包含了一個(gè)預(yù)編異的組件NavigationAnimatedView來管理場(chǎng)景間的動(dòng)畫。它內(nèi)部的每一個(gè)視圖都可以有自己的手勢(shì)和動(dòng)畫菊霜。

React Native項(xiàng)目已經(jīng)不再維護(hù)Navigator組件而全面轉(zhuǎn)向NavigationExperimental組件了坚冀。NavigationExperimental改進(jìn)了Navigator組件的一下幾個(gè)方面:

  • 單向數(shù)據(jù)流, 它使用reducers 來操作最頂層的state 對(duì)像,而在Navigator中鉴逞,當(dāng)你在子導(dǎo)航頁中记某,不可能操作到app最初打開頁面時(shí)的state對(duì)像,除非构捡,一級(jí)級(jí)的通過props傳遞過方法名或函數(shù)名液南,然后在子頁面中調(diào)用這些方法或者函數(shù),來修改某個(gè)頂層的數(shù)據(jù)勾徽。

  • 為了允許存在本地和基于 js的導(dǎo)航視圖贺拣,導(dǎo)航的邏輯和路由,必須從視圖邏輯中獨(dú)立出來捂蕴。

  • 改進(jìn)了切換時(shí)的場(chǎng)景動(dòng)畫譬涡,手勢(shì)和導(dǎo)航欄

NavigationExperimental的使用:
實(shí)現(xiàn)方案可參考此處

具體使用也可以看這里.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末啥辨,一起剝皮案震驚了整個(gè)濱河市涡匀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌溉知,老刑警劉巖陨瘩,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異级乍,居然都是意外死亡舌劳,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門玫荣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來甚淡,“玉大人,你說我怎么就攤上這事捅厂」嶝裕” “怎么了资柔?”我有些...
    開封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長撵割。 經(jīng)常有香客問我贿堰,道長,這世上最難降的妖魔是什么啡彬? 我笑而不...
    開封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任羹与,我火速辦了婚禮,結(jié)果婚禮上庶灿,老公的妹妹穿的比我還像新娘纵搁。我一直安慰自己,他們只是感情好跳仿,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開白布诡渴。 她就那樣靜靜地躺著捐晶,像睡著了一般菲语。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上惑灵,一...
    開封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天山上,我揣著相機(jī)與錄音,去河邊找鬼英支。 笑死佩憾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的干花。 我是一名探鬼主播妄帘,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼池凄!你這毒婦竟也來了抡驼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤肿仑,失蹤者是張志新(化名)和其女友劉穎致盟,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尤慰,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡馏锡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了伟端。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杯道。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖责蝠,靈堂內(nèi)的尸體忽然破棺而出蕉饼,到底是詐尸還是另有隱情虐杯,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布昧港,位于F島的核電站擎椰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏创肥。R本人自食惡果不足惜达舒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望叹侄。 院中可真熱鬧巩搏,春花似錦、人聲如沸趾代。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撒强。三九已至禽捆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間飘哨,已是汗流浹背胚想。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芽隆,地道東北人浊服。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像胚吁,于是被迫代替她去往敵國和親牙躺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容