[关闭]
@Bios 2018-12-10T08:39:19.000000Z 字数 7485 阅读 874

记React-native开发APP的过程

React-native app


初始化项目

本项目基于react ntive @0.51+ node@6.8

配置开发环境(window)

react-native init

初始化一个项目:react-native init my_app

项目名不能有横线'-',可以有下划线'_'

等待几分钟就会生成一个项目,该项目已经下载好了各个依赖插件。

通过 react-native run-android,此时如果正确连接了模拟器,则会在模拟器上安装一个app。

此时还不能正常运行,需要在模拟器上打开Dev Settings -> Debug server host & port for device
设置端口为ip:8081,例如192.168.10.209:8081.

怎样查看本机IP: 在CMD中运行ipconfig则可以查看对应ip。

安装配置mobx

yarn add mobx mobx-reactnpm i mobx mobx-react --save

mobx中涉及了ES7的装饰器(decorator),所以需要安装相应的插件支持。

npm i babel-plugin-transform-decorators-legacy babel-preset-react-native-stage-0 --save-dev
yarn add babel-plugin-transform-decorators-legacy babel-preset-react-native-stage-0 --dev

.babelrc文件中配置babel插件:

  1. {
  2. 'presets': ['react-native'],
  3. 'plugins': ['transform-decorators-legacy']
  4. }

安装react-navigation

译注:从0.44版本开始,Navigator被从react native的核心组件库中剥离到了一个名为react-native-deprecated-custom-components的单独模块中。如果你需要继续使用Navigator,则需要先npm i facebookarchive/react-native-custom-components安装,然后从这个模块中import,即import { Navigator } from ‘react-native-deprecated-custom-components’.——官网

npm i react-navigation --save

yarn add react-navigation

安装字体图标

npm install react-native-vector-icons --save

yarn add react-native-vector-icons

安卓关联图标:在项目根目录下执行
npm i -g rnpm
rnpm link

在需要使用的地方:
import Icon from 'react-native-vector-icons/FontAwesome';

使用组件:
<Icon name="qq" size={30} color="#52C0FE"/>

遇到图标不能显示却没有报错的问题,可以删除模拟器上的app,再执行一个react-native run-android

如果报错信息:

原因是因为插件的SDK的platform与项目自身的不同。
修改node_modules\react-native-vector-icons\android\build.gradle

  1. android {
  2. compileSdkVersion 23
  3. buildToolsVersion "23.0.1"
  4. defaultConfig {
  5. minSdkVersion 16
  6. targetSdkVersion 22
  7. versionCode 1
  8. versionName "1.0"
  9. }
  10. lintOptions {
  11. abortOnError false
  12. }
  13. }

再次执行react-native run-android就ok了。

封装fetch请求

  1. const delay = timeout => {
  2. return new Promise((resolve, reject) => {
  3. setTimeout(() => reject('请求超时!'), timeout * 1000)
  4. })
  5. }
  6. /**
  7. * http请求
  8. * @param {[type]} options.url [请求地址]
  9. * @param {String} options.method [请求方法]
  10. * @param {Object} options.params [请求参数]
  11. * @param {[type]} timeout} [超时时间]
  12. * @return {[type]} [Promise]
  13. */
  14. const httpReq = ({url,method = 'get',params = {}, timeout}) => {
  15. const paramArr = [];
  16. /**
  17. * var obj = {'a':'123','b':'345'};
  18. * console.log(Object.keys(obj)); //['a','b']
  19. */
  20. if (Object.keys(params).length !== 0) {
  21. for (const key in params) {
  22. paramArr.push(`${key}=${params[key]}`);
  23. }
  24. }
  25. const urlStr = `${url}?${paramArr.join('&')}`; // get请求url拼接
  26. const body = paramArr.join('&'); // post请求body传参
  27. let options = {};
  28. options = {
  29. method: 'POST',
  30. headers: {
  31.     'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
  32.    },
  33. body: body
  34. }
  35. if (method.toLowerCase() === 'get'){
  36. if (timeout === undefined) {
  37. return fetch(urlStr)
  38. } else {
  39. // Promise.race当数组中有一个promise返回则返回,其余的不再执行。如果超时了就不执行了
  40. return Promise.race([fetch(urlStr), delay(timeout)])
  41. }
  42. } else if (method.toLowerCase() === 'post') {
  43. if (timeout === undefined) {
  44. return fetch(url,options)
  45. } else {
  46. return Promise.race([fetch(url,options), delay(timeout)])
  47. }
  48. } else {
  49. return new Promise((resolve,reject) => {
  50. resolve('不支持该请求!');
  51. })
  52. }
  53. }
  54. export { httpReq }

