React Native - 组件

概述

组件样式

在React Native中,仍然使用JavaScript来写样式。所有的核心组件都接受名为style的属性。这些样式名基本上是遵循了web上的CSS的命名,只是按照JS的语法要求使用了驼峰命名法。

style属性可以是一个普通的JavaScript对象。还可以传入一个数组 —- 在数组中位置居后的样式对象比居前的优先级更高,这样可以间接实现样式的继承。

实际开发中组件的样式会越来越复杂,建议使用StyleSheet.create来集中定义组件的样式。

const styles = StyleSheet.create({
  bigblue: {
    color: 'blue',
    fontWeight: 'bold',
    fontSize: 30,
  },
  red: {
    color: 'red',
  },
});

常见的做法是按顺序声明和使用style属性,以借鉴CSS中的”层叠”做法(即后声明的属性会覆盖先声明的同名属性)。

组件尺寸

组件的高度和宽度决定了其在屏幕上显示的尺寸。

指定宽高

在样式中指定固定的width和height。React Native中的尺寸都是无单位的,表示的是与设备像素密度无关的逻辑像素点。

<View style={{width: 50, height: 50}} />

Android使用DP作为逻辑像素单位,iOS使用PT作为逻辑像素单位,逻辑像素与设备像素不一定相等。

弹性(Flex)宽高

在组件样式中使用flex可以使其在可利用的空间中动态地扩张或收缩。一般而言我们会使用flex:1来指定某个组件扩张以撑满所有剩余的空间。如果有多个并列的子组件使用了flex:1,则这些子组件会平分父容器中剩余的空间。如果这些并列的子组件的flex值不一样,则谁的值更大,谁占据剩余空间的比例就更大(即占据剩余空间的比等于并列组件间flex值的比)。

组件能够撑满剩余空间的前提是其父容器的尺寸不为零。如果父容器既没有固定的width和height,也没有设定flex,则父容器的尺寸为零。其子组件如果使用了flex,也是无法显示的。

<View style={{flex: 1}}>
    <View style={{flex: 1, backgroundColor: 'powderblue'}} />
    <View style={{flex: 2, backgroundColor: 'skyblue'}} />
    <View style={{flex: 3, backgroundColor: 'steelblue'}} />
</View>

以上代码的运行结果为:三个View的宽度相同,而高度按比例占据父容器的剩余空间(flexDirection的默认值是column)。

Flexbox布局

React Native中的Flexbox的工作原理和web上的CSS基本一致,当然也存在少许差异。首先是默认值不同:flexDirection的默认值是column而不是row,而flex也只能指定一个数字值。

  • flexDirection
    • 表示布局的主轴方向,默认值为column
    • column竖直轴方向,从上至下排列
    • column-reverse竖直轴方向,从下至上排列
    • row水平轴方向,从左至右排列
    • row-reverse水平轴方向,从右至左排列
  • justifyContent
    • 表示子项的在主轴上的对齐方式,默认值为flex-start
    • flex-start主轴的起点对齐
    • flex-end主轴的终点对齐
    • center主轴的中点对齐
    • space-between两端对齐,子项之间的间隔都相等
    • space-around子项两侧的间隔相等,因此子项之间的间隔等于子项与边框的间隔的2倍
  • alignItems
    • 表示子项的在次轴(与主轴垂直的轴)上的对齐方式,默认值为stretch
    • flex-start次轴的起点对齐
    • flex-end次轴的终点对齐
    • center次轴的中点对齐
    • baseline项目的第一行文字的基线对齐
    • stretch如果子项在次轴方向上未设置固定的尺寸或设为auto,将占满整个容器在次轴的尺寸

组件

AppRegistry

AppRegistry模块是用来告知React Native哪一个组件被注册为整个应用的根容器。一般在整个应用里AppRegistry.registerComponent这个方法只会调用一次,注册应用(registerComponent)后才能正确渲染。

AppRegistry.registerComponent(appKey: string, getComponentFunc: ComponentProvider)

