4) React Native 入门项目与解析

通过编写的第二篇文章可以成功创建一个新项目,即直接利用以下语句创建:

//命令行创建项目:
react-native init AwesomeProject

创建成功后,刚入门的我们主要关注两个文件:
1)iOS项目目录下的AppDelegate.m
为将iOS项目连接js文件的入口,以及相关初始化操作。
2)根目录下的index.ios.js
为iOS对应的js入口文件。

一、 解析iOS项目中的AppDelegate.m

1. AppDelegate.m 代码如下:

#import "AppDelegate.h"

//  React Native相关头文件
#import "RCTBundleURLProvider.h"
#import "RCTRootView.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSURL *jsCodeLocation;

  /* 
   当应用开始运行的时候,RCTRootView将会从以下的URL中加载应用:(本地调试的时候是直接在本地服务器中的index.ios加载,发布时设置有所不同)
   重新调用了你在运行这个App时打开的终端窗口,它开启了一个 packager 和 server 来处理上面的请求。
   在 Safari 中打开那个 URL;你将会看到这个 App 的 JavaScript 代码
  */
  [[RCTBundleURLProvider sharedSettings] setDefaults];
  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];

  // RCTRootView是一个UIView容器,承载着React Native应用。同时它也提供了一个联通原生端和被托管端的接口。
  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"AwesomeProject"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];
  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  return YES;
}

@end

2. RCTRootView

RCTRootView将React Natvie视图封装到原生组件中。(用户能看到的一切内容都来源于这个RootView,所有的初始化工作也都在这个方法内完成。)

解析:
通过RCTRootView的初始化函数你可以将任意属性传递给React Native应用。
参数initialProperties必须是NSDictionary的一个实例。
这一字典参数会在内部被转化为一个可供JS组件调用的JSON对象。

NSArray *imageList = @[@"http://foo.com/bar1.png",
                  @"http://foo.com/bar2.png"];
NSDictionary *propsDict = @{@"images" : imageList};

RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"AwesomeProject"
                                               initialProperties: propsDict
                                                   launchOptions:launchOptions];

在js文件中,则是通过this.props.images调用上面定义的参数。
this为AppRegistry.registerComponent注册的组件(下面会讲到)

RCTRootView同样提供了一个可读写的属性appProperties。在appProperties设置之后,React Native应用将会根据新的属性重新渲染。当然,只有在新属性和之前的属性有区别时更新才会被触发。
(注意:1.可以随时更新属性,但是更新必须在主线程中进行,读取则可以在任何线程中进行。2.更新属性时并不能做到只更新一部分属性)

NSArray *imageList = @[@"http://foo.com/bar3.png",
                   @"http://foo.com/bar4.png"];
rootView.appProperties = @{@"images" : imageList};

二、解析js入口文件(index.ios.js)

1. index.ios.js 代码如下:

'use strict'; // 全局进入严格模式(目前发现不用也行)
/**< 
消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
消除代码运行的一些不安全之处,保证代码运行的安全;
提高编译器效率,增加运行速度;
为未来新版本的Javascript做好铺垫。
*/

//导入一些必要的模块
//React Native内置的组件可以直接通过import { xxx } from 'react-native' 进行导入,当然也可以自定义组件。
import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View,
  TouchableOpacity
} from 'react-native';

//类,这是默认的载入类,继承自Component,Component类(组件)似于Android和iOS中的View
//这里为创建一个组件
class AwesomeProject extends Component {
  
  //构造器 ,每个组件都拥有自己的属性(props)和状态(state)
  //调用this.setState更改状态text或者isTouchDown时,组件会触发render函数进行渲染更新   
  constructor(props) {
     super(props);
     this.state = {
       text:'Welcome to React Native!',
       isTouchDown:false
      };
   }  
  
  //在最初的渲染之前调用一次,可在里面进行预处理操作
  //在React中,设置this.state会导致重新渲染,但是componentWillMount设置this.state并不会对导致render调用多次
  //之后会对component的生命周期进一步解释
  componentWillMount() {
  }
  
