學習 React.js:用 ReactJS 30 分鐘打造一個移動應用

Make a Mobile App with ReactJS in 30 Minutes

Ken Wheeler (@ken_wheeler)

React 能讓前端開發者以前所未有的方式來構建應用。它有許多好處:比如單向數據流,簡單的組件生命週期,聲明組件之類的。

Reapp 是最近發佈的基於 React 的一個框架。它是一個專為性能和生產效率而生的移動應用平台。你可以把它看做是一個精心優化過的 UI 組件,並且帶有一套很好的編譯系統,以及許多有用工具,能讓你輕松構建你的應用。

Reapp 給我們提供了一堆很棒的東西:

  • 完整的移動 UI 組件
  • "reapp new" 生成一個可用的 app
  • "reapp run" 啟動你的 ES6 app,並且可以更新無需重啟
  • 主題和動畫
  • 路由和包請求
  • 用 Cordova 創建應用

#我們的目標

為了演示 Reapp,我們將創建一個應用,可以讓你檢索 Flickr API,然後把結果在圖片庫裏面顯示出來。跟著這個教程做的話,大約也就會佔用你不到半個小時的時間!

#開始

首先你得安裝有 node,然後我們來執行 sudo npm install -g reapp 安裝 Reapp CLI。安裝完之後,執行 reapp new flickrapp。最後, cd flickrappreapp run

你就會看到如下:

在你的瀏覽器中打開 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.jsxhome/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 中的 URL
  • this.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 demoHacker News App

#直接看代碼

如果你想直接看這個例子的代碼,你可以直接克隆工程。它包括了你需要的所有的東西,當然除了 Flickr API key,你要測試它的話你必須自己去註冊一個。

啟動這個工程的步驟:

  1. 安裝 Node/npm,以及 Reapp:sudo npm install -g reapp
  2. 克隆工程:git clone git@github.com:reapp/flickr-demo
  3. 安裝依賴:npm install
  4. 啟動服務:reapp run
  5. 在瀏覽器中輸入 http://localhost:3010 查看效果

你應該還是想看看 Reapp 起步 的,如果你想深入再看一下 UI 組件文檔把。

Happy hacking!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom