Make a Mobile App with ReactJS in 30 Minuteshtml
Ken Wheeler (@ken_wheeler)前端
React 能讓前端開發者之前所未有的方式來構建應用。它有許多好處:好比單向數據流,簡單的組件生命週期,聲明組件之類的。node
Reapp 是最近發佈的基於 React 的一個框架。它是一個專為性能和生產效率而生的移動應用平臺。你能夠把它看作是一個精心優化過的 UI 組件,並且帶有一套很好的編譯系統,以及許多有用工具,能讓你輕鬆構建你的應用。react
Reapp 給我們提供了一堆很棒的東西:git
#我們的目標github
為了演示 Reapp,我們將創建一個應用,能夠讓你檢索 Flickr API,然後把結果在圖片庫裏面顯示出來。跟著這個教程作的話,大約也就會佔用你不到半個小時的時間!chrome
#開始npm
首先你得安裝有 node,然後我們來執行 sudo npm install -g reapp
安裝 Reapp CLI。安裝完之後,執行 reapp new flickrapp
。最後, cd flickrapp
再 reapp run
。json
你就會看到以下:api
在你的瀏覽器中打開 localhost:3010,你就會看到默認的 Reapp 應用啦:
小提示:在 Chrome 的開發者工具裏面,能夠打開模擬移動設備,以移動應用的方式來查看你的應用。
棒!現在我們已經徹底跑起來一個用 Reapp 組件創建的全 React 棧應用。來看看文件結構:
<!-- lang: js --> /app components/ home/ Sub.jsx App.jsx Home.jsx app.js routes.js /assets
Reapp 給我們搭建了一些演示的頁面,正如你看到的,放在 . app/components
裏面,一個典型 REST 結構。./app/app.js
是我們應用的入口,它會加載 Reapp 和執行我們的路由,路由會在 ./app/routes.js
裏配置。
##從視圖開始
我們生成一個應用,不過 Reapp 生成的應用包括了許多內嵌頁面,我們不須要那麼多,只要單頁就好。讓我們先把它弄簡單點。在 routes.js
裏面,我們能夠把它改爲這樣:
<!-- lang: js --> module.exports = ({ routes, route }) => routes(require, route('app', '/', { dir: '' }) );
這樣指定了根路由 (http://localhost:3010) 到 app
,這樣 Reapp 路由器會自動的查找 ./components/App.jsx
。
現在我們能夠放心的把 Home.jsx
和 home/Sub.jsx
文件刪了,因為我們不須要多視圖。當然你也能夠留著它們,萬一你想之後要用呢。
在 App.jsx
文件裏面,我們能夠把它這樣簡化:
<!-- lang: js --> import React from 'react'; import View from 'reapp-ui/views/View'; export default React.createClass({ render() { var { photos } = this.state; return ( <View title="Flickr Search" styles={{ inner: { padding: 20 } }}> <p>Hello World</p> </View> ); } });
若是你刷新一下,你就能夠看到界面變成了一個空的視圖加上頂部的一條新標題"Flickr Search"。
##從 Flickr 拿數據
現在我們有了界面可是沒邏輯。在我們把按鈕和顯示照片連動起來以前,當然要用 React 方式先從 Flickr 拿到照片數據。首先,你得有個 Flickr 帳號,和 API key。沒有?點這裏。
填好它(沒有帳號你就先註冊),把它們給你的 Public Key 拷貝到 App.jsx
保存成靜態量。你還須要檢索照片用的 URL,你能夠在 API explorer 找出來,太麻煩我直接給你把 https://www.flickr.com/services/api explore/flickr.photos.search 。
看起來文件就是這樣:
<!-- lang: js --> const key = '__YOUR_KEY_HERE__'; const base = 'https://api.flickr.com/services/rest/?api_key=${key}&format=rest&format=json&nojsoncallback=1';
那個 "YOUR_KEY_HERE" 是給你放 key 的地方。
注意: const
是下一代 JavaScript 的新特性,也就是 ES6。它看起來像變量,不過一但設置好了之後就不會改變了。我們應該怎麼在應用中調用它? Reapp 有內建的 [Webpack][13]
編譯系統,支持 ES6 新特性!
下面,給我們的 React 類定義 getInitialState()
,這樣我們的組件能夠跟蹤我們拿到的照片數據。我們把它做為第一個屬性加到 React.createClass
後面。因為我們把照片保存到一個列表中,因此要有一個列表:
<!-- lang: js --> getInitialState() { return { photos: [] } },
這樣我們就能夠在渲染方法裏面,訪問 this.state.photos
了。在 UI 裏面,我們須要一個按鈕和一個關鍵字輸入框:
<!-- lang: js --> import Button from 'reapp-ui/components/Button'; import Input from 'reapp-ui/components/Input';
然後更新 render()
方法:
<!-- lang: js --> render() { var { photos } = this.state; return ( <View title="Flickr Search"> <Input ref="search img-responsive" /> <Button onTap={this.handleSearch}>Search Images</Button> <div className="verticalCenter"> {!photos.length && <p>No photos!</p> } </div> </View> ); }
然後你接滿就變成這樣:
簡單到爆!不過還是有一些須要值得注意的地方。首先,看到輸入框的 ref 屬性 沒有?Ref 是 reference 的縮寫,用來讓我們來在 class 中跟蹤 DOM 元素的。我們之後會用它來拿界面上輸入的值。
還有,請看 div 上的 className="verticalCenter"
。兩點:因為我們是用 JSX 編譯成 JS 對象(更詳細的看這裏) ,我們不能用普通的 class
屬性,因此我們用 className
來設置 class。另外 verticalCenter
屬性是 Reapp 提供的,它會把元素放到我們頁面的中間。
最後,看到按鈕上的 onTap
屬性沒有?它指向 this.handleSearch
。不過我們還沒有 handleSearch 方法。 React 須要它,好了我們來完成它吧。首先, npm install --save superagent
,安裝我們須要的 Superagent 庫。然後導入:
<!-- lang: js --> import Superagent from 'superagent';
然後定義 handleSearch:
<!-- lang: js --> handleSearch() { let searchText = this.refs.search.getDOMNode().value; Superagent .get(`${base}&method=flickr.photos.search&text=${searchText}&per_page=10&page=1`, res => { if (res.status === 200 && res.body.photos) this.setState({ photos: res.body.photos.photo }); }); },
幾個新的注意點:
this.refs.search.getDOMNode()
返回輸入的 DOM 節點,也就是我們以前放了 "search" ref 的節點${base}
抓取我們定義在 constant 中的 URLthis.setState
會幫我們拿到照片, 存在我們以前定義在 getInitialState
裏的 this.state.photos
數組##顯示 Flickr 照片
現在我們拿到了 Flickr 照片,把它們放到了 state 中。最後一步是顯示了。你能夠加一句 console.log 到 render 方法第一行看看 Flick 給你返回了什麼:
<!-- lang: js --> render() { console.log(this.state.photos); // ... rest of render }
在控制檯你會看到 Flickr 給你返回了一個對象,裏面有許多屬性,這些屬性取決於你的請求 URL。你能夠在這個幫助頁面看看應該怎樣處理 flickr 的 URL。
你看我是這樣來處理個人 URL 的,我放了一個簡單的方法在類裏面:
<!-- lang: js --> getFlickrPhotoUrl(image) { return `https://farm${image.farm}.staticflickr.com/${image.server}/${image.id}_${image.secret}.jpg`; },
這個方法拿到我們的 Flickr 對象,把它轉化成我們須要顯示的 URL 。下面,我們來編輯 handleSearch,setState
調用:
<!-- lang: js --> this.setState({ photos: res.body.photos.photo.map(this.getFlickrPhotoUrl) });
map
方法會遍歷我們的照片對象,然後把它們傳給 getFlickrPhotoUrl,轉換成 URL。然後就能夠顯示啦!
我們把 reapp 的 Gallery 組件到進來:
<!-- lang: js --> import Gallery from 'reapp-ui/components/Gallery';
在 render 方法裏面, 在 <p>No photos found!</p>
下面:
<!-- lang: js --> {!!photos.length && <Gallery images={photos} width={window.innerWidth} height={window.innerHeight - 44} /> }
Gallery 組件會把這三個屬性拿到,然後全屏模式顯示圖片,你能夠左右滑動切換它們。到這裏,我們已經完成我們的應用啦。在你的瀏覽器來看看它是怎樣運做的吧。
注意:為什麼要 window.innerHeight -44
?因為我們設置了應用的 TitleBar 高 44。當然還有別的更好的方式來處理,不過現在求效果不求質量,簡單暴力又好用。
##最後
到目前我們乾的挺好的,不過確實還有許多能夠調整的地方。好比說圖片庫我們現在關不掉。當然我們若是給它加一個 onClose 屬性的話,還是能夠的。不過我們還須要在它關閉的時候更新我們的狀態。好吧這很簡單,來看下面:
<!-- lang: js --> onClose={() => this.setState({ photos: [] })}
同樣,我們的輸入框看起來醜死了。我們來加個特效,border,margin 和 placeholder:
<!-- lang: js --> <Input ref="search" placeholder="Enter your search" styles={{ input: { margin: '0 0 10px 0', border: '1px solid #ddd' } }} />
是否是很帥!
##完整代碼
好了,我們全部的代碼都是寫在 ./components/App.jsx
裏面的。很簡單,也很容易理解,還用了一些很酷的 ES6 屬性。來看完整版:
<!-- lang: js --> import React from 'react'; import View from 'reapp-ui/views/View'; import Button from 'reapp-ui/components/Button'; import Input from 'reapp-ui/components/Input'; import Superagent from 'superagent'; import Gallery from 'reapp-ui/components/Gallery'; const MY_KEY = '__YOUR_KEY_HERE__'; const base = `https://api.flickr.com/services/rest/?api_key=${MY_KEY}&format=rest&format=json&nojsoncallback=1`; export default React.createClass({ getInitialState() { return { photos: [] } }, // see: https://www.flickr.com/services/api/misc.urls.html getFlickrPhotoUrl(image) { return `https://farm${image.farm}.staticflickr.com/${image.server}/${image.id}_${image.secret}.jpg`; }, handleSearch() { let searchText = this.refs.search.getDOMNode().value; Superagent .get(`${base}&method=flickr.photos.search&text=${searchText}&per_page=10&page=1`, res => { if (res.status === 200 && res.body.photos) this.setState({ photos: res.body.photos.photo.map(this.getFlickrPhotoUrl) }); }); }, render() { var { photos } = this.state; return ( <View title="Flickr Search" styles={{ inner: { padding: 20 } }}> <Input ref="search" placeholder="Enter your search" styles={{ input: { margin: '0 0 10px 0', border: '1px solid #ddd' } }} /> <Button onTap={this.handleSearch}>Search Images</Button> <div className="verticalCenter"> {!photos.length && <p>No photos!</p> } {!!photos.length && <Gallery onClose={() => this.setState({ photos: [] })} images={photos} width={window.innerWidth} height={window.innerHeight - 44} /> } </div> </View> ); } });
#下一步
我們能夠以此為起點繼續往前走。我們能夠先顯示一個圖片列表,點擊它們再顯示 gallery。 Reapp 也提供了它的組件文檔,因此你能夠看看有什麼須要的把它加到你的應用裏面。 Reapp 還有幾個它們提供的不錯的例子:Kitchen Sink demo 和 Hacker News App。
#直接看代碼
若是你想直接看這個例子的代碼,你能夠直接克隆工程。它包括了你須要的全部的東西,當然除了 Flickr API key,你要測試它的話你必須本身去註冊一個。
啟動這個工程的步驟:
sudo npm install -g reapp
git clone git@github.com:reapp/flickr-demo
npm install
reapp run
你應該還是想看看 Reapp 起步 的,若是你想深刻再看一下 UI 組件文檔把。
Happy hacking!