  //渲染函数,用来渲染实际的Component可视部分
  render() {
    //var定义变量,根据状态值改变对应值
    var welcomeText = this.state.text;
    var bgcolor;
    if (this.state.isTouchDown) {
      bgcolor = '#c5c5ab';
    } else {
      bgcolor = '#F5FCFF';
    }
    console.log('testtststststts');

    //返回的即界面显示内容
    return (
      <View style={[styles.container, {backgroundColor: bgcolor}]}>
        <Text style={styles.welcome}>
          {welcomeText}
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.android.js
        </Text>
        <Text style={styles.instructions}>
            Shake or press menu button for dev menu
        </Text>
        <TouchableOpacity onPress={this.touchDown.bind(this)}>
          <Text style={[styles.instructions, {backgroundColor: 'green'}]}>
            test touch Me
          </Text>
        </TouchableOpacity>
      </View>
    );
  }

  //  自定义函数
  touchDown() {
    //  console.log 控制台打印,可打印值,多用于调试
    console.log('>>', this.isTouchDown);

    if (!this.state.isTouchDown) {
      this.setState({
        text:'Test Touch Down Success',
        isTouchDown:true
      });
    } else {
      this.setState({
        text:'Test Touch Down Again Success',
        isTouchDown:false
      });
    }
  }

}

//定义样式
const 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 定义了App的入口,并提供了根组件。
//第一个'AwesomeProject'要与AppDelegate里注册的moduleName一致
//第二个AwesomeProject则是入口组件,即上面定义的Component类
AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);

2. 运行效果:

简单运行效果.png

3. 基础概念解释

1)组件

代码中的 Text, View, TouchableOpacity均为基础组件。AwesomeProject则是自己创建的组件,也作为项目的入口组件。
在React Native项目中,所有展示的界面,都可以看做是一个组件(Component)只是功能和逻辑上的复杂程度不同。每一个是许许多多小的组件拼成的,每个小的组件也有自己对应的逻辑。

2)组件的状态与属性

组件本质上是状态机,输入确定,输出一定确定。组件把状态与结果一一对应起来,组件中有state与prop(状态与属性)。

属性(props)是标签里面的属性, 组件之前通过标签的属性来传递数据,由父组件传递给子组件(单向的属性传递)。
如果component的某些状态由外部所决定,并且会影响到component的render,那么这些状态就应该用props表示。
例如:一个下拉菜单的component,有哪些菜单项,是由这个component的使用者和使用场景决定的,那么“菜单项”这个状态,就应该用props表示,并且由外部传入。

状态(state)是子组中的状态,内部的事件方法或者生命周期方法都可以调用this.setState来变更,当状态发生变化的同时,组件也会触发render函数进行渲染更新。
如果component的某些状态需要被改变,并且会影响到component的render,那么这些状态就应该用state表示。
例如:一个购物车的component,会根据用户在购物车中添加的产品和产品数量,显示不同的价格,那么“总价”这个状态,就应该用state表示。


这篇文章利用了一个很简单的例子去具体解析了React Native的基本代码,最主要想理解组件的概念,由此可以更好的去理解别人的代码,,接下去为了更好的入门,会继续写两篇文章分别介绍React Native用到的js基础、react基础,以及介绍组件的生命周期。

正在写React Native的学习教程ing,是一边研究一边编写的,已有的成果如下:
1) React Native 简介与入门
2) React Native 环境搭建和创建项目(Mac)
3) React Native 开发之IDE

  1. React Native 入门项目与解析
    5) React Native 相关JS和React基础
    6) React Native 组件生命周期(ES6)
    7) React Native 集成到原生项目(iOS)
    8) React Native 与原生之间的通信(iOS)
    9) React Native 封装原生UI组件(iOS)

推荐阅读更多精彩内容