React RN移动端开发

React RN移动端开发

了解React-Native

Facebook发起的开源的一套新的APP开发方案,Facebook在当初深入研究Hybrid(其实就是手机内置的浏览器,12306)开发后,觉得这种模式有先天的缺陷,所以果断放弃,转而自行研究,后来推出了自己的“React Native”方案,不同于H5,也不同于原生Native,更像是用JS写出原生应用

ReactJs和React Native的原理是相同的,都是由js实现的虚拟dom来驱动界面view层渲染。只不过ReactJs是驱动html dom渲染; React Native是驱动android/ios原生组件渲染。

  • 优点
  1. 开发成本小于Native模式 Andriod-Java-Kotlin IOS-OC-Swift

  2. 性能体验高于Hybrid

  3. 一次学习,跨平台开发Android和iOS, 小程序

  4. 社区繁荣

  • 几种开发技术对比应用质量开发效率的平衡折衷的结果

在这里插入图片描述

  • ReactNative原理

在这里插入图片描述

它里面有一个虚拟dom的东西,我们写的每一个组件都是dom中的一个节点,最终都会翻译成各个平台的组件 比如web 我们写的div都会翻译过去,但是自定义的hello标签会翻译成符合规则的html标签

  • 三种开发类型的对比

在这里插入图片描述

了解React-Native工作流程

  1. 项目开发:使用Node初始化项目(需要安装Node),使用JavaScript/JSX语言开发
  2. 语言翻译:Python, C++将js翻译成java代码(需要安装Python2)
  3. 代码编译:Android-SDK将java编译成字节码(二进制),打包成可安装的apk(需要JDK8 & Android-SDK)
  4. 安装运行:通过Adb工具,把apk运行到Android模拟器

ReactNative中文网(查看流程和必须)

https://reactnative.cn/docs/getting-started/

创建第一个React-Native项目

注意 Node 的版本必须高于 8.3,Python 的版本必须为 2.x(不支持 3.x),而 JDK 的版本必须是 1.8(目前不支持 1.9 及更高版本)

  • 安装python环境

  • 配置SDK

    • 创建ANDROID_HOME环境变量 ANDROID_HOME=G:\sdk
    • 添加path %ANDROID_HOME%\platform-tools
  • 打开usb调试连接手机

    • adb devices查看是否有连接
    • 可以通过adb connect 127.0.0.1:62001尝试连接
      • 如果连接不上可以把sdkplatform-tools里面的adb.exe拷贝到夜神模拟器下替换adb.exenox_adb.exe
  • 安装脚手架react-native-cli 同时安装新的版包管理工具

    npm install -g yarn react-native-cli

  • 创建项目:doubanMovie(在不包含中文的目录执行)

    react-native init doubanMovie --version react-native@0.55.4

    建议指定–version 不然使用最新的 以来的android sdk版本比较新

  • 运行项目

    • 更改gradle路径doubanMovie\android\gradle\wrapper\gradle-wrapper.properties

      • distributionUrl值修改为file:///E:/myandroid/gradle-2.14.1-all.zip 直接复制过来的路径要把反斜线\改成正斜线/
    • 在项目根目录执行react-native run-android

      运行期间会开启一个Node服务,不要关闭

      react-native start 只会开启服务

    • 第一次运行报错,需要在设备上设置app的Node服务地址

      解决方式: 打开app > 菜单按钮 > Dev Settings -> Debug server host …

      填写服务ip和端口号, 注意冒号用英文半角,重启服务,重启应用

出现问题:https://blog.csdn.net/jasonzds/article/details/78747524

了解React-Native项目及结构

  • 目录结构

    • .babelrc 配置编译reactnative代码
    • .flowconfig js语法规则 js会有语法提示 类型检查工具
    • index.js 项目入口文件
    • App.js 项目根组件,用户看到的首页组件
    • package.json 项目配置文件
    • app.json 配置项目名称
  • React-Native与React对比

    • 组件写法

      RN提供View,Text组件,没有html的dom元素

      View -> div 布局容器

      Text -> p 显示文字

    • 样式写法

      使用const styles = StyleSheet.create({...})

  • React-Native平台相关代码处理

    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',
    });
    

