React Native 简单教程 已翻译 100%

oschina 投递于 2015/05/19 12:03 (共 19 段, 翻译完成于 06-04)
阅读 84770
收藏 270
15
加载中

As a JavaScript developer, I would've never thought that I could make native  mobile applications easily in JavaScript. Sure, we have tools such as  PhoneGap, but wrapping our browser based application in native  application leaves a lot to be desired.

It's all changed now - the React team over at Facebook have released  React Native. This not only allows us to use the React framework to  create applications that utilise native mobile components, but it does  that all in real time - meaning that we don't have to recompile our  applications as we develop on them - this makes it incredibly easy to  create mobile apps! Luckily I got to preview React Native in its beta  and even since then it's progressed massively.

Please note that currently iOS is supported. You will therefore need  to be running OS X with XCode in order to follow this tutorial.

已有 1 人翻译此段
我来翻译

If you haven't had a chance to learn React, take a look at my tutorial to get started with it.

It's important to note that this doesn't mean we can write code once  and use it everywhere. Trying to do that can be disastrous as the level  of abstraction would be insane. Instead, React Native allows us to learn  once, write everywhere.

Going back to 2004

If you have watched The Social Network you will remember FaceMash,  the application that kicked off Facebook. For those who haven't watched,  11 years ago (wow) Mark Zuckerberg created FaceMash, an application  where you could view two people and rate which one is hotter. Each  person would have a score (although the original algorithm is unknown,  the movie shows the Elo rating algorithm being used) that reflected how "hot" they are.

Here it is in all it's glory -

 

Let's go full circle - we're going to be recreating FaceMash in React  Native. If you think it's immoral to rate girls on their looks, feel  free to change the pictures to something else that you find attractive  (dogs, snippets of code, etc, I'm not judging you).

已有 1 人翻译此段
我来翻译

Creating our app

You can clone the start repo here if you wish. This isn't necessary, however there different branches of the code at different stages just in case you get lost!

Houston, we have lift off

If you haven't cloned the repo, we need to setup our base project.  React Native allows us to quickly start up a project with the react-native-cli npm package CLI. If you don't have it installed, quickly run

npm install -g react-native-cli

And then we can get started.

Navigate to a folder in the terminal and run

react-native init FaceMash

This will create our base application ready for us to dig in and add to.

Open it up

Launch XCode and navigate to the directory you created the application in. From here, we will need to open up facemash.xcodeproj.

React Native supports us working in both the iOS Simulator and our actual iOS devices.

I will be developing in the iOS Simulator as it allows more rapid application development - we can press Command + R  to refresh the application when we make changes to our JavaScript, or  we can be super lazy and enable live reloading via the developer menu  (accessible via Command + Control + Z). We can even debug our code in the Chrome developer tools.

已有 1 人翻译此段
我来翻译

If you wish to develop the application using your iOS device, you  will need to be on the same network as your computer. By default, React  Native will look at localhost for the JavaScript so you will need to point it to your computer.

We can do this by editing the AppDelegate.m file and changing localhost to our local IP. You can find this on OS X by Alt + Click-ing the wireless menu.

We can now run our application. The application will open in whatever  target you've picked in XCode. When we click run, a terminal process  will also spawn running npm start in the directory of our application. If you don't wish to run the app through XCode, make sure you run npm start. This will create a local web server on port 8081 which points to our compiled JavaScript code and also watch and recompile our code on save.

 

I'm running the application in a simulated iPhone 6, running at a 50% scale.

There we go, we've got a blank canvas with so much room for activities!

已有 1 人翻译此段
我来翻译

Taking a peek

Let's take a look at the code it takes to render what we can see in the screenshot above. Open up index.ios.js.

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
'use strict';
var React = require('react-native');
var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
} = React;
var facemash = React.createClass({
  render: function() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.ios.js
        </Text>
        <Text style={styles.instructions}>
          Press Cmd+R to reload,{'\n'}
          Cmd+Control+Z for dev menu
        </Text>
      </View>
    );
  }
});
var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});
AppRegistry.registerComponent('facemash', () => facemash);

You can close your mouth now - yes, that is all it takes to render our application. Looks familiar, doesn't it?

React Native vs the browser

Not all of React Native matches what you would've been using with  React in the browser. However, the differences between the two are so  minuscule there's no need to worry about them.

  • Instead of using block elements such as div or section, we use the View component in React Native. This maps over to the native iOS component UIView.

  • All text must be wrapped inside the Text component

  • No stylesheets - all your styling is written as JavaScript objects

  • We don't need to worry about what is browser compatibility - ES6 harmony is supported out of the box as well as flexbox

