博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
React-Native 之 项目实战(二)
阅读量:4086 次
发布时间:2019-05-25

本文共 20720 字,大约阅读时间需要 69 分钟。

前言


  • 本文有配套视频,可以酌情观看。
  • 文中内容因各人理解不同,可能会有所偏差,欢迎朋友们联系我。
  • 文中所有内容仅供学习交流之用,不可用于商业用途,如因此引起的相关法律法规责任,与我无关。
  • 如文中内容对您造成不便,烦请联系 277511806@qq.com 处理,谢谢。
  • 转载麻烦注明出处,谢谢。
  • 本篇资源:链接:  密码: yy79

属性声明和属性确定


  • 有朋友反馈这边 属性声明和属性确定 不了解,这边就来补充一下。

  • 在 -Native 创建的自定义组件是可以复用的,而开发过程中一个组件可能会由多个人同时开发或者多个人使用一个组件,为了让开发人员之间减少沟通成本,我们会对某些必要的属性进行属性声明,让使用的人知道需要传入什么!甚至有些需要传入但没有传入值的属性我们会进行警告处理!

  • 这边先来看下 属性声明 的示例:

static propTypes = {        name:PropTypes.string,        ID:PropTypes.number.isRequired,    } 
1
2
3
4
5
1
2
3
4
5
  • 上面我们声明了 name 和 ID 两个属性,并且进行了属性的确认,其中,’isRequired’ 表示如果不传递这个属性,那么开发阶段中,系统会出现警告,让我们对其进行属性确认,也就是说是否为必须属性。

  • 属性确认语法分为:

    • 属性为任何类型
    React.PropTypes.any   
    1
    2
    1
    2
    • 属性是否是 JavaScript 基本类型
    React.PropTypes.array;    React.PropTypes.func;    React.PropTypes.bool;    React.PropTypes.number;    React.PropTypes.object;    React.PropTypes.string;   
    1
    2
    3
    4
    5
    6
    7
    1
    2
    3
    4
    5
    6
    7
    • 属性是某个 React 元素
    React.PropTypes.element;   
    1
    2
    1
    2
    • 属性为几个特定的值
    React.PropTypes.oneOf(['value1', 'value2'])   
    1
    2
    1
    2
    • 属性为指定类型中的一个
    React.PropTypes.oneOfType([        React.PropTypes.node,        React.PropTypes.number,        React.PropTypes.string    ])   
    1
    2
    3
    4
    5
    6
    1
    2
    3
    4
    5
    6
    • 属性为可渲染的节点
    React.PropTypes.node;   
    1
    2
    1
    2
    • 属性为某个指定类的实例
    React.PropTypes.instanceOf(NameOfClass);   
    1
    2
    1
    2
    • 属性为指定类型的数组
    React.PropTypes.arrayOf(React.PropTypes.string)   
    1
    2
    1
    2
    • 属性有一个指定的成员对象
    React.PropTypes..objectOf(React.PropTypes.number)   
    1
    2
    1
    2
    • 属性是一个指定构成方式的对象
    React.PropTypes.shape({        color:React.PropTypes.stirng,        fontSize:React.PropTypes.number    })   
    1
    2
    3
    4
    5
    1
    2
    3
    4
    5
    • 属性默认值(当我们没有传递属性的时候使用)
    static defaultProps = {        name:'苍井空'    };   
    1
    2
    3
    4
    1
    2
    3
    4

占位图


  • 开发中,我们会有许多图片都是从网络进行请求的,但是,如果出现网络卡顿的情况,图片就会迟迟不出现,又或者有的并没有图片,这样图片就为空白状态;为了不让用户感觉太突兀影响用户体验,也为了视图整体性,一般我们会选择使用占位图先展示给用户看,等到图片加载完毕再将图片展示出来。

  • 这边我们需要对cell内部进行一些处理。

{/* 左边图片 */}      
1
2
3
1
2
3

占位图.png

无数据情况处理


  • 还是网络问题,在网络出现问题或者无法加载数据的时候,一般我们会展示空白页,在空白页中提示 无数据 之类的提示,比较好的还会使用指示器 的方式告诉用户网络出现问题等等。

  • 这边我们做以下处理,当无数据时,我们就先初始化基础界面,然后展示 提示 页面,等到有数据时,再重新渲染数据。

  • 首先设置 无数据 页面