开发资料

配置reactnative文件模板

  • 打开setting

  • File and Code template

  • 添加模板 名称以及后缀名

  • 把下面这段代码添加到文件中

    import React from 'react'
    import {
        StyleSheet,
        Text,
        View
    } from 'react-native';
    
    class ${NAME} extends React.Component {
        render() {
            return (
                <View>
                    <Text>#[[$Title$]]#</Text>
                </View>
            );
        }
    }
    
    export default ${NAME}
    

项目开发

路由(react-native-router-flux)

  • react-native-router-flux

  • 源码地址:https://github.com/aksonov/react-native-router-flux

    应用场景:在RN项目中进行路由跳转时使用

    安装方式:yarn add react-native-router-flux

  • 使用:

    Router(路由): 一般写在项目的根组件

    Stack (栈):给Scene场景提供容器

    Scene(场景):设置路由跳转规则

    Actions (动作):触发路由跳转

  • 导入依赖

    //导入路由  重点
    import {Router, Stack, Scene} from 'react-native-router-flux'
    
  • 定义路由规则

    export default class App extends Component<Props> {
      render() {
        return (
          <Router>
            <Stack>
              <Scene key='home' component={HomeScene} title='主界面' initial={true}></Scene>
              <Scene key='movieList' component={MovieListScene} title='主界面'></Scene>
              <Scene key='about' component={AboutScene} title='主界面'></Scene>
            </Stack>
          </Router>
        );
      }
    }
    
  • 触发路由:三种方式 (注意导入Actions组件)

    import {Actions} from 'react-native-router-flux'
    <Text onPress={Actions.movieList}>电 影</Text>
    <Text onPress={() => { Actions.movieList()}}>电 影</Text>
    <Text onPress={() => { Actions['about'].call() }}>关 于</Text>
    

注意事项:需要导入Router Stack Scene 界面中使用View以及Text不能使用div

###去除警告

  • 最新版的react-native-router-flux会在react-native 0.55.4版本出现isMounted(…)警告,可在App.js添加以下代码忽略警告。随后两个框架更新后,此警告也可消除。

    import { YellowBox } from 'react-native'
    YellowBox.ignoreWarnings(['Warning: isMounted(...) is deprecated'])
    

###首页三个标题

路由跳转

样式的定义

import React from 'react'
import {Actions} from 'react-native-router-flux'
import {
    StyleSheet,
    Text,
    View
} from 'react-native';

class HomeScene extends React.Component {
    render() {
        return (
            <View style={styles.container}>
                <Text style={styles.item} onPress={Actions.movie}>正在上映</Text>
                <Text style={styles.item} onPress={Actions.movie}>即将上映</Text>
                <Text style={styles.item} onPress={Actions.about}>关于</Text>
            </View>
        );
    }
}
let styles = StyleSheet.create({
    container:{
        display: 'flex',
        flexDirection: 'row',
        backgroundColor:'#311B92',
        height:30,
        alignItems: 'center'
    },
    item:{
        flex: 1,
        textAlign: 'center',
        color:'#fff'
    }
})
export default HomeScene