注意,registerComponent的第一个参数是一个字符串,必须和react-native init命令创建的项目名一致,第二个参数是一个函数,该函数返回应用的根组件。

Image

  • 静态图片资源source={require('./src/img/img.png')}

View

View 常用作其他组件的容器,来帮助控制布局和样式。

TextInput

处理用户输入的组件。onChangeText属性接受一个函数,该函数在文本变化时调用。onSubmitEditing属性接受一个函数,该函数在文本提交后(用户按下软键盘上的提交键)调用。

ScrollView

可以容纳多个不同或相同类型组件和视图的可滚动容器,默认为垂直滚动,将horizontal属性设为true可改成水平滚动。ScrollView适合用来显示数量不多的滚动元素。放置在ScollView中的所有组件都会被渲染,哪怕有些组件因为内容太长被挤出了屏幕外。

ListView

ListView组件用于显示一个垂直的滚动列表,其中的元素之间结构近似而仅数据不同。ListView更适于长列表数据,且元素个数可以增删。和ScrollView不同的是,ListView并不立即渲染所有元素,而是优先渲染屏幕上可见的元素。

ListView组件必须的两个属性是dataSource和renderRow。dataSource是列表的数据源,可以通过创建一个设置了rowHasChanged函数属性的ListView.DataSource的实例,然后给它传递一个普通的数据数组,作为数据源。而renderRow是一个函数,负责逐个解析数据源中的数据,然后返回一个设定好格式的组件来渲染。

class ListViewBasics extends Component {
    // 初始化模拟数据
    constructor(props) {
        super(props);
        const ds = new ListView.DataSource({
            rowHasChanged: (r1, r2) => r1 !== r2
        });
        this.state = {
            dataSource: ds.cloneWithRows([
                'John',
                'Joel',
                'James',
                'Jimmy',
                'Jackson',
                'Jillian',
                'Julie',
                'Devin'
            ])
        };
    }
    render() {
        return (
            <View style={{
                    flex: 1,
                    paddingTop: 22
                }}>
                <ListView dataSource={this.state.dataSource} renderRow={(rowData) => <Text>{rowData}</Text>}/>
                <FlatList data={[
                    {
                        key: 'Devin'
                    }, {
                        key: 'Jackson'
                    }, {
                        key: 'James'
                    }, {
                        key: 'Joel'
                    }, {
                        key: 'John'
                    }, {
                        key: 'Jillian'
                    }, {
                        key: 'Jimmy'
                    }, {
                        key: 'Judlie'
                    }, {
                        key: 'Jodel'
                    }, {
                        key: 'Jdohn'
                    }, {
                        key: 'ds'
                    }, {
                        key: 'daffafd'
                    }, {
                        key: 'Julsie'
                    }, {
                        key: 'Jimm3y'
                    }, {
                        key: 'Juddflie'
                    }, {
                        key: 'Jodfadel'
                    }, {
                        key: 'Jdgfgohn'
                    }, {
                        key: 'dfgfgs'
                    }, {
                        key: 'dafffgfafd'
                    }, {
                        key: 'gfgfg'
                    }, {
                        key: 'Julssdfsdfie'
                    }, {
                        key: 'Jimsdfsdfm3y'
                    }, {
                        key: 'Judsdfdfdflie'
                    }, {
                        key: 'dfdddsfsdf'
                    }, {
                        key: 'sdfdfddd'
                    }, {
                        key: 'dfgdddfgs'
                    }, {
                        key: 'dafdffgfafd'
                    }, {
                        key: 'gfgddfg'
                    }
                ]} renderItem={({item}) => <Text style={styles.item}>{item.key}</Text>}/>
            </View>
        );
    }
}

FlatList

FlatList组件是一种垂直滚动的列表,该组件必须的两个属性是data和renderItem。data是列表的数据源,而renderItem接受一个函数,从数据源中逐个解析数据,然后返回一个设定好格式的组件来渲染。