import React, { Component } from 'react';    import {        StyleSheet,        View,        Text,    } from 'react-native';    export default class GDNoDataView extends Component {
render() { return(
无数据
); } } const styles = StyleSheet.create({ container: { flex:1, justifyContent:'center', alignItems:'center', }, textStyle: { fontSize:21, color:'gray' } });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  • 接着,没有数据的时候我们进行一些处理就可以了
// 根据网络状态决定是否渲染 listview    renderListView() {        if (this.state.loaded === false) {            return(                
); }else { return(
this.fetchData(resolve)} dataSource={
this.state.dataSource} renderRow={
this.renderRow} showsHorizontalScrollIndicator={
false} style={styles.listViewStyle} initialListSize={
5} /> ); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

无数据界面.png

listView 头部设置


  • 根据原版效果发现 提示标题 应该放到 ListView 的头部才对,所以这边就做下小修改。
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
  • renderHeader 方法实现
// 返回 listview 头部    renderHeader() {        return (            
根据每条折扣的点击进行统计,每5分钟更新一次
); }
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9

ListView头部.gif

下拉刷新


  • 为了避免适配问题带来的麻烦,这边我们采用第三方框架 react-native-pull 实现下拉刷新和上拉加载更多的功能。
this.fetchData(resolve)} dataSource={
this.state.dataSource} renderRow={
this.renderRow} showsHorizontalScrollIndicator={
false} style={styles.listViewStyle} initialListSize={
5} renderHeader={
this.renderHeader} />
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
  • fetchData 方法修改
// 网络请求    fetchData(resolve) {        setTimeout(() => {            fetch('http://guangdiu.com/api/gethots.php')                .then((response) => response.json())                .then((responseData) => {                    this.setState({                        dataSource: this.state.dataSource.cloneWithRows(responseData.data),                        loaded:true,                    });                    if (resolve !== undefined){                        setTimeout(() => {                            resolve();  // 关闭动画                        }, 1000);                    }                })                .done();        });    } 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

下拉刷新.gif

网络请求之POST(重要)


  • GET 和 POST 是我们请求 HTTP 接口常用的方式,针对表单提交的请求,我们通常采用 POST 的方式。

  • 在  中,传入对象框架会自动封装成 formData 的形式,但是在 fetch 中没有这个功能,所以我们需要自己初始化一个 FormData 直接传给body (补充:FormData也可以传递字节流实现上传图片功能)。

let formData = new FormData();    formData.append("参数", "值");    formData.append("参数", "值");    fetch(url, {        method:'POST,        headers:{},        body:formData,        }).then((response)=>{            if (response.ok) {                return response.json();            }        }).then((json)=>{            alert(JSON.stringify(json));        }).catch.((error)=>{            console.error(error);        }) 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • 如果想详细了解 Fetch ,可以点击  查看。

首页模块


  • 这边我们按照前面提到的步骤,进行数据的加载。
import React, { Component } from 'react';    import {        StyleSheet,        Text,        View,        TouchableOpacity,        Image,        ListView,        Dimensions    } from 'react-native';    // 第三方    import {PullList} from 'react-native-pull';    const {width, height} = Dimensions.get('window');    // 引用外部文件    import CommunalNavBar from '../main/GDCommunalNavBar';    import CommunalHotCell from '../main/GDCommunalHotCell';    import HalfHourHot from './GDHalfHourHot';    import Search from './GDSearch';    export default class GDHome extends Component {
// 构造 constructor(props) { super(props); // 初始状态 this.state = { dataSource: new ListView.DataSource({rowHasChanged:(r1, r2) => r1 !== r2}), loaded:true, }; this.fetchData = this.fetchData.bind(this); } // 网络请求 fetchData(resolve) { let formData = new FormData(); formData.append("count", "30"); setTimeout(() => { fetch('http://guangdiu.com/api/getlist.php', { method:'POST', headers:{}, body:formData, }) .then((response) => response.json()) .then((responseData) => { this.setState({ dataSource: this.state.dataSource.cloneWithRows(responseData.data), loaded:true, }); if (resolve !== undefined){ setTimeout(() => { resolve(); }, 1000); } }) .done(); }); } // 跳转到近半小时热门 pushToHalfHourHot() { this.props.navigator.push({ component: HalfHourHot, }) } // 跳转到搜索 pushToSearch() { this.props.navigator.push({ component:Search, }) } // 返回左边按钮 renderLeftItem() { return(
{
this.pushToHalfHourHot()}} >
); } // 返回中间按钮 renderTitleItem() { return(
); } // 返回右边按钮 renderRightItem() { return(
{
this.pushToSearch()}} >
); } // 根据网络状态决定是否渲染 listview renderListView() { if (this.state.loaded === false) { return(
); }else { return(
this.fetchData(resolve)} dataSource={
this.state.dataSource} renderRow={
this.renderRow} showsHorizontalScrollIndicator={
false} style={styles.listViewStyle} initialListSize={
5} renderHeader={
this.renderHeader} /> ); } } // 返回每一行cell的样式 renderRow(rowData) { return(
); } componentDidMount() { this.fetchData(); } render() { return (
{
/* 导航栏样式 */}
this.renderLeftItem()} titleItem = {() => this.renderTitleItem()} rightItem = {() => this.renderRightItem()} /> {
/* 根据网络状态决定是否渲染 listview */} {
this.renderListView()}
); } } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', backgroundColor: 'white', }, navbarLeftItemStyle: { width:20, height:20, marginLeft:15, }, navbarTitleItemStyle: { width:66, height:20, }, navbarRightItemStyle: { width:20, height:20, marginRight:15, }, listViewStyle: { width:width, }, });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
  • OK,这边也已经成功拿到数据,所以接着就是完成 cell 样式部分就可以了。