已有 1 人翻译此段
我来翻译

Get to work

We're going to start by clearing out the stylesheet and render  function of our React component. Ideally, to get a basic feeling of  React Native, we're going to try and use as many different components as  we can.

Let's start off with the TabBarIOS component. You might recognise the TabBar component, used in some of the core iOS apps such as Clock and Photos.

var React = require('react-native');
var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  TabBarIOS
} = React;
var facemash = React.createClass({
  getInitialState() {
    return {
      selectedTab: 'faceMash'
    }
  },
  render: function() {
    return (
      <TabBarIOS>
        <TabBarIOS.Item
          title="FaceMash"
          icon={ require('image!facemash') }
          selected={ this.state.selectedTab === 'faceMash' }>
          <View style={ styles.pageView }>
            <Text>Face Mash</Text>
          </View>
        </TabBarIOS.Item>
        <TabBarIOS.Item
          title="Messages"
          icon={ require('image!messages') }
          selected={ this.state.selectedTab === 'messages' }>
          <View style={ styles.pageView }>
            <Text>Messages</Text>
          </View>
        </TabBarIOS.Item>
        <TabBarIOS.Item
          title="Settings"
          icon={ require('image!settings') }
          selected={ this.state.selectedTab === 'settings' }>
          <View style={ styles.pageView }>
            <Text>Settings</Text>
          </View>
        </TabBarIOS.Item>
      </TabBarIOS>
    );
  }
});
var styles = StyleSheet.create({
  pageView: {
    backgroundColor: '#fff',
    flex: 1
  }
});
// omitted code

       

Look at that! You'll notice that the text is currently over the status bar, but don't worry, we'll fix that later on.

The TabBarIOS component uses TabBarIOS.Item  for each of its children. We're going to have three pages - the page  where you rate people, a messages list and a settings page.

The TabBarIOS.Item must have one child. This will be the  contents of the page that has been selected (you can see us setting  selected to true/false depending on the state of the component).

Obviously, a tab bar wouldn't look good without icons. There are a  few system icons you can use, however if you use them the text of the  tab also changes to match the icon. Instead, we can use our own icons.  To import a local image asset in React Native, you can use require and prepend the asset name with image!.

I'm using icons that are available free, under the CC 3.0 licence, from flaticon.

已有 1 人翻译此段
我来翻译

Using Static Images

To add a static image for React Native, open up XCode. In the Project Navigator (the first icon on the left hand pane), open up Images.xcassets. All your images will sit here.

This allows us to keep all our assets under one name, whilst  providing different images for both each resolution, and even device  specific images.

Images must follow a strict naming convention. Use the asset name (such as messages or settings)  and append it with the resolution it should be displayed at. For  instance, I'm building my application for an iPhone 6 so I would use the  @2x resolution.

Once you've named your image file correctly, drag and drop it into the left hand page in Images.xcassets.

You can then use require('image!assetname') in React Native!

已有 1 人翻译此段
我来翻译

Back to the code

The next logical step is to set up our main component to allow  switching between tabs. We can do this by just setting the state when  the user clicks on it. TabBarIOS.Item allows us to give it an onPress property, which we can use to detect when a user taps on the tab.

// omitted code

var facemash = React.createClass({
  getInitialState() {
    return {
      selectedTab: 'faceMash'
    }
  },
  changeTab(tabName) {
    this.setState({
      selectedTab: tabName
    });
  },
  render: function() {
    return (
      <TabBarIOS>
        <TabBarIOS.Item
          title="FaceMash"
          icon={ require('image!facemash') }
          onPress={ () => this.changeTab('faceMash') }
          selected={ this.state.selectedTab === 'faceMash' }>
          <View style={ styles.pageView }>
            <Text>Face Mash</Text>
          </View>
        </TabBarIOS.Item>
        <TabBarIOS.Item
          title="Messages"
          icon={ require('image!messages') }
          onPress={ () => this.changeTab('messages') }
          selected={ this.state.selectedTab === 'messages' }>
          <View style={ styles.pageView }>
            <Text>Messages</Text>
          </View>
        </TabBarIOS.Item>
        <TabBarIOS.Item
          title="Settings"
          icon={ require('image!settings') }
          onPress={ () => this.changeTab('settings') }
          selected={ this.state.selectedTab === 'settings' }>
          <View style={ styles.pageView }>
            <Text>Settings</Text>
          </View>
        </TabBarIOS.Item>
      </TabBarIOS>
    );
  }
});

