基於雨點兒網,分享react-native開發android app的方法。
上篇文章《零基礎用react-native開發android app》介紹了RN(react-native)的一些基本概念以及開發流程,這篇文章主要結合個人開源項目raindrop-app跟你們交流下我本身的代碼組織以及開發過程當中遇到的一些問題。css
. ├── components //組成應用的各個組件 │ ├── Routers.android.js //每一個組件若實現不同,分爲android的實現和ios的實現。 │ ├── Routers.ios.js │ ├── common //公共組件 │ ├── issues //議題頁面 │ ├── navigation //導航組件,android用側邊欄,ios準備用tab │ └── project //項目頁面 └── network //網絡服務 └── DataService.js
我本身的代碼所有放在src目錄下,這樣寫代碼過程當中搜索啊什麼操做比較方便,從邏輯上也比較清晰。html
react的應用,是用自定義組件或原生組件層層嵌套而成的。所以我將整個應用劃分爲組件部分(組成各個頁面)和一些其餘服務(目前比較簡單,只抽象出發get請求的網絡服務)。react
components內,根據本身的業務邏輯進行抽象,把整個應用劃分爲層層嵌套的組件,目錄結構的組織形式基本就是我頁面的組織形式。若是有一些比較通用的功能,能夠提取成公共組件,我放在common目錄下。android
每一個組件若是ios和android的實現不太同樣,則建立兩個文件,如Routers.android.js和Routers.ios.js。ios
根組件:
我定義了一個Routers組件,做爲整個app的根組件。Router組件實際上包裝的官方的Navigator組件,主要做用:git
負責整個app的全部路由,當使用navigator去跳轉路由時,會最終進入renderScene函數來渲染不一樣的頁面。github
提供了默認router,整個程序啓動時,默認加載頁面ProjectList。web
各個頁面:不一樣路由對應不一樣的頁面,如Routers的renderScene函數中,每一個if分支是一個頁面。這些頁面實際上就是一個個導出的組件。好比ProjectList組件是用來作項目列表的,但他自身又包含了一個用來渲染每一個項目單元格的projectCell組件。如此,全部組件都是對上層呈現成一個統一的組件接口,對下層本身去組裝多個不一樣組件,最終造成一個模塊化的統一的app。chrome
組件之間的關聯:
組件之間常常會發生關聯。我本身用到了如下狀況:segmentfault
父改變子:
子經過state對外提供接口,父能夠經過setState去改變子的狀態,並讓子從新渲染。state是React的一個很重要的概念。在組件上能夠設一些屬性,這些屬性都有一個初始狀態,而後用戶的操做產生交互,只要是用setState去觸發這個組件狀態變化,則會觸發這個組件從新渲染 UI 。
父直接調用子導出的方法,好比官方組件DrawerLayoutAndroid
提供的openDrawer
方法。可使用react的refs機制去調用。好比我在NavTab組件的openNavDrawer函數中,以this.refs['drawer'].openDrawer();
這樣的函數方式去調用。那麼如何像這種方式導出本身的方法供父組件直接以函數方式調用?注意導出的方法必須是做爲類方法就能夠了,好比openNavDrawer這個函數就是導出給父用的。
子調用父:
這其實有點相似是反向依賴的設計模式。就是子提供觸發回調的接口,可是到底是觸發後執行什麼,子並不關心。好比我封裝的NavToolbar(就是不少界面上面的工具條)組件的onClicked方法。
不少地方的按鈕都是返回上一級。 <NavToolbar ... onClicked={() => {this.props.nav.pop();}} /> 可是最底層的幾個界面上的按鈕,換成了彈出側面導航條,以供切換。 <NavToolbar ... onClicked={this.onToolbarClicked} /> 對於這種狀況,導航條要想抽象成公共的組件,他就不能依賴於他的父到底是哪一個界面。觸發的具體動做就須要經過回調注入進來,這時就用這種方式。
兄弟關係:
在共同的父中組合上面兩種狀況就能夠了。好比ProjectList.android.js中
onToolbarClicked: function (){ this.refs['navTab'].openNavDrawer(); }, <NavTab ref='navTab' nav={this.props.nav}> <NavToolbar icon={"ic_menu_white"} title={'項目'} onClicked={this.onToolbarClicked} /> {content} </NavTab>
其餘狀況:
參考這篇文章,不過目前我還沒用到這種毫無關係的事件觸發,因此還沒有研究。
chrome調試:
安裝react dev的chrome官方插件。在手機上設置host的ip,點擊start chrome debugging。 chrome會自動跳轉到調試地址,在瀏覽器上打開調試窗口,會發現裏面多了一個react頁籤。
inspect元素:
在模擬器中打開inspect element面板,點擊模擬器中的元素,chrome會跳轉到對應dom。
槽點:
在瀏覽器改動css後,模擬器的佈局不跟着更新。注意每一個dom都有個RN的包裹,須要更改這個以RCT開頭的包裹元素。參考issue。
瀏覽器的dom和手機上的元素位置對不許確。我有時會分不清哪一個dom對應我屏幕哪一塊。
調試常常失效,調試窗口的react頁籤動不動就找不到了,我大部分時候是直接改代碼,在模擬器看效果的。
模擬器中的程序常常崩潰,代碼語法有低級錯誤,一但reload js
,程序就有很大機率崩潰,須要react-native run-android
從新開始。
換工程運行項目,react-native run-android 前最好關下後臺,不然兩個項目會互相影響。
出錯提示很不完善。
好比有時我會將<View>
誤寫成<view>
,或者忘記關閉標籤。而這些低級錯誤,RN裏面每每會很是難排除,提示每每都很奇怪,我都是靠走讀代碼發現。
好比有一次,我看了ECMAScript 6 Features的語法後,將DataService中var SERVER = 'http://www.yudianer.com/api';
這句改爲了const SERVER = 'http://www.yudianer.com/api';
,當時沒發現什麼問題。但後面發現了奇怪的問題,只有在瀏覽器調試的時候,app才能正常運行,不然什麼也不顯示,並且沒有任何提示。最後打包運行無數次都沒反應,只能一點一點註釋代碼排除,才發現是我用了ECMAScript 6 Features,卻沒有配置。。。
RN的有些組件有些限制,每每是後知後覺。例如:
DrawerLayoutAndroid這個組件外面不能再包一個<View></View>
。若是你不幸這麼作了,會整個頁面不顯示了,而沒有任何提示。。。
若是ListView包在一個View中,那麼外面這個View須要設置style={flex: 1}。不然ListView將不能滾動。
當遇到這種問題,最好去google一下,或去github看下有沒有相似的議題。實在不行就經過註釋代碼的方法排除。
JSX的語法常常搞錯,跟通常的模板語言不太同樣。好比:
renderProject: function(project){ return( <ProjectCell onSelect={() => this.selectProject(project)} project={project}/> ); },
我會常常忘記這是個函數,而直接寫成:
renderProject: function(project){ <ProjectCell onSelect={() => this.selectProject(project)} project={project}/> },
這看上去沒什麼,問題是這種相似錯誤的提示很奇怪,很差定位。
RN在android上確實不太完善,調試工具,錯誤提示,文檔等都不是很友好。但去學習下仍是挺酷的,並且在facebook竭盡全力的推進,相信會愈來愈完善的。