错误处理

throwIIfClosurRequired 报错

"babel-preset-react-native": "5.0.0" ----> "babel-preset-react-native": "4.0.0"

知识点

flex布局

针对两个view的左右布局

  1. <View style={styles.productBox}>
  2. <View >
  3. <Text style={{color:'#444',fontSize:16}}>{item.productName}</Text>
  4. <View style={{flexDirection: 'row',alignItems: 'center',marginTop:3}}>
  5. <Image source={require('../assets/higher.png')}
  6. style={{width: 33, height:20}}
  7. resizeMode = "contain"
  8. />
  9. <Text style={{marginBottom:5,color:'#fc3d14'}}>{item.productPrice}</Text>
  10. </View>
  11. </View>
  12. <View >
  13. <Icon name="angle-right" size={30} color="#8f8888"/>
  14. </View>
  15. </View>
  16. const styles = StyleSheet.create({
  17. productBox: {
  18. width: width - 40,
  19. marginLeft: 20,
  20. marginTop: 20,
  21. borderRadius: 4,
  22. padding: 20,
  23. flexDirection: 'row',
  24. alignItems: 'center',
  25. justifyContent: 'space-between', // 针对view (未测试所有情形,建议用vie包裹)
  26. backgroundColor:'#fff'
  27. }
  28. })

Image 完全显示在屏幕中

  1. <View>
  2. <Image source={require('./assets/merchantBg_group/default_MB.png')}
  3. resizeMode = "contain"
  4. style={{width: width,marginTop:-65}}
  5. />
  6. </View>

多个样式用数组

  1. <View style={[styles.productBox,comStyle.card]}></View>

Platform

  1. import {
  2. Platform
  3. } from 'react-native';
  4. // 不同的平台显示不同的内容
  5. const instructions = Platform.select({
  6. ios: 'Press Cmd+R to reload,\n' +
  7. 'Cmd+D or shake for dev menu',
  8. android: 'Double tap R on your keyboard to reload,\n' +
  9. 'Shake or press menu button for dev menu',
  10. });
  11. type Props = {};
  12. export default class App extends Component<Props> {
  13. render() {
  14. return (
  15. <View style={styles.container}>
  16. <Text style={styles.welcome}>
  17. Welcome to React Native!
  18. </Text>
  19. <Text style={styles.instructions}>
  20. To get started, edit App.js
  21. </Text>
  22. <Text style={styles.instructions}>
  23. {instructions} // 直接调用
  24. </Text>
  25. </View>
  26. );
  27. }
  28. }