首页数据效果.gif

效果:


  • 有时候我们需要在跳转的时候使用不同的跳转动画,比如我们 半小时热门 的跳转方式在  内叫 模态跳转,特性就是当页面退出后会直接销毁,多用于注册、登录等不需要常驻内存的界面。

  • react-native 中为了方便实现这样的功能,我们可以在初始化 Navigator 的时候,在 ‘configsSence’ 中进行操作;具体操作如下:

// 设置跳转动画configureScene={(route) => this.setNavAnimationType(route)}// 设置Navigator跳转动画    setNavAnimationType(route) {        if (route.animationType) {  // 有值            return route.animationType;        }else {            return Navigator.SceneConfigs.PushFromRight;        }    } 
1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 这样我们在需要跳转的地方只需要传入相应的参数即可。
// 跳转到近半小时热门        pushToHalfHourHot() {            this.props.navigator.push({                component: HalfHourHot,                animationType:Navigator.SceneConfigs.FloatFromBottom            })        } 
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9

navigator跳转动画.gif

关闭 Navigator 返回手势

  • 上面操作后,发现这边有个小细节就是我们使用了 ` 作为跳转动画,但是当我们下拉的时候,动画中默认附带的 **返回手势** 会干扰我们ListView的滑动手势,这个怎么解决呢?其实很简单,我们只要关闭Navigator手势就可以了嘛,怎么关闭呢?其实在源码中我们可以找到,手势包含在动画中,我们如果不需要,只需要给其赋值为null` ,这样它就不知道需要响应手势事件了,方法如下:
// 设置Navigator跳转动画    setNavAnimationType(route) {        if (route.animationType) {  // 有值            let conf = route.animationType;            conf.gestures = null;   // 关闭返回手势            return conf;        }else {            return Navigator.SceneConfigs.PushFromRight;        }    } 
1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12

navigator返回手势关闭.gif

  • 这样我们就成功关闭了 Navigator 手势功能。

上拉加载更多


  • react-native-pull 框架的上拉加载使用也很简单,配合 onEndReachedonEndReachedThresholdrenderFooter使用
loadMore() {        // 数据加载操作    }    renderFooter() {        return (            
); } // 根据网络状态决定是否渲染 listview renderListView() { if (this.state.loaded === false) { return(
); }else { return(
this.fetchData(resolve)} dataSource={
this.state.dataSource} renderRow={
this.renderRow} showsHorizontalScrollIndicator={
false} style={styles.listViewStyle} initialListSize={
5} renderHeader={
this.renderHeader} onEndReached={
this.loadMore} onEndReachedThreshold={
60} renderFooter={
this.renderFooter} /> ); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

上拉加载更多.gif

网络请求基础封装


  • 到这里,相信各位对 React-Native 有所熟悉了吧,从现在开始我们要慢慢往实际的方向走,这边就先从网络请求这部分开始,在正式开发中,网络请求一般都单独作为一部分,我们在需要使用的地方只需要简单调用一下即可,这样做的好处是让整个 工程 的结构更加清晰,让组件们各司其职,只管好自己该管的事,并且后期维护成本也会相应降低。

  • 首先,我们要先对 fetch 的 GET 和 POST 请求方式进行一层基础封装,也就是要把它们单独独立出来,那么这边先来看下 GET 这边:

