前言
上篇文章讲了自己在搭建React Native环境时遇到的问题,这篇就接着来说一下RN项目的基础,包括布局、各种事件等项目日常开发需要用到的,同时也是做一个记录,方便以后自己查询。
正文
环境搭建好了,接下来就是最基本的Hello World
咯,不过不着急,可以先使用Xcode
打开HelloWorld
项目来看一下项目概况。
以上就是项目的基本结构,其中包括了我们最常见的AppDelegate
、Images.xcassets
、Info.plist
、LaunchScreen.xib
、main.m
、这几个文件外,还出现了一个Libraries
文件夹,其中有很多以.xcodeproj
结尾的文件,这些应该是引入的库文件,可以暂时先放放,先我们最为熟悉的main.m
和AppDelegate
文件。
在main.m
文件中,还是跟我们原生的iOS新建项目中的main.m
一样,作为程序的起始点,将程序的入口引向了AppDelegate
文件。
再来看看AppDelegate
文件,.h
文件中没有什么变化,但是在.m
中却有些改变,以下是.m
文件中的代码
#import "AppDelegate.h"
#import
#import
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"HelloWorld"
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
在原生的项目中,我们一般都是设置Nav
为window.rootViewController
,但是这里却将Nav
替换成了RCTRootView
,虽然不知道是什么,但是根据上面引入的头文件可知,是从React
中引入的,我们可以点击进去看一下RCTRootView
类的真面目。
根据进去RCTRootView
类中看到的可知其是一个继承自UIView
的类,其中包含了两个初始化方法和若干属性,RCTRootView
类也充当了该项目的程序主页面咯。
废话到此为止,接下来就详细介绍一下入门基础。
Hello World
学习编程的第一步就是输出Hello World
咯
使用自己喜欢的编辑器,打开项目根目录中的App.js
文件,很简单的几十行代码,看看和运行起来的界面有何联系,更改其中的某一些代码使模拟器中显示出来Hello World
这两个英文单词吧,相信大家都会的。这里不再赘述。
下面给出原始的App.js
文件中的代码做出部分讲解(此讲解是以个人第一次见到的第一印象来解释,并不准确,可作为参考,也可培养自己的码感),具体解释直接放在代码旁边
import React, {Component} from 'react'; // 根据iOS中的#import也可以知道是导入头文件,大概意思就是说从react库中导入React和Component,至于为什么Component用大括号括起来,我也不晓得,或许是类别不一样吧
import {Platform, StyleSheet, Text, View} from 'react-native';
// const 常量声明的关键字,所以可以大概知道后面声明的这是一个常量,常量中又有ios和android两个
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
android:
'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});
type Props = {};
// 下面这堆东西可以理解为一个名字为App的主类
export default class App extends Component {
render() {
return (
// 根据界面显示,可以知道界面上显示的内容就是下面这个View中的内容
// 这种写法就跟HTML类似,只不过将换成了view
// 至于其中跟的style就更明显了,样式声明,可是后面跟的{style.container}是个什么鬼,不过往下面的代码中瞄一眼就立马发现了猫腻,下面声明了一个styles的常量,其中包含了这里所写的styles.container,所以可以知道这里就是将样式声明单独列在一起,避免代码臃肿不便阅读。
Welcome to React Native!
To get started, edit App.js
// 下面这句代码中的尖括号中写的是{instructions},这里的样式表的值就是用大括号括起来的,这里也是用大括号,所以可以暂时性理解为大括号代表一个变量,大括号中的是变量名字,这里对应的就是上面声明的那个常量。
// 再根据界面显示的内容,就可以发现上面声明的那个常量后面是一个平台鉴别的函数,鉴别当前使用的设备是iOS还是android以此来显示不同的内容
{instructions}
);
}
}
// 根据StyleSheet.create可以知道这是一个函数,并且是创建了一个样式表的函数
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,
},
});
// 其实关于上面样式表声明的这种写法还是可以理解的,因为在原生iOS开发的时候,在初始化控件的时候我也采用过以下的写法,供大家参考,其原理我也不大清楚,按照我自己的理解,等号的右边就是一个有返回值的无名函数
// 以下是iOS原生示例,非App.js中代码
// 以下是iOS原生示例,非App.js中代码
// 以下是iOS原生示例,非App.js中代码
UIView *vv = ({
UIView *v = [[UIView alloc] init];
v.frame = CGRectMake(10, 10, 100, 44);
v.backgroundColor = [UIColor redColor];
// 这里可以声明很多控件组成的一个复杂控件,只需要在这里的最后返回这个复杂控件即可
return v;
});
样式
在RN当中,所有的界面样式都是使用JavaScript
来实现的,所有的核心组件都接受名为style
的属性,只是其中的样式名称命名做了改变,其余的均为改变。
Style
属性是一个普通的JavaScript对象,在这里使用的时候还可以传入一个数组,在数组中位置居后的样式对象比居前的优先级更高,这样可以间接实现样式的继承。
实际开发中组件的样式会越来越复杂,我们可以使用StyleSheet.create
来几种定义组件的样式,比如下面所示:
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, Text, View } from 'react-native';
export default class LotsOfStyles extends Component {
render() {
return (
// 这个Text定义的样式中只改变了显示文字的颜色为红色,其他均为默认
just red
// 第二个Text的样式定义改变文字颜色为蓝色,粗体,字号30
just bigblue
// 第三个Text的样式定义改变文字颜色为蓝色,粗体,字号30,但是这里是一个数组,styles.red居后,优先级高,所以最终字体的显示效果为红色,粗体,字号30
bigblue, then red
// 第四个Text的样式定义改变文字颜色为红色,但是styles.bigblue优先级更高,故最终显示效果为蓝色,粗体,字号30(后面的字体颜色优先级高于styles.red中定义的红色,故显示蓝色)
red, then bigblue
);
}
}
const styles = StyleSheet.create({
bigblue: {
color: 'blue',
fontWeight: 'bold',
fontSize: 30,
},
red: {
color: 'red',
},
});
显示效果图如下:
高度与宽度
最简单的给组件设定尺寸的方式就是在样式中指定固定数值的width
和height
。RN中的尺寸都是无单位的,表示的是与设备像素密度无关的逻辑像素点。
如下:
import React, { Component } from 'react';
import { AppRegistry, View } from 'react-native';
export default class FixedDimensionsBasics extends Component {
render() {
return (
);
}
}
效果图
除了指定具体数值的宽高设置,在组件样式中还可以使用flex
来在可利用的空间中动态的扩张或收缩。一般而言我们会使用flex:1
来指定某个组件扩张以撑满所有剩余的控件。如果有多个并列的子组件使用了flex:1
,则这些子组件会平分父容器中剩余的控件。如果这些并列的子组件的flex
值不一样,则谁的值更大,谁占据剩余空间的比例就更大(即占据剩余控件的比等于并列组件间flex
值的比)
如下示例:
import React, { Component } from 'react';
import { AppRegistry, View } from 'react-native';
export default class FlexDimensionsBasics extends Component {
render() {
return (
// 试试去掉父View中的`flex: 1`。
// 则父View不再具有尺寸,因此子组件也无法再撑开。
// 然后再用`height: 300`来代替父View的`flex: 1`试试看?
);
}
}
效果图:
使用Flexbox布局
我们在RN中使用 flexbox
规则来指定某个组件的子元素的布局。Flexbox
可以在不同屏幕尺寸上提供一致的布局结构。
一般来说,使用flexDirection
、alignItems
和 justifyContent
三个样式属性就已经能满足大多数布局需求。
Flex Direction
在组件的style
中指定flexDirection
可以决定布局的主轴。
row: 子元素沿着水平轴(row)方向排列
column: 子元素沿着竖直轴(column)方向排列(默认值)
示例如下
import React, { Component } from 'react';
import { AppRegistry, View } from 'react-native';
export default class FlexDirectionBasics extends Component {
render() {
return (
// 尝试把`flexDirection`改为`column`看看
);
}
};
Justify Content
在组件的 style 中指定justifyContent可以决定其子元素沿着主轴的排列方式。效果图均以flexDirection : 'column'
为准
以下代码,每次更换justifyContent
的值,查看效果
export default class JustifyContentBasics extends Component {
render() {
return (
1
2
3
);
}
};
flex-start
:
center
:
flex-end
:
space-around
:
space-between
:
space-evenly
:
Align Items
在组件的 style 中指定alignItems可以决定其子元素沿着次轴(与主轴垂直的轴,比如若主轴方向为row,则次轴方向为column)的排列方式。
以下代码,以column
纵轴为主轴,横轴为次轴,每次更换alignItems
的值,查看效果
import React, { Component } from 'react';
import { AppRegistry, View } from 'react-native';
export default class AlignItemsBasics extends Component {
render() {
return (
1
2
3
);
}
};
flex-start
:
center
:
flex-end
:
stretch
:
注意:要使stretch选项生效的话,子元素在次轴方向上不能有固定的尺寸。
要使stretch
生效的话,子元素在次轴上方向上不能有固定的尺寸,即如果主轴为column
,子元素不能有固定值的宽;代码如下:
import React, { Component } from 'react';
import { AppRegistry, View } from 'react-native';
export default class AlignItemsBasics extends Component {
render() {
return (
1
2
3
);
}
};
属性(Props)
在OC开发中,当我们自定义一个组件的时候,肯定要对外暴露部分的属性或者方法,以此来增加组件的兼容性,能够让使用者对组件的颜色、内容等可以定制化开发。
在React Native中的大多数组件在创建时就可以使用各种参数来定制。用于定制的这些参数就成为props
(属性)。
比如基础组件Image
,在创建图片的时候传入一个名为source
的prop来指定要显示的图片的地址,传入一个名为style
的prop来控制其尺寸大小。
import React, { Component } from 'react';
import { Image } from 'react-native';
export default class Bananas extends Component {
render() {
let pic = {
uri: 'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg'
};
return (
);
}
}
注意:{pic}
外围有一层大括号,大括号的意思是说在大括号内部是一个js变量或者表达式,需要执行后取值。
以上说的是自带的基础组件,当然我们也可以像在OC中一样,自定义自己需要的组件,这时候我们也可以为自己的组件定义prop,当在调用的时候,就可以在父组件中指定其值。如下:
// 此为一个js文件
import React, { Component } from 'react';
import { Text, View } from 'react-native';
// 在RN的一个js文件中,可以有很多形如 “class 组件名 extends Component” 的组件声明
class Greeting extends Component {
render() {
return (
Hello {this.props.name}!
);
}
}
// 在RN的一个js文件中,必须要只有一个下面的默认组件声明,这个声明可以理解为一个文件的入口函数
export default class LotsOfGreetings extends Component {
render() {
return (
// 这里是引用上面定义的Greeting组件,其中name是其属性,在其内部使用this.props.name引用父组件设置的值
);
}
}
状态(State)
我们使用两种数据来控制一个组件:props
和state
。props
是在父组件中指定,而且一经指定,在被指定的组件的声明周期中则不再改变。对于需要改变的数据,我们需要使用state
。
就像我们在iOS原生中请求网络数据一样,在得到服务器返回的数据后,我们就会进行赋值、刷新界面等操作。在React Native中,我们可以直接使用state
,一般来说,我们首先需要在constructor中初始化state
,然后在需要修改时(如收到服务器返回的数据时)调用setState
方法,在每次调用setState
时,系统会重新执行render方法重新渲染界面。
注意:
- 一切界面变化都是
状态state变化
-
state
的修改必须通过setState()
方法
- setState是一个merge合并操作,只修改指定属性,不影响其他属性
- setState是异步操作,修改不会马上生效
结束语
关于React Native的入门介绍其实并不是很多,如果是从事网站开发的人员入手会更加的简单快速,这对于App开发人员来说也不是特别难的一件事,当然,关于这些入门的懂了之后,剩下的就是研究一下React Native的基础组件的使用、生命周期函数、一些特别的写法等问题,在接下来的空闲时间,我也会一一列举这些知识,希望能够帮到更多的人。