class FlatListBasics extends Component {
    render() {
        return (
            <View style={styles.container}>
                <FlatList data={[
                    {
                        key: 'Devin'
                    }, {
                        key: 'Jackson'
                    }, {
                        key: 'James'
                    }, {
                        key: 'Joel'
                    }, {
                        key: 'John'
                    }, {
                        key: 'Jillian'
                    }, {
                        key: 'Jimmy'
                    }, {
                        key: 'Julie'
                    }
                ]} renderItem={({item}) => <Text style={styles.item}>{item.key}</Text>}/>
            </View>
        );
    }
}

SectionList

SectionList是高性能的分组(section)列表组件,支持下面这些常用的功能:

  • 完全跨平台。
  • 支持水平布局模式。
  • 行组件显示或隐藏时可配置回调事件。
  • 支持单独的头部组件。
  • 支持单独的尾部组件。
  • 支持自定义行间分隔线。
  • 支持下拉刷新。
  • 支持上拉加载。
  • 如果你的列表不需要分组(section),那么可以使用结构更简单的

网络

Fetch API

React Native提供了和web标准一致的Fetch API,用于满足开发者访问网络的需求。

fetch(url, config)

url参数表示资源地址,config参数可以用来定制HTTP请求一些参数,例如指定header参数,或是指定使用POST方法,又或是提交数据等等。

示例:

// 获取JSON数据
fetch('https://mywebsite.com/mydata.json')

// 提交JSON数据
fetch('https://mywebsite.com/endpoint/', {
    method: 'POST',
    headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({firstParam: 'yourValue', secondParam: 'yourOtherValue'})
})

// 提交表单数据
fetch('https://mywebsite.com/endpoint/', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: 'key1=value1&key2=value2'
})

Fetch方法会返回一个Promise,这种模式可以简化异步风格的代码。

getMoviesFromApiAsync() {
    return fetch('https://facebook.github.io/react-native/movies.json')
        .then((response) => response.json()).then((responseJson) => {
            return responseJson.movies;
        }).catch((error) => {
            console.error(error);
        });
}

也可以在React Native应用中使用ES7标准中的async/await语法。

// 注意这个方法前面有async关键字
async getMoviesFromApi() {
    try {
        // 注意这里的await语句,其所在的函数必须有async关键字声明
        let response = await fetch('https://facebook.github.io/react-native/movies.json');
        let responseJson = await response.json();
        return responseJson.movies;
    } catch (error) {
        console.error(error);
    }
}

别忘了catch住fetch可能抛出的异常,否则出错时可能看不到任何提示。

默认情况下,iOS会阻止所有非https的请求。如果请求的接口是http协议,那么首先需要添加一个App Transport Security的例外。

AJAX

React Native中已经内置了XMLHttpRequest API(也就是俗称的ajax)。一些基于XMLHttpRequest封装的第三方库也可以使用,例如frisbee或是axios等。但注意不能使用jQuery,因为jQuery中还使用了很多浏览器中才有而RN中没有的东西(所以也不是所有web中的ajax库都可以直接使用)。

var request = new XMLHttpRequest();
request.onreadystatechange = (e) => {
    if (request.readyState !== 4) {
        return;
    }

    if (request.status === 200) {
        console.log('success', request.responseText);
    } else {
        console.warn('error');
    }
};

request.open('GET', 'https://mywebsite.com/endpoint/');
request.send();

需要注意的是,安全机制与网页环境有所不同:在应用中可以访问任何网站,没有跨域的限制。

WebSocket

React Native还支持WebSocket,这种协议可以在单个TCP连接上提供全双工的通信信道。

var ws = new WebSocket('ws://host.com/path');

// 打开一个连接
ws.onopen = () => {
    ws.send('something'); // 发送一个消息
};

// 接收到了一个消息
ws.onmessage = (e) => {
    console.log(e.data);
};

// 发生了一个错误
ws.onerror = (e) => {
    console.log(e.message);
};

// 连接被关闭了
ws.onclose = (e) => {
    console.log(e.code, e.reason);
};

参考文章

  1. reactnative.cn