// omitted code

       

Done! It's that simple. Refresh your app by pressing Command + R  in the iOS simulator (or recompile it via XCode if you're developing on  your device) and you'll notice we can now tap between tabs, and the  screen changes!

Although we haven't written much code, you can check out the step-one branch, which also contains all the icons we've used for the tabs.

已有 1 人翻译此段
我来翻译

Let's Mash

Let's work on the FaceMash tab. We're going to hit an endpoint to load our data using fetch. In the step-one branch, I've included a config.yaml file in the rest/ folder that we're going to use to mock our endpoints using stubby. All the users in the endpoint/pictures were randomly generated at randomuser.me.

Open up your terminal and run

stubby -d rest/config.yaml

And let's get started!

Create a new file in the folder named tabs/ called FaceMash.js and stick a basic React component in there -

'use strict';

var React = require('react-native');

var {
  StyleSheet,
  Text,
  View
  } = React;

var facemashTab = React.createClass({
  render: function() {
    return (
      <View style={ styles.container }>
        <Text>
          FaceMash tab!
        </Text>
      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff'
  }
});

module.exports = facemashTab;

All we've got on this tab so far is a basic View component with some text inside of it. We've also got some basic styling for the View, to make sure that it takes up all available height/width.

We're going to add a header, purely for display purposes.

// omitted code

var facemashTab = React.createClass({
  render: function() {
    return (
      <View style={ styles.container }>
        <View style={ styles.header }>
        </View>
        <View style={ styles.content }>
          <Text>
            FaceMash tab!
          </Text>
        </View>
      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff'
  },
  header: {
    height: 40,
    background: '#ff0000'
  }
});

module.exports = facemashTab;

       

Now we're talking. However, this looks terrible as the status bar is black. Not to worry, we can change that using the StatusBarIOS API. When the changeTab function is called, we can check if the tab is the FaceMash tab. If it is, we'll set the status bar style to 1 (white), if not we'll set it to 0 (black).

已有 1 人翻译此段
我来翻译

index.ios.js

// omitted code

var {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  TabBarIOS,
  StatusBarIOS
} = React;

var facemash = React.createClass({
  ...,
  changeTab(tabName) {
  StatusBarIOS.setStyle(tabName === 'faceMash' ? 1 : 0);
    this.setState({
      selectedTab: tabName
    });
  },
  ...
});

// omitted code

Refresh and you'll see a white status bar - sorted!

We can now get to hitting the endpoint and displaying our users. We'll be using fetch, which is included for us in React Native by default.

// omitted code

var facemashTab = React.createClass({
  getInitialState: function() {
    return {
      list: [],
      currentIndex: 0
    };
  },
  componentWillMount: function() {
    fetch('http://localhost:8882/rest/mash')
      .then(res => res.json())
      .then(res => this.setState({ list: res }));
  },
  render: function() {
    return (
      ...
    );
  }
});

// omitted code

The request will populate our state with the returned data. As the initial state is an empty array, we can check that in our render function and show the user a loading page whilst they wait.

var {
  StyleSheet,
  Text,
  View,
  ActivityIndicatorIOS
  } = React;

var facemashTab = React.createClass({
  ...,
  render: function() {
    var contents;
    if (!this.state.list.length) {
      contents = (
        <View style={ styles.loading }>
          <Text style={ styles.loadingText }>Loading</Text>
          <ActivityIndicatorIOS />
        </View>
      )
    } else {
      contents = (
        <View style={ styles.content }>
          <Text>Loaded</Text>
        </View>
      )
    }
    return (
      <View style={ styles.container }>
        <View style={ styles.header }>
          <Text style={ styles.headerText }>FaceMash</Text>
        </View>
        { contents }
      </View>
    );
  }
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff'
  },
  loading: {
    flex: 1,
    backgroundColor: '#fff',
    justifyContent: 'center',
    alignItems: 'center'
  },
  loadingText: {
    fontSize: 14,
    marginBottom: 20
  },
  header: {
    height: 50,
    backgroundColor: '#760004',
    paddingTop: 20,
    alignItems: 'center'
  },
  headerText: {
    color: '#fff',
    fontSize: 20,
    fontWeight: 'bold'
  }
});

           

We will now have access to our data in this.state.list.  We've also got what current index of that array we're at in the state as  the endpoint returns an array of objects - each object being two people  that the user can rate.

As there are two people to pick from, both of which have the same  data associated with them, we're going to make a React component that  will display their data.

// omitted code

var Person = React.createClass({
  render: function() {
    var person = this.props.person;
    return (
      <View style={ styles.person }>
        <Text>Person!</Text>
      </View>
    )
  }
});

var facemashTab = React.createClass({
  getInitialState: function() {
    return {
      list: [],
      currentIndex: 0
    };
  },
  componentWillMount: function() {
    fetch('http://localhost:8882/rest/mash')
      .then(res => res.json())
      .then(res => this.setState({ list: res }));
  },
  render: function() {
    var contents;
    if (!this.state.list.length) {
      contents = (
        <View style={ styles.loading }>
          <Text style={ styles.loadingText }>Loading</Text>
          <ActivityIndicatorIOS />
        </View>
      )
    } else {
      var { list, currentIndex } = this.state;
      var record = list[currentIndex];
      var people = record.users.map(person => <Person person={ person } />);
      contents = (
        <View style={ styles.content }>
          { people }
        </View>
      )
    }
    return (
      <View style={ styles.container }>
        <View style={ styles.header }>
          <Text style={ styles.headerText }>FaceMash</Text>
        </View>
        { contents }
      </View>
    );
  }
});

var styles = StyleSheet.create({
  ...,
  person: {
    flex: 1,
    margin: 10,
    borderRadius: 3,
    overflow: 'hidden'
  }
});

We've now got a component that mounts twice (once for each person),  and the appropriate configuration passed through to it. We can now  display the profile picture and the relevant user info.

已有 1 人翻译此段
我来翻译
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(29)

小点点
小点点
蚂蚁金服 在用不在用不确定,但是他们团队推出了 基于react 的 UI ant design
AirBaylor
AirBaylor

引用来自“kut”的评论

这货目测现在还是一玩具,上不了生产,哪个团队用都要承担项目失败的巨大风险。

引用来自“1棵拼搏的寂静草”的评论

天猫,淘宝用的

引用来自“javadeveloper”的评论

用在什么产品上了,介绍介绍。
美团的二级界面 都用上了
AirBaylor
AirBaylor

引用来自“kut”的评论

这货目测现在还是一玩具,上不了生产,哪个团队用都要承担项目失败的巨大风险。

引用来自“1棵拼搏的寂静草”的评论

天猫,淘宝用的

引用来自“javadeveloper”的评论

用在什么产品上了,介绍介绍。
美团的二级界面 都用上了
抽烟被狗咬
文章也就看起来不错,照着敲就发现各种没法用,缺失东西有点多。
Jiazz
Jiazz
android sky~~
kimown
kimown
我司已大规模使用中。
您的好友
您的好友

引用来自“kut”的评论

这货目测现在还是一玩具,上不了生产,哪个团队用都要承担项目失败的巨大风险。

引用来自“1棵拼搏的寂静草”的评论

天猫,淘宝用的

引用来自“工头叫我去搬砖”的评论

怪不得我觉得淘宝app那么难用呢,与他的公司地位完全不符啊

引用来自“1棵拼搏的寂静草”的评论

最近刚发布的版本才是react,之前的不是
同时会web和安卓iOS开发 比任何框架都来的实在
1棵拼搏的寂静草
1棵拼搏的寂静草

引用来自“kut”的评论

这货目测现在还是一玩具,上不了生产,哪个团队用都要承担项目失败的巨大风险。

引用来自“1棵拼搏的寂静草”的评论

天猫,淘宝用的

引用来自“工头叫我去搬砖”的评论

怪不得我觉得淘宝app那么难用呢,与他的公司地位完全不符啊
最近刚发布的版本才是react,之前的不是
工头叫我去搬砖
工头叫我去搬砖

引用来自“kut”的评论

这货目测现在还是一玩具,上不了生产,哪个团队用都要承担项目失败的巨大风险。

引用来自“1棵拼搏的寂静草”的评论

天猫,淘宝用的
怪不得我觉得淘宝app那么难用呢,与他的公司地位完全不符啊
1棵拼搏的寂静草
1棵拼搏的寂静草

引用来自“kut”的评论

这货目测现在还是一玩具,上不了生产,哪个团队用都要承担项目失败的巨大风险。

引用来自“1棵拼搏的寂静草”的评论

天猫,淘宝用的

引用来自“javadeveloper”的评论

用在什么产品上了,介绍介绍。
自己百度 天猫 react 有比较详细的介绍
返回顶部
顶部