mobx使用(非注入,仅当Model与View的分离)

  1. // model/goods.js
  2. import { observable, action } from 'mobx';
  3. import { httpReq } from '../api/httpReq.js';
  4. export default class GoodsModel {
  5. @observable goodsList = []; // 被观察者
  6. @action // 改变数据
  7. getGoodsList = async (id) => {
  8. const url = 'http://dev.xyf.78dk.com/v5/firstproductlists';
  9. const method = 'post';
  10. const params = {
  11. merchantId: id || 1005
  12. }
  13. const responseData = await httpReq({url, method, params}).then(res => res.json());
  14. this.goodsList = [].concat(responseData.data.productList);
  15. }
  16. }
  1. // views/goods.js
  2. // 伪代码
  3. import React, { Component } from 'react';
  4. import {observer} from 'mobx-react';
  5. // 加载数据模型
  6. import GoodsModel from '../models/goods.js';
  7. import {
  8. StyleSheet,
  9. Text,
  10. View,
  11. Image,
  12. Dimensions,
  13. TouchableOpacity
  14. } from 'react-native';
  15. const {width,height} = Dimensions.get('window');
  16. import Icon from 'react-native-vector-icons/FontAwesome';
  17. @observer // 观察者
  18. export default class Goods extends Component{
  19. // new数据实例
  20. goodsModel = new GoodsModel();
  21. componentDidMount(){
  22. this.getProductList();
  23. }
  24. getProductList(){
  25. // 调用实例方法
  26. this.goodsModel.getGoodsList();
  27. }
  28. render() {
  29. // 拿取数据 (解构赋值)
  30. const { goodsList } = this.goodsModel;
  31. // 渲染
  32. let productItem = goodsList.length>0&&goodsList.map((item, index) => {
  33. return (
  34. <TouchableOpacity onPress={() => this.onPressBtn(index)} key={index}>
  35. <View style={styles.productBox}>
  36. <Text style={{color:'#444',fontSize:16}}>{item.productName}</Text>
  37. <Text style={{color:'#444',fontSize:16}}>{item.productPrice}</Text>
  38. </View>
  39. </TouchableOpacity>
  40. )
  41. })
  42. return (
  43. <View>
  44. <View style={{backgroundColor:"#fff"}}>
  45. ……
  46. </View>
  47. {productItem}
  48. </View>
  49. );
  50. }
  51. }
  52. const styles = StyleSheet.create({
  53. ……
  54. });

父组件传递方法到子组件

  1. // nextStep.js
  2. import React, { Component } from 'react';
  3. import {
  4. StyleSheet,
  5. View,
  6. Text,
  7. TouchableOpacity
  8. } from 'react-native';
  9. export default class NextStep extends Component {
  10. constructor(props){
  11. super(props);
  12. }
  13. onPressBtn(){
  14. this.props.click(); // 触发父组件传递的方法
  15. }
  16. render() {
  17. return (
  18. <TouchableOpacity onPress={() => this.onPressBtn()}>
  19. <View style = {styles.nextStep}>
  20. <View style= {styles.btn}>
  21. <Text style={styles.text}>下一步</Text>
  22. </View>
  23. </View>
  24. </TouchableOpacity>
  25. );
  26. }
  27. }
  28. const styles = StyleSheet.create({
  29. ……
  30. });
  1. // index.js
  2. import React, { Component } from 'react';
  3. import {
  4. StyleSheet,
  5. Text,
  6. View,
  7. } from 'react-native';
  8. import NextStep from '../components/nextStep.js';
  9. export default class Index extends Component{
  10. componentDidMount(){
  11. this.getProductList();
  12. }
  13. // 传递到子组件的方法
  14. click(){
  15. alert(1);
  16. }
  17. render() {
  18. return (
  19. <View>
  20. ……
  21. <NextStep click={() => this.click()}></NextStep>
  22. </View>
  23. );
  24. }
  25. }
  26. const styles = StyleSheet.create({
  27. ……
  28. });

路由

  1. // route/index.js
  2. import { StackNavigator } from 'react-navigation';
  3. // 继续增加新的页面
  4. import Goods from '../views/goods'
  5. import GoodsDetail from '../views/goods-detail'
  6. const AppNavi = StackNavigator({
  7. Goods: {
  8. screen: Goods
  9. },
  10. GoodsDetail: {
  11. screen: GoodsDetail
  12. }
  13. },{
  14. initialRouteName: 'Goods' // 初始化的页面
  15. });
  16. export default AppNavi;
  1. // app.js
  2. import React, { Component } from 'react';
  3. import AppNavi from './src/routes'
  4. import {
  5. View,
  6. Text
  7. } from 'react-native';
  8. export default class App extends Component {
  9. render() {
  10. return (
  11. <AppNavi />
  12. );
  13. }
  14. }

ScrowView

  1. <View style={{height: 300}}>
  2. <ScrowView>
  3. .......
  4. </ScrowView>
  5. </View>
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注