var HTTPBase = {};    /**     *     * GET请求     *     * @param url     * @param params {}包装     * @param headers     *     * @return {Promise}     *     * */    HTTPBase.get = function (url, params, headers) {        if (params) {            let paramsArray = [];            // 获取 params 内所有的 key            let paramsKeyArray = Object.keys(params);            // 通过 forEach 方法拿到数组中每个元素,将元素与参数的值进行拼接处理,并且放入 paramsArray 中            paramsKeyArray.forEach(key => paramsArray.push(key + '=' + params[key]));            // 网址拼接            if (url.search(/\?/) === -1) {                url += '?' + paramsArray.join('&');            }else {                url += paramsArray.join('&');            }        }        return new Promise(function (resolve, reject) {            fetch(url, {                method:'GET',                headers:headers            })                .then((response) => response.json())                .then((response) => {                    resolve(response);                })                .catch((error) => {                    reject({status:-1})                })                .done();        })    } 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  • 好,这边我们 GET 就封装好了,简单使用一下:
fetchData(resolve) {        HTTPBase.get('http://guangdiu.com/api/gethots.php')            .then((responseData) => {                this.setState({                    dataSource: this.state.dataSource.cloneWithRows(responseData.data),                    loaded:true,                });                if (resolve !== undefined){                    setTimeout(() => {                        resolve();  // 关闭动画                    }, 1000);                }            })            .catch((error) => {            })    }    export default HTTPBase; 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  • 接着,我们继续来对 POST 进行封装:
/**     *     * POST请求     *     * @param url     * @param params {}包装     * @param headers     *     * @return {Promise}     *     * */    HTTPBase.post = function (url, params, headers) {        if (params) {            // 初始化FormData            var formData = new FormData();            // 获取 params 内所有的 key            let paramsKeyArray = Object.keys(params);            // 通过 forEach 方法拿到数组中每个元素,将元素与参数的值进行拼接处理,并且放入 paramsArray 中            paramsKeyArray.forEach(key => formData.append(key, params[key]));        }        return new Promise(function (resolve, reject) {            fetch(url, {                method:'POST',                headers:headers,                body:formData,            })                .then((response) => response.json())                .then((response) => {                    resolve(response);                })                .catch((error) => {                    reject({status:-1})                })                .done();        })    }    export default HTTPBase; 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  • 好,来试一下:
// 网络请求    fetchData(resolve) {        let params = {
"count" : 5 }; HTTPBase.post('http://guangdiu.com/api/getlist.php', params) .then((responseData) => { this.setState({ dataSource: this.state.dataSource.cloneWithRows(responseData.data), loaded:true, }); if (resolve !== undefined){ setTimeout(() => { resolve(); }, 1000); } }) .catch((error) => { }) }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

  • 这次篇幅有点短,实在是太忙了!
  • http://blog.csdn.net/yeshaojian/article/details/65023875

你可能感兴趣的文章
【TINY4412】LINUX学习笔记:(4)sysfs、debugfs、procfs文件系统
查看>>
搭建Linux平台PHP、MySQL、Apache环境
查看>>
搭建Windows平台PHP、MySQL、Apache环境
查看>>
【TINY4412】QT5移植笔记:(1)tslib移植
查看>>
【TINY4412】QT5移植笔记:(2)QT5.7移植到Linux
查看>>
【TINY4412】QT5移植笔记:(3)QT5.6移植到Linux
查看>>
STM32 USB HID报告描述符分析
查看>>
STM32 USB 从机HID分析
查看>>
STM32 USB 枚举分析
查看>>
STM32 USB转串口分析
查看>>
java对象序列化
查看>>
GIT和GitHub联合工作流程
查看>>
PHP中SESSION与COOKIE区别与联系
查看>>
桌面应用程序自动检测更新
查看>>
Windows搭建Tomcat开发环境
查看>>
GitHub与Hexo搭建个人博客
查看>>
Hexo主题优化
查看>>
在Linux上部署Hexo
查看>>
LINUX学习笔记:(1)编写应用程序
查看>>
LINUX学习笔记:(2)编写驱动模块
查看>>