开发组件(swiper)

  • 开源轮播图react-native-swiper

  • 源码地址:https://github.com/leecade/react-native-swiper

    应用场景:在首页展示轮播图

    安装方式:yarn add react-native-swiper

    ​ 或npm i react-native-swiper --save

  • 常用属性:

    index={1} 					默认位置,0开始
    showsButtons={true} 		 是否显示按钮
    autoplayTimeout={2.5} 		 自动播放停留时间
    autoplay={true}				是否自动播放
    showsPagination={true}		 显示分页指示器
    
  • 导入组件

    import Swiper from 'react-native-swiper';
    
  • 使用Swiper

    import React from 'react'
    import {Actions} from 'react-native-router-flux'
    import {
        StyleSheet,
        Text,
        View
    } from 'react-native';
    
    //导入Swiper
    import Swiper from 'react-native-swiper';
    class HomeScene extends React.Component {
        render() {
            return (
                <View style={{height:180}}>
                    <Swiper style={styles.wrapper}
                            showsButtons={false}
                            autoplay={true}
                            autoplayTimeout={1}>
                        <View style={styles.slide1}>
                            <Text style={styles.text}>Hello Swiper</Text>
                        </View>
                        <View style={styles.slide2}>
                            <Text style={styles.text}>Beautiful</Text>
                        </View>
                        <View style={styles.slide3}>
                            <Text style={styles.text}>And simple</Text>
                        </View>
                    </Swiper>
                    <View style={styles.container}>
                        <Text style={styles.item} onPress={Actions.movie}>正在上映</Text>
                        <Text style={styles.item} onPress={Actions.movie}>即将上映</Text>
                        <Text style={styles.item} onPress={Actions.about}>关于</Text>
                    </View>
                </View>
    
    
            );
        }
    }
    let styles = StyleSheet.create({
        container:{
            display: 'flex',
            flexDirection: 'row',
            backgroundColor:'#311B92',
            height:30,
            alignItems: 'center'
        },
        item:{
            flex: 1,
            textAlign: 'center',
            color:'#fff'
        },
    
    
        wrapper: {
        },
        slide1: {
            flex: 1,
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: '#9DD6EB',
        },
        slide2: {
            flex: 1,
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: '#97CAE5',
        },
        slide3: {
            flex: 1,
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: '#92BBD9',
        },
        text: {
            color: '#fff',
            fontSize: 30,
            fontWeight: 'bold',
        }
    })
    export default HomeScene
    

    注意:swiper不能控制高度,需要在父标签view中指定高度

替换swiper中条目为图片

使用Image控件

  • 引入Image控件

    import {
        StyleSheet,
        Text,
        View,
        Image
    } from 'react-native';
    
  • 使用Image

    <Swiper style={styles.wrapper}
                            showsButtons={false}
                            autoplay={true}
                            autoplayTimeout={1}>
                        <View style={styles.slide1}>
                            <Image style={{width:'100%',height:'100%'}} source={{uri:'http://www.itheima.com/images/slidead/HOMEPAGE/20180613114526968x385.jpg'}}/>
                        </View>
                        <View style={styles.slide2}>
                            <Image style={{width:'100%',height:'100%'}} source={{uri:'http://www.itheima.com/images/slidead/HOMEPAGE/2018051409245620185529165539341.jpg'}}/>
                        </View>
                        <View style={styles.slide3}>
                            <Image style={{width:'100%',height:'100%'}} source={require('../static/images/blockchain.png')}/>
                        </View>
                    </Swiper>
    

    注意:Image中设置图片地址分为两种方式

    ​ 第一种:source={{uri:'http://www.itheima.com/images/slidead/HOMEPAGE/2018051409245620185529165539341.jpg'}

    ​ 第二种:source={require('../static/images/blockchain.png')}

###长列表FlatList展示电影列表

https://facebook.github.io/react-native/docs/using-a-listview

import React from 'react'
import {
    StyleSheet,
    Text,
    View,
    FlatList
} from 'react-native';
class MovieListScene extends React.Component {
    constructor(){
        super()
        this.state = {
            data:[{key: 'Devin'},
                {key: 'Jackson'},
                {key: 'James'},
                {key: 'Joel'},
                {key: 'John'},
                {key: 'Jillian'},
                {key: 'Jimmy'},
                {key: 'Julie'}]

        }
    }
    render() {
        return (
            <View>
                <FlatList data={this.state.data}
                renderItem={({item,index})=>{
                    return<Text>{item.key}</Text>
                }}/>
                <Text>movie列表界面</Text>
            </View>
        );
    }
}

export default MovieListScene

注意:

​ 数据data需要有key属性

网络请求(fetch)

豆瓣api查找一下

  • Axios

  • fetch

  • componentDidMount执行请求并在回调中执行setState

    // 组件已经挂载
    componentDidMount() {
        const url = 'http://api.douban.com/v2/movie/in_theaters';
        fetch(url).then(res => res.json())
            .then(data => {
            // 处理网络json数据
            this.setState({
                isLoading: false,
                data: data.subjects
            })
            // console.warn(data.subjects)
        }).catch((err) => {
            console.error(err);
        });
    }
    
  • 请求完之后记得替换key

    keyExtractor={(item, index) => item.title}
    

###加载进度条展示ActivityIndicator

导入

import {
    StyleSheet,
    Text,
    View,
    FlatList,
    ActivityIndicator
} from 'react-native';

使用

if(this.state.isLoading){
            return(<View><ActivityIndicator size='large' color='#f00'></ActivityIndicator></View>)
        }

条目数据展示

import React from 'react'
import {
    StyleSheet,
    Text,
    View,
    FlatList,
    ActivityIndicator,
    Image
} from 'react-native';
class MovieListScene extends React.Component {
    constructor(){
        super()
        this.state = {
            isLoading:true,
            data:[{key: 'Devin'},
                {key: 'Jackson'},
                {key: 'James'},
                {key: 'Joel'},
                {key: 'John'},
                {key: 'Jillian'},
                {key: 'Jimmy'},
                {key: 'Julie'}]

        }
    }
    render() {
        if(this.state.isLoading){
            return(<View><ActivityIndicator size='large' color='#f00'></ActivityIndicator></View>)
        }
        return (
            <View>
                <FlatList data={this.state.data}
                renderItem={({item,index})=>{
                    return(
                        <View style={styles.container}>
                            <Image source={{uri:item.images.large}} style={styles.img}></Image>
                            <View style={styles.content_wrapper}>
                                <Text style={{fontWeight:'bold',fontSize:20}}>{item.title}</Text>
                                <Text style={styles.item}>电影类型:{item.genres.join(',')}</Text>
                                <Text style={styles.item}>上映年份:{item.year}</Text>
                                <Text style={styles.item}>豆瓣评分:{item.rating.average}</Text>
                            </View>
                        </View>
                    )
                }}
                keyExtractor={(item, index) => item.title}/>
            </View>
        );
    }

    componentDidMount() {
        const url = 'http://api.douban.com/v2/movie/in_theaters'
        fetch(url).then(res=>res.json())
            .then(data=>{
                // console.error(data)
                this.setState({
                    data:data.subjects,
                    isLoading:false
                })
            })
            .catch((error)=>{
                console.error('失败:'+error)
                this.setState({
                    isLoading:false
                })
            })
    }

}
let styles = StyleSheet.create({
   container:{
     padding: 10,
       display: 'flex',
       flexDirection: 'row',
       borderBottomColor:'#ccc',
       borderBottomWidth: 1
   },
    img:{
        width:100,
        height:140
    },
    content_wrapper:{
        display: 'flex',
        flexDirection: 'column',
        justifyContent: 'space-around',
        marginLeft: 10
    },
    item: {
        fontSize: 18,
    },
})
export default MovieListScene

长列表条目显示和点击事件

触摸变色的控件TouchableOpacity

activeOpacity属性控制透明度

  • 对于条目view不能设置onPress 可以通过 TouchableOpacity

  • 条目点击跳转

return (
            <View>
                <FlatList data={this.state.data}
                renderItem={({item,index})=>{
                    return(
                        <TouchableOpacity style={styles.container}
                                          onPress={()=>Actions.detail({id:item.id,title:item.title})}
                                          activeOpacity={0.6}>
                            <Image source={{uri:item.images.large}} style={styles.img}></Image>
                            <View style={styles.content_wrapper}>
                                <Text style={{fontWeight:'bold',fontSize:20}}>{item.title}</Text>
                                <Text style={styles.item}>电影类型:{item.genres.join(',')}</Text>
                                <Text style={styles.item}>上映年份:{item.year}</Text>
                                <Text style={styles.item}>豆瓣评分:{item.rating.average}</Text>
                            </View>
                        </TouchableOpacity>
                    )
                }}
                keyExtractor={(item, index) => item.title}/>
            </View>
        );

加载电影详情数据

import React from 'react'
import {
    StyleSheet,
    Text,
    View
} from 'react-native';

class MovieDetailScene extends React.Component {
    render() {
        return (
            <View>
                <Text>电影详情</Text>
            </View>
        );
    }

    componentDidMount() {
        let url = `http://api.douban.com/v2/movie/subject/${this.props.id}`
        fetch(url)
            .then((res)=>res.json())
            .then((result)=>{
                console.error(result)
            })
            .catch((error)=>{
                console.warn(error)
            })
    }

}

export default MovieDetailScene

详情界面加载进度条

###显示详情界面数据

  • 标题和大图片展示,注意resizeMode

    return (
                <View>
                    <Text style={styles.title}>{this.state.data.title}</Text>
                    <Image style={styles.img} source={{uri:this.state.data.images.large}}></Image>
                </View>
            );
    
    let styles = StyleSheet.create({
        title:{
            textAlign:'center',
            fontSize:30,
            fontWeight:'bold'
        },
        img:{
            height: 250,
            marginTop:20,
            resizeMode:'contain'
        }
    })
    
  • 演员表滚动 使用Scrollview 数组.map 方法

     <ScrollView horizontal={true} showsHorizontalScrollIndicator={false}>
                        {
                            this.state.data.casts.map((item, index, arr) => {
                                return (
                                    <View style={{padding:10}} key={item.id}>
                                        <Image source={{uri: item.avatars.small}}
                                               style={{width:50, height:80}}/>
                                        <Text>{item.name}</Text>
                                    </View>
                                )
                            })
                        }
                    </ScrollView>
    
  • 其它布局实现

    <Text style={{fontSize:20}}>剧情介绍:</Text>
                    <Text style={{marginBottom:10}}>{this.state.data.summary}</Text>
    

打包及发布

参见中文官网文档:https://reactnative.cn/docs/0.51/signed-apk-android.html#content

  • 第一步:生成签名文件

    keytool -genkey -v -keystore itheima.keystore -alias itheima -keyalg RSA -keysize 2048 -validity 10000
    
  • 把生成的itheima.keystore放到android/app

  • 编辑andorid下的gradle.properties文件加上

    MYAPP_RELEASE_STORE_FILE=itheima.keystore
    MYAPP_RELEASE_KEY_ALIAS=itheima
    MYAPP_RELEASE_STORE_PASSWORD=123456
    MYAPP_RELEASE_KEY_PASSWORD=123456
    
  • 编辑androidapp下的build.gradle

    android {
        ...
        defaultConfig { ... }
        signingConfigs {
            release {
                storeFile file(MYAPP_RELEASE_STORE_FILE)
                storePassword MYAPP_RELEASE_STORE_PASSWORD
                keyAlias MYAPP_RELEASE_KEY_ALIAS
                keyPassword MYAPP_RELEASE_KEY_PASSWORD
            }
        }
        buildTypes {
            release {
                ...
                signingConfig signingConfigs.release
            }
        }
    }
    
  • cd到android文件夹下执行下面命令打包

    gradlew assembleRelease
    
  • 执行安装

    gradlew installRelease
    
  • 注意:如果安装到手机出现显示welcome界面 执行首先清空app下main下的assets文件夹 执行下面命令

    react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res/
    
  • 修改图标

    • [项目名称]\android\app\src\main\res\mipmap-xhdpi
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值