db reactnative - CSDN
精华内容
参与话题
  • React Native悬浮效果组件

    千次阅读 2018-05-27 21:47:44
    由于其他的原因,对于React Native相关的内容最近没有投入太多的关注,从去年年底出版了《React Native移动开发实战》后,对于React Native的关注就比较少了。最近由于公司之前的项目需要,所以React Native又重新...

    由于其他的原因,对于React Native相关的内容最近没有投入太多的关注,从去年年底出版了《React Native移动开发实战》后,对于React Native的关注就比较少了。最近由于公司之前的项目需要,所以React Native又重新回到我的世界,并且,最近出去面试深深的感觉到原生开发的饱和,不管是Android还是iOS,移动市场基本已经饱和,而更多的公司和开发者开始转向了前端,这对于移动开发人员,特别是有过跨平台开发经验或者小程序开发经验的开发者来说,学习前端是异常的容易。因此,我后面的目光也主要放在跨平台和大前端上。

    对React Native发展历史比较了解的同学都知道,React Native早期除了性能外,生态也是特别差的,但是在经过了2017年的优化和发展之后,现在跨平台开发如React Native和Weex可以说是相当的吃香。并且,随着跨平台生态的逐渐形成,跨平台的组件和文章也越来越多。

    今天给大家讲的是一个可以实现悬浮效果的组件,效果如下:
    这里写图片描述
    这里写图片描述

    该库的源码地址为:https://github.com/mastermoo/react-native-action-button

    安装

    在项目中使用如下的命令安装react-native-action-button库:

    npm i react-native-action-button --save
    

    因为用到了react-native-vector-icons图标组件,需要还需要做下link,命令如下:

    react-native link react-native-vector-icons

    或者使用下面的命令执行link。

    react-native link

    使用实例

    首先导入该。

    import ActionButton from 'react-native-action-button';

    例如下面是一个具体的实例代码:

    import React, { Component } from 'react';
    import { StyleSheet, View } from 'react-native';
    import ActionButton from 'react-native-action-button';
    import Icon from 'react-native-vector-icons/Ionicons';
    
    
    class App extends Component {
    
      render() {
        return (
          <View style={{flex:1, backgroundColor: '#f3f3f3'}}>
            {/* Rest of the app comes ABOVE the action button component !*/}
            <ActionButton buttonColor="rgba(231,76,60,1)">
              <ActionButton.Item buttonColor='#9b59b6' title="New Task" onPress={() => console.log("notes tapped!")}>
                <Icon name="md-create" style={styles.actionButtonIcon} />
              </ActionButton.Item>
              <ActionButton.Item buttonColor='#3498db' title="Notifications" onPress={() => {}}>
                <Icon name="md-notifications-off" style={styles.actionButtonIcon} />
              </ActionButton.Item>
              <ActionButton.Item buttonColor='#1abc9c' title="All Tasks" onPress={() => {}}>
                <Icon name="md-done-all" style={styles.actionButtonIcon} />
              </ActionButton.Item>
            </ActionButton>
          </View>
        );
      }
    
    }
    
    const styles = StyleSheet.create({
      actionButtonIcon: {
        fontSize: 20,
        height: 22,
        color: 'white',
      },
    });

    其中,ActionButton组件是一个容器组件,即我们上面看到的“+”组件,而ActionButton.Item组件则是子组件。这有点类似于Android的RadioGrop和RadioButton的关系。

    参数说明

    ActionButton

    • size:按钮的大小,默认为56
    • active:是否显示按钮
    • position:按钮的位置,可以为left center right
    • offsetX:X轴上的偏移位置
    • offsetY:Y轴上的偏移位置
    • onPress:点击事件
    • onLongPress:长按事件
    • buttonText:按钮标题
    • verticalOrientation:弹出按钮的方向,up 或者 down
    • renderIcon:可以自定义按钮显示的样式,默认是一个加号

    ActionButton.Item

    • size:按钮的大小,默认为56
    • title:按钮标题
    • buttonColor:按钮颜色
    • onPress:点击事件

    当然除了上面介绍的一些常用属性外,react-native-action-button还有一些其他的属性,大家可以通过官方资料来学习。

    展开全文
  • React Native制作仿美团APP总结

    千次阅读 2018-05-05 19:35:13
    最近学习了一个网上的React Native项目,利用React Native制作一个类似于美团的App,项目属于对之前React Native常用组件的基本使用,但是仍有一些关键点值得记录。最后做成的效果如下:1、通过React Navigation来...

            最近学习了一个网上的React Native项目,利用React Native制作一个类似于美团的App,项目属于对之前React Native常用组件的基本使用,但是仍有一些关键点值得记录。最后做成的效果如下:

    1、通过React Navigation来搭建整体的页面框架

        可以看到这个App大致分为四个板块:主页、商家、我的、更多,分别对应四个标签导航,可以利用React native提供的TabNavigator来实现四个标签页的导航。每个页面对应一个组件,新建一个专门的文件夹component用于存放这些组件。

    import HomeStack from './component/Home/HomeScreen'
    import ShopStack from './component/Shop/ShopScreen'
    import MoreScreen from './component/MoreScreen'
    import MineScreen from './component/MineScreen'
    
    const Main=TabNavigator (
      {
        Home:{screen:HomeStack},       //标签页Home对应HomeScreen组件
        Shop:{screen:ShopStack},
        Mine:{screen:MineScreen},
        More:{screen:MoreScreen},
      },

        以上是一级页面,在点击其中的图标后可以跳转到响应的二级详情页面,可以用React Navigation中的StackNavigator来实现详情页的跳转。相应的,对于复杂的模块,可能不止包含一个组件模块,这就需要为它新建文件夹来存放它的组件了,例如主页Home文件夹下包含了主页面HomeScreen及详情页HomeDetail,以及其他的组件。


    2、组件化的开发思维

        2.1组件的相同

            使用React开发网页最便捷的地方就是它可以把相同的部分提炼成组件,通过组件化的思路来渲染页面,可以免去很多重复的代码。首先实现最简单的页面:更多

                

        可以看到这个页面的元素类型都是单一的选项条,只不过显示的文字不同,可以把每一条抽象成为一个类Cell,当传入不同的属性时,显示不同的文字

    class Cell extends Component{
      render(){
        return(
          <TouchableOpacity activeOpacity={0.5} onPress={this.props.cellFunction}>
            <View style={styles.cellBar}>
              <Text style={{fontSize:16}}>{this.props.title}</Text>
              <Image style={styles.cellImage} source={{uri:'icon_cell_rightarrow'}} />
            </View>
          </TouchableOpacity>
        )
      }
    }
        在页面调用Cell组件:
    <Cell title="消息提醒"/>
    <Cell title="邀请好友"/>
    <Cell title="清空缓存"/>

    2.2、组件的不同

        在类似的组件中,难免存在不同的地方,需要根据不同的情况进行分别处理,例如接下来实现的“我的”页面:

                

        页面中也存在许多类似的选项条:“我的订单”、“钱包”、“抵用券”......它们整体结构分为左边图标、标题,右边文字、箭头,但仔细看“今日推荐”右边没有文字,而是一个图标“new”。这就需要在调用时传入属性来规定是否渲染图标以及渲染怎样的图标。在react中会根据是否传入参数来决定是否渲染,例如在组件定义时将右边的文字、图标都写上:

    {/*右边的文字*/}
    <Text>{this.props.infoText}</Text>
    {/*右边的图标*/}
    <Image source={{uri:this.props.badgeSrc}} style={{width:30,height:18}} />
    {/*右边的箭头*/}
    <Image style={styles.cellArrow} source={{uri:'icon_cell_rightarrow'}} />
    但在调用组件时有的组件需要文字,就传入属性infoText,有的组件需要图标,就传入badgeSrc,不传入的属性就不会被渲染:
    <Cell title='今日推荐' iconSrc="icon_mine_recommend" badgeSrc="icon_cell_new" />
    <Cell title='我要合作' iconSrc="icon_mine_corporation" infoText="轻松开店" />

    3、Flex布局的使用

        通过flex可以很快捷地将组件布局为想要的样式,例如通过flexDirection:'row',justifyContent:'space-between'可以将元素分布于左边、右边,而不是设置float浮动:


        或者通过justifyContent:'space-around'将元素均匀分布在一行,而不必根据屏幕大小计算每个元素的大小与margin,通过alignItem:'center',在竖直方向上元素居中


    4、FlatList与ScrollView的使用

        在渲染一组相似结构的数据时,可以利用FlatList,我们只需要定义其中一个元素的渲染方法,就可以把一组数据渲染出来。

        当希望页面可以上下或左右滚动时,需要使用ScrollView。例如下面主页的菜单:

                

            每个菜单选项都是一个图标加一个文字,可以通过flatlist来渲染,菜单可以左右滑动来切换两个FlatList,需要在外面包装一个ScrollView组件。

    <View style={styles.menu}>
      <ScrollView horizontal={true} showsHorizontalScrollIndicator={false}
                  pagingEnabled={true} onMomentumScrollEnd={(e)=>this.slideMenu(e)}
      >
        <FlatList data={this.props.listData[0]} style={styles.menuList}
                  keyExtractor = {(item, index) => index.toString()}
                  renderItem={this.renderMenuItem} numColumns={5}
                  columnWrapperStyle={styles.menuColumn}
        />
        <FlatList data={this.props.listData[1]} style={styles.menuList}
                  keyExtractor = {(item, index) => index.toString()}
                  renderItem={this.renderMenuItem} numColumns={5}
                  columnWrapperStyle={styles.menuColumn}
        />
      </ScrollView>
      <View style={styles.indicateBar}>
        {/*渲染底部指示标签点*/}
        {this.renderIndicate()}
      </View>
    </View>

    5、动态数据加载

        App中的数据不可能是写死的,而是从网络上随时动态请求的,但是在页面中呈现的格式却是固定的。我们可以动态的从网络上请求数据,然后将这些数据通过属性传递给对应的组件模块,就可以实现动态的数据渲染。

        在组件挂载之后利用fetch请求数据并保存到state中:

    componentDidMount() {
      let shopUrl="http://api.meituan.com/group/v2/recommend/homepage/city/20?userId=160495643...";
     fetch(shopUrl).then((res)=>res.json())
        .then((resJson)=>{
          this.setState({
            shopList:resJson.data
          });
        }).catch((err)=>{
        console.log(err);
      })
    }
    

        例如以下为一条请求的数据,其中包括渲染标题的颜色、主标题、副标题、图片地址、对应的跳转链接等,

    {
            "position": 0,
            "typeface_color": "#ff9900",
            "id": 7486,
            "share": {
                "message": "1元能吃肯德基",
                "url": "http://i.meituan.com/firework/kfchanbao"
            },
            "title": "1元吃肯德基",
            "module": false,
            "maintitle": "1元肯德基",
            "tplurl": "imeituan://www.meituan.com/web?url=http://i.meituan.com/firework/kfchanbao",
            "type": 1,
            "imageurl": "http://p0.meituan.net/w.h/groupop/9aa35eed64db45aa33f9e74726c59d938450.png",
            "solds": 0,
            "deputytitle": "新用户专享"
     }

        接着就需要把这些数据填充到界面上,界面上的显示模块是固定的,例如主页中的活动模块如下:

       

        可以看到活动广告模块可以分为三类:MediumBlock(左上角粉色框)、SmallBlock(绿色框)、LargeBlock(蓝色框),可以将这三类框分别抽象为组件,然后排布到页面上。例如SmallBlock.js:

    export default class SmallBlock extends Component {
      render() {
        return (
          <TouchableOpacity style={styles.container}>
            <View>
              <Text style={[{color:this.props.data.typeface_color},styles.title]}>
                {this.props.data.title}
              </Text>
              <Text>{this.props.data.deputytitle}</Text>
            </View>
            <Image source={{uri:this.handleUrl(this.props.data.imageurl)}} style={styles.image}/>
    
          </TouchableOpacity>
        )
      }
      handleUrl(url){
        let imageUrl='';
        if(url.indexOf('w.h')===-1){
          imageUrl=url;
        }else {//美团的图片url中有w.h字段,代表图片的长与宽,需要替换后才能得到图片
          imageUrl=url.replace('w.h','60.60');
        }
        return imageUrl;
      }
    }
        在页面中调用组件,并填充数据:
    <SmallBlock data={this.props.shopList[4]}/>

    6、反向事件绑定

        例如将商家页面shopScreen.js中的“购物中心”封装成为一个组件ShopCenter,

            

            当点击它时跳转到详情页shopDetail.js,但是在每个ShopCenter组件中是没办法处理跳转事件的,只有在ShopScreen类中才可以访问到navigation对象,实现跳转。因此需要在ShopScreen中调用ShopCenter组件时,为其绑定一个事件属性onClick(这个属性名可自定),然后在ShopCenter组件中点击时调用该属性触发父组件中对应的事件:

        例如父组件中调用子组件ShopCenter以及绑定onClick属性为jumpDetail函数:

    <ShopCenter key={index} data={item} onClick={this.jumpDetail}/>
    ...
    jumpDetail(url){
      navigation.navigate('Detail',url);
    }
        其中变量navigation是this.props.navigation,是由StackNavigator传递给它的子组件的,我直接使用时,会报错this.props未定义,于是我把它保存到一个全局变量navigation中,然后再调用其navigate方法。

        在子组件ShopCenter中点击触发jumpTo函数来调用父组件属性onClick

    export default class ShopCenter extends Component {
      render() {
        return (
          <TouchableOpacity  style={styles.container}
            onPress={()=>this.jumpTo(this.props.data.detailurl)}
          >
            <Image source={{uri:this.props.data.img}} style={styles.image} />
            <Text style={styles.imageLabel}>{this.props.data.showtext.text}</Text>
            <Text style={styles.name}>{this.props.data.name}</Text>
          </TouchableOpacity>
        )
      }
    
      jumpTo(detailurl){
        let url=detailurl;//对url进行处理,去掉url前面没用的部分
        url=detailurl.replace('imeituan://www.meituan.com/web/?url=','');
        this.props.onClick({url:url});//触发父组件onOnclick,并传入url参数
      }
    }

    7、Hybrid开发思维

           App中并不是所有的页面都是写死的,这样很不易于维护与更新。一些页面是通过网页来实现的,在App中点击时跳转到对应的网页。当我们想要修改时,只需要更新在服务器端网页就可以,而不必更新App、重新发布等。这种思维就是一种Hybrid混合开发的思维。

            例如当点击购物中心时跳转到ShopDetail页面,并通过navigation传入对应网页的url,在ShopDetail中只需通过<WebView>组件将网页呈现出来即可。

        

            ShopDetail.js就只有很短几行用于呈现WebView:

    export default class ShopDetail extends Component {
      static navigationOptions={
        title:'商场详情',
        headerStyle:{                                 //导航栏样式设置
          backgroundColor:'#8bffce',
        },
      };
      render() {
        let url=this.props.navigation.state.params.url+ '?uuid=5C7B6342814C7B496D836A69C872';
       return (
          <WebView source={{uri: url}}
                   javaScriptEnabled={true}
                   domStorageEnabled={true}
          />
        )
      }
    }

    8、打包发布

        之前一直通过debug来将react native安装到手机上,如果需要发行则需要打包生成apk。

    8.1、生成签名

        Android要求所有应用都有一个数字签名才会被允许安装在用户手机上,所有首先需要生成一个签名密钥。要通过keytool生成密钥,首先进入jdk下的bin目录,打开cmd输入如下命令

    keytool -genkey -v -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000

        其中my-release-key为密钥库的名字,my-key-alias为密钥库别名可以自定义,接着会出现命令行提示,要求输入相关信息,并设置相关密码,之后会在当前目录下生成my-release-key.keystore文件。

        把该文件拷贝到react native工程下的android/app目录下

    8.2设置全局gradle

        在C:\Users\你的用户名\.gradle目录下新建gradle.properties文件,并在其中输入如下内容:

    MYAPP_RELEASE_STORE_FILE=my-release-key.keystore     密钥库的名字
    MYAPP_RELEASE_KEY_ALIAS=my-key-alias                 密钥库别名
    MYAPP_RELEASE_STORE_PASSWORD=*****                   密钥库密码 
    MYAPP_RELEASE_KEY_PASSWORD=*****                     密钥密
          我的密钥密码与库密码一致

    8.3、配置项目的gradle文件

        打开react native项目下的android/app/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
            }
        }
    }
        

    8.4、生成apk

        进入react native项目的android目录下执行cmd命令:

    gradlew assembleRelease
        生成的apk文件位于项目的android/app/build/outputs/apk/app-release.apk


    在GitHub上的代码仓库为:https://github.com/SuperTory/React-Native-ECommerce

    展开全文
  • (一)前言 前面的课程我们已经对React Native的环境搭建以及开发的IDE做了相关的讲解,今天我们的主要讲解的是应用设备运行(Running)以及调试方法(Debugging)。本节的前提条件就是大家已经搭建了React Native的相关...

    转载请标明出处:

    http://blog.csdn.net/developer_jiangqq/article/details/50508534

    本文出自:【江清清的博客】


    ()前言       

            【好消息】个人网站已经上线运行,后面博客以及技术干货等精彩文章会同步更新,请大家关注收藏:http://www.lcode.org

           前面的课程我们已经对React Native的环境搭建以及开发的IDE做了相关的讲解,今天我们的主要讲解的是应用设备运行(Running)以及调试方法(Debugging)本节的前提条件就是大家已经搭建了React Native的相关环境。如果没有请关注第一讲(点击进入)

             刚创建的React Native技术交流3群(496508742),React Native技术交流4群(458982758),请不要重复加群!欢迎各位大牛,React Native技术爱好者加入交流!同时博客左侧欢迎微信扫描关注订阅号,移动技术干货,精彩文章技术推送!

    ()创建React Native项目  

            需要运行和调试应用,首先我们需要使用react-native init xxproject来创建一个项目,这个步骤虽然在之前我们已经讲过了,不过这边在演示一下。

            2.1.命令行运行:react-native init TestOne   [].该命令创建项目时间有时候会比较耗时,一般几分钟吧,请耐心等待!运行日志截图如下:


           2.2.在相应目录生成项目,项目的目录机构如下:


          2.3.最后我们通过IDE引入该项目(Atom或者WebStorm),进行编写代码即可。

    ()应用设备运行(Running)

             [注意].如果你需要应用运行在真机设备中,那么我们首先设备要开启USB调试模式。具体真机怎么样打开USB调试模式,请百度哈(点击打开USB调试模式)

             真机打开USB调试模式之后,然后连接电脑,命令行adb devices可以查看当前的设备列表信息,不过我现在电脑没有连接真机,所有只有会显示模拟器信息了。      


    现在大家可以看到里边有一台设备已经连接了,不过如果我们需要运行应用的话,那我们必须确保当前只有一台设备已经连接即可了。接下来我们命令行运行以下命令:

    react-native run-android


    接着就是开始编译代码,然后运行程序到设备中了。

    [注意]

    在真机上运行时可能会遇到白屏的情况,请找到并开启悬浮窗权限。比如小米系统设置(点击设置)

            3.1.从设备上面访问实时服务器数据(这边服务器指本地代码)

    当我们启动开发服务的情况下,我们可以通过以下两种方法来更新远程代码来快速的更新修改我们的应用

          [注意]现在很多Android设备以及那个去除了Menu按键了,这时候我们可以通过摇晃设备来进行打开菜单,然后完成应用的重新加载,调试以及其他功能。

    • Android 5.0以上及更高版本,可以使用adb reverse命令

    首先你的设备连接电脑,然后打开USB调试模式。接着命令行运行

    adb reverse tcp:8081 tcp:8081

    然后我们就可以使用Reload JS和其他的开发选项了。

    • Android 以下版本可以通过Wifi连接

    .手机和电脑USB连接并且电脑和手机设备在同一个Wifi网络环境下

    .react-native run-android运行应用。

    .现在发现"红色",继续下面的步骤进行解决,截图如下:


    .摇晃设备输入adb shell input keyevent 82,打开开发者菜单,如下效果:


    .Dev Settings进入,然后选择Debug server host& port for device

    .输入电脑IP址和端口号(主要查看电脑的IP地址哦,这边用我这边的IP地址和端口,具体要根据实际情况哦),截图如下:


    .回到开者菜单,然后选择点击Reload JS。重新加载以下即可。


    ()应用调试(Debugging)-针对Android应用设备

            4.1.访问应用内开发者菜单       

            Android设备我们可以摇晃或者点击菜单键(不过现在很多手机已经没有这个模拟按键了)。如果你使用的是genymotion模拟器,你可以打开应用,然后发现屏幕右下方有一个箭头,点击然后点击菜单图标即可。截图如下:



    [注意]如果我们的APP正式发布的话(Release版本)。默认情况下我们采用gradle的assembleRelease来进行构建即可。或者通过代码ReactInstanceManagersetUseDeveloperSupport方法来进行设置是否开启调试支持。

     4.2.应用刷新

            正常情况下,如果我们只是修改应用的JS代码的话,那么我们可以直接点击Reload JS选择实时刷新即可。但是如果我们修改Android项目中的资源文件(例如res/drawable文件中图片)或者修改Android的源代码,那么就需要重新编译生成应用才可以生效。

     4.3.Chrome开发调试工具

           开发的应用进行调试的时候,我们可以使用Chrome来调试js代码,点击开发菜单中的Debugin Chrome。然后会打开一个网页: http://localhost:8081/debugger-ui   界面截图如下:


    不过第一次打开需要安装ReactDevTools(最好翻墙一下)

    安装方法教程:

    http://facebook.github.io/react/blog/2015/09/02/new-react-developer-tools.html       

    Chrome添加开发调试插件:


    安装完插件,回退到原来的界面,然后刷新一下即可:


            如何打开开发者工具:

    想必做过Web前端开发的人都知道:Chrome中可以使用option+cammod+i打开或者Chrome选择菜单-更多工具-开发者工具来进行打开控制台。不过如果我们的程序出现异常话,可以开启(Pause On Caught Exceptions)。这样程序出现异常的时候,程序会暂停执行可以更好的调试错误。

    真机调试方法:

           .针对Android 5.0或更高版本的设备,可以通过USB连接,然后使用adb命令建立一个设备到电脑的转向端口:命令如下:

    adb reverse tcp:8081 tcp:8081

    或者摇晃打开开发者菜单,选择DevSettings,然后Debug server host for device中设置电脑的IP和端口号。

           .针对Android5.0以下的设备,连接方式Wifi,其他步骤差不多哦。

     4.4..实时刷新JS

            我们可以进行那个如下修改,当我们前端JS代码发生更改的时候,自动让设备进行刷新界面。

             Android平台上面,打开开发者菜单,选择Dev Settings,然后点击Auto reload on JS change选择,不过有些版本好像没有这个更选项了,默认自动刷新的。

    ()最后总结

              今天我们主要讲解了React Native应用设备运行方式和真机方式,主要材料来自React Native官网,这边所有步骤我已经全部测试过下的。因为讲解起来和实际使用还是回遇到各种问题的。大家有问题可以加一下群React Native技术交流群(282693535)或者底下进行回复一下。

           尊重原创,转载请注明:From Sky丶清(http://blog.csdn.net/developer_jiangqq) 侵权必究!

           关注我的订阅号(codedev123),每天分享移动开发技术(Android/IOS),项目管理以及博客文章!(欢迎关注,第一时间推送精彩文章)

    关注我的微博,可以获得更多精彩内容

     

    展开全文
  • 本篇前两部分内容简单介绍一下ReactNative,后面的章节会把整个RN框架的iOS部分,进行代码层面的一一梳理 全文是不是有点太长了,我要不要分拆成几篇文章 函数栈代码流程图,由于采用层次缩进的形式,层次关系比较...

    本篇前两部分内容简单介绍一下ReactNative,后面的章节会把整个RN框架的iOS部分,进行代码层面的一一梳理

    全文是不是有点太长了,我要不要分拆成几篇文章

    函数栈代码流程图,由于采用层次缩进的形式,层次关系比较深的话,不是很利于手机阅读,

    ReactNative 概要

    ReactNative,动态,跨平台,热更新,这几个词现在越来越火了,一句使用JavaScript写源生App吸引力了无数人的眼球,并且诞生了这么久也逐渐趋于稳定,携程,天猫,QZone也都在大产品线的业务上,部分模块采用这个方案上线,并且效果得到了验证(见2016 GMTC 资料PPT)

    我们把这个单词拆解成2部分

    • React

    熟悉前端的朋友们可能都知道React.JS这个前端框架,没错整个RN框架的JS代码部分,就是React.JS,所有这个框架的特点,完完全全都可以在RN里面使用(这里还融入了Flux,很好的把传统的MVC重组为dispatch,store和components,Flux架构

    所以说,写RN哪不懂了,去翻React.JS的文档或许都能给你解答

    以上由@彩虹 帮忙修正

    • Native

    顾名思义,纯源生的native体验,纯源生的UI组件,纯原生的触摸响应,纯源生的模块功能

    那么这两个不相干的东西是如何关联在一起的呢?

    React.JS是一个前端框架,在浏览器内H5开发上被广泛使用,他在渲染render()这个环节,在经过各种flexbox布局算法之后,要在确定的位置去绘制这个界面元素的时候,需要通过浏览器去实现。他在响应触摸touchEvent()这个环节,依然是需要浏览器去捕获用户的触摸行为,然后回调React.JS

    上面提到的都是纯网页,纯H5,但如果我们把render()这个事情拦截下来,不走浏览器,而是走native会怎样呢?

    当React.JS已经计算完每个页面元素的位置大小,本来要传给浏览器,让浏览器进行渲染,这时候我们不传给浏览器了,而是通过一个JS/OC的桥梁,去通过[[UIView alloc]initWithFrame:frame]的OC代码,把这个界面元素渲染了,那我们就相当于用React.JS绘制出了一个native的View

    拿我们刚刚绘制出得native的View,当他发生native源生的- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event触摸事件的时候,通过一个OC/JS的桥梁,去调用React.JS里面写好的点击事件JS代码

    这样React.JS还是那个React.JS,他的使用方法没发生变化,但是却获得了纯源生native的体验,native的组件渲染,native的触摸响应

    于是,这个东西就叫做React-Native

    ReactNative 结构

    大家可以看到,刚才我说的核心就是一个桥梁,无论是JS=>OC,还是OC=>JS。

    刚才举得例子,就相当于把纯源生的UI模块,接入这个桥梁,从而让源生UI与React.JS融为一体。

    那我们把野心放长远点,我们不止想让React.JS操作UI,我还想用JS操作数据库!无论是新玩意Realm,还是老玩意CoreData,FMDB,我都希望能用JS操作应该怎么办?好办,把纯源生的DB代码模块,接入这个桥梁

    如果我想让JS操作Socket做长连接呢?好办,把源生socket代码模块接入这个桥梁。如果我想让JS能操作支付宝,微信,苹果IAP呢?好办,把源生支付代码模块接入这个桥梁

    由此可见RN就是由一个bridge桥梁,连接起了JS与na的代码模块

    • 链接了哪个模块,哪个模块就能用JS来操作,就能动态更新
    • 发现现有RN框架有些功能做不到了?扩展写个na代码模块,接入这个桥梁

    这是一个极度模块化可扩展的桥梁框架,不是说你从facebook的源上拉下来RN的代码,RN的能力就固定一成不变了,他的模块化可扩展,让你缺啥补上啥就好了

    ReactNative 结构图

    RN结构图

    大家可以看这个结构图,整个RN的结构分为四个部分,上面提到的,RN桥的模块化可扩展性,就体现在JSBridge/OCBridge里的ModuleConfig,只要遵循RN的协议RCTBridgeModule去写的OC Module对象,使用RCT_EXPORT_MODULE()宏注册类,使用RCT_EXPORT_METHOD()宏注册方法,那么这个OC Module以及他的OC Method都会被JS与OC的ModuleConfig进行统一控制

    RN类图

    上面是RN的代码类结构图

    • 大家可以看到RCTRootView是RN的根试图,

      • 他内部持有了一个RCTBridge,但是这个RCTBridge并没有太多的代码,而是持有了另一个RCTBatchBridge对象,大部分的业务逻辑都转发给BatchBridge,BatchBridge里面写着的大量的核心代码

        • BatchBridge会通过RCTJavaScriptLoader来加载JSBundle,在加载完毕后,这个loader也没什么太大的用了

        • BatchBridge会持有一个RCTDisplayLink,这个对象主要用于一些Timer,Navigator的Module需要按着屏幕渲染频率回调JS用的,只是给部分Module需求使用

        • RCTModuleXX所有的RN的Module组件都是RCTModuleData,无论是RN的核心系统组件,还是扩展的UI组件,API组件

          • RCTJSExecutor是一个很特殊的RCTModuleData,虽然他被当做组件module一起管理,统一注册,但他是系统组件的核心之一,他负责单独开一个线程,执行JS代码,处理JS回调,是bridge的核心通道
          • RCTEventDispatcher也是一个很特殊的RCTModuleData,虽然他被当做组件module一起管理,统一注册,但是他负责的是各个业务模块通过他主动发起调用js,比如UIModule,发生了点击事件,是通过他主动回调JS的,他回调JS也是通过RCTJSExecutor来操作,他的作用是封装了eventDispatcher得API来方便业务Module使用。

    后面我会详细按着代码执行的流程给大家细化OCCode里面的代码,JSCode由于我对前端理解还不太深入,这个Blog就不会去拆解分析JS代码了

    ReactNative通信机制可以参考bang哥的博客 React Native通信机制详解

    ReactNative 初始化代码分析

    我会按着函数调用栈类似的形式梳理出一个代码流程表,对每一个调用环节进行简单标记与作用说明,在整个表梳理完毕后,我会一一把每个标记进行详细的源码分析和解释

    下面的代码流程表,如果有类名+方法的,你可以直接在RN源码中定位到具体代码段

    • RCTRootView-initWithBundleURLXXX(RootInit标记)
      • RCTBridge-initWithBundleXXX
        • RCTBridge-createBatchedBridge(BatchBridgeInit标记
          • New Displaylink(DisplaylinkInit标记
          • New dispatchQueue (dispatchQueueInit标记)
          • New dispatchGroup (dispatchGroupInit标记)
          • group Enter(groupEnterLoadSource标记
            • RCTBatchedBridge-loadSource (loadJS标记)
          • RCTBatchedBridge-initModulesWithDispatchGroup(InitModule标记 这块内容非常多,有个子代码流程表)
          • group Enter(groupEnterJSConfig标记
            • RCTBatchedBridge-setUpExecutor(configJSExecutor标记
            • RCTBatchedBridge-moduleConfig(moduleConfig标记
            • RCTBatchedBridge-injectJSONConfiguration(moduleConfigInject标记
          • group Notify(groupDone标记
            • RCTBatchedBridge-executeSourceCode(evaluateJS标记
            • RCTDisplayLink-addToRunLoop(addrunloop标记

    RootInit标记:所有RN都是通过init方法创建的不再赘述,URL可以是网络url,也可以是本地filepath转成URL

    BatchBridgeInit标记:前边说过rootview会先持有一个RCTBridge,所有的module都是直接操作bridge所提供的接口,但是这个bridge基本上不干什么核心逻辑代码,他内部持有了一个batchbrdige,各种调用都是直接转发给RCTBatchBrdige来操作,因此batchbridge才是核心

    RCTBridge在init的时候调用[self setUp]

    RCTBridge在setUp的时候调用[self createBatchedBridge]

    DisplaylinkInit标记:batchbridge会首先初始化一个RCTDisplayLink这个东西在业务逻辑上不会被所有的module调用,他的作用是以设备屏幕渲染的频率触发一个timer,判断是否有个别module需要按着timer去回调js,如果没有module,这个模块其实就是空跑一个displaylink,注意,此时只是初始化,并没有run这个displaylink

    dispatchQueueInit标记:会初始化一个GCDqueue,后面很多操作都会被扔到这个队列里,以保证顺序执行

    dispatchGroupInit标记:后面接下来进行的一些列操作,都会被添加到这个GCDgroup之中,那些被我做了group Enter标记的,当group内所有事情做完之后,会触发group Notify

    groupEnterLoadSource标记:会把无论是从网络还是从本地,拉取jsbundle这个操作,放进GCDgroup之中,这样只有这个操作进行完了(还有其他group内操作执行完了,才会执行notify的任务)

    loadJS标记:其实就是异步去拉取jsbundle,无论是本地读还是网络啦,[RCTJavaScriptLoader loadBundleAtURL:self.bundleURL onComplete:onSourceLoad];只有当回调完成之后会执行dispatch_group_leave,离开group

    InitModule标记:这个函数是在主线程被执行的,但是刚才生成的GCD group会被当做参数传进内部,因为内部的一些逻辑是需要加入group的,这个函数内部很复杂 我会继续绘制一个代码流程表

    • 1)RCTGetModuleClasses() 

    一个C函数,RCT_EXPORT_MODULE()注册宏会在+load时候把Module类都统一管理在一个static NSArray里,通过RCTGetModuleClasses()可以取出来所有的Module

    • 2)RCTModuleData-initWithModuleClass 

    此处是一个for循环,循环刚才拿到的array,对每一个注册了得module都循环生成RCTModuleData实例

    • 3)配置moduleConfig

    每一个module在循环生成结束后,bridge会统一存储3分配置表,包含了所有的moduleConfig的信息,便于查找和管理

    
     
    
     
    • 4)RCTModuleData-instance

    这是一个for循环,每一个RCTModuleData都需要循环instance一下,需要说明的是,RCTModuleData与Module不是一个东西,各类Module继承自NSObject,RCTModuleData内部持有的instance实例才是各类Module,因此这个环节是初始化RCTModuleData真正各类Module实例的环节

    通过RCTModuleData-setUpInstanceAndBridge来初始化创建真正的Module

    
     
    
     

    这里需要说明,每一个Module都会创建一个自己独有的专属的串行GCD queue,每次js抛出来的各个module的通信,都是dispatch_async,不一定从哪个线程抛出来,但可以保证每个module内的通信事件是串行顺序的

    每一个module都有个bridge属性指向,rootview的bridge,方便快速调用

    • 5)RCTJSCExecutor 

    RCTJSCExecutor是一个特殊的module,是核心,所以这里会单独处理,生成,初始化,并且被bridge持有,方便直接调用

    RCTJSCExecutor初始化做了很多事情,需要大家仔细关注一下

    创建了一个全新的NSThread,并且被持有住,绑定了一个runloop,保证这个线程不会消失,一直在loop,所有与JS的通信,一定都通过RCTJSCExecutor来进行,所以一定是在这个NSThread线程内,只不过各个模块的消息,会进行二次分发,不一定在此线程内

    • 6)RCTModuleData-gatherConstants

    每一个module都有自己的提供给js的接口配置表,这个方法就是读取这个配置表,注意!这行代码执行在主线程,但他使用dispatch_async 到mainQueue上,说明他先放过了之前的函数调用栈,等之前的函数调用栈走完,然后还是在主线程执行这个循环的gatherConstants,因此之前传进来的GCD group派上了用场,因为只有当所有module配置都读取并配置完毕后才可以进行 run js代码

    下面思路从子代码流程表跳出,回到大代码流程表的标记

    groupEnterJSConfig标记:代码到了这块会用到刚才创建,但一直没使用的GCD queue,并且这块还比较复杂,在这次enter group内部,又创建了一个子group,都放在这个GCD queue里执行

    如果觉得绕可以这么理解他会在专属的队列里执行2件事情(后面要说的2各标记),当这2个事情执行完后触发子group notify,执行第三件事情(后面要说的第三个标记),当第三个事情执行完后leave母group,触发母group notify

    
     
    
     

    configJSExecutor标记:再次专门处理一些JSExecutor这个RCTModuleData

    1)property context懒加载,创建了一个JSContext

    2)为JSContext设置了一大堆基础block回调,都是一些RN底层的回调方法

    moduleConfig标记:把刚才所有配置moduleConfig信息汇总成一个string,包括moduleID,moduleName,moduleExport接口等等

    moduleConfigInject标记:把刚才的moduleConfig配置信息string,通过RCTJSExecutor,在他内部的专属Thread内,注入到JS环境JSContext里,完成了配置表传给JS环境的工作

    groupDone标记:GCD group内所有的工作都已完成,loadjs完毕,配置module完毕,配置JSExecutor完毕,可以放心的执行JS代码了

    evaluateJS标记:通过[_javaScriptExecutor executeApplicationScript:script sourceURL:url onComplete:]来在JSExecutor专属的Thread内执行jsbundle代码

    addrunloop标记:最早创建的RCTDisplayLink一直都只是创建完毕,但并没有运作,此时把这个displaylink绑在JSExecutor的Thread所在的runloop上,这样displaylink开始运作

    小结

    整个RN在bridge上面,单说OC侧,各种GCD,线程,队列,displaylink,还是挺复杂的,针对各个module也都是有不同的处理,把这块梳理清楚能让我们更加清楚OC代码里面,RN的线程控制,更方便以后我们扩展编写更复杂的module模块,处理更多native的线程工作。

    后面的 js call oc oc call js 我也会以同样的方式进行梳理,让大家清楚线程上是如何运作的

    PS:JS代码侧其实bridge的设计也有一套,包括所有call oc messageQueue会有个队列控制之类的,我对JS不是那么熟悉和理解,JS侧的代码我就不梳理了。

    ReactNative JS call OC 代码分析

    既然整个RCTRootView都初始化完毕,并且执行了jsbundle文件了,整个RN就已经运作起来了,那么RN运作起来后,JS的消息通过JS代码的bridge发送出来之后,是如何被OC代码识别,分发,最重转向各个module模块的业务代码呢?我们接下来就会梳理,这个流程的代码

    JS call OC 可以有很多个方法,但是所有的方法一定会走到同一个函数内,这个关键函数就是

    - (void)handleBuffer:(id)buffer batchEnded:(BOOL)batchEnded

    需要说明的事,handleBuffer一定发生在RCTJSExecutor的Thread内

    正所谓顺藤摸瓜,我可以顺着他往上摸看看都哪里会发起js2oc的通信

    • [RCTJSExecutor setUp]

    可以看到这里面有很多JavaScriptCore的JSContext[“xxx”]=block的用法,这个用法就是JS可以把xxx当做js里面可以识别的function,object,来直接调用,从而调用到block得意思,可以看出来nativeFlushQueueImmediate当js主动调用这个jsfunction的时候,就会下发一下数据,从而调用handleBuffer,可以确定的是,这个jsfunction,会在jsbunlde run起来后立刻执行一次

    这个方法要特别强调一下,这是唯一个一个JS会主动调用OC的方法,其他的js调用OC,都他由OC实现传给JS一个回调,让JS调用。

    JS侧主动调用nativeFlushQueueImmediate的逻辑

    • [RCTBatchBridge enqueueApplicationScript:]

    可以看到这句代码只发生在执行jsbundle之后,执行之后会[RCTJSExecutor flushedQueue:callback]在callback里调用handleBuffer,说明刚刚执行完jsbundle后会由OC主动发起一次flushedQueue,并且传给js一个回调,js通过这个回调,会call oc,进入handleBuffer

    • [RCTBatchBridge _actuallyInvokeCallback:]
    • [RCTBatchBridge _actuallyInvokeAndProcessModule:]

    两个_actuallyInvoke开头的方法,用处都是OC主动发起调用js的时候,会传入一个call back block,js通过这个callback block回调,这两个方法最后都会执行[RCTJSExecutor _executeJSCall:]

    从上面可以看出JS只有一个主动调用OC的方法,其他都是通过OC主动调用JS给予的回调

    我们还可以顺着handleBuffer往下摸看看都会如何分发JS call OC的事件

    以handleBuffer为根,我们继续用函数站代码流程表来梳理

    • RCTBatchedBridge-handlebuffer
      • analyze Buffer(analyze buffer标记)
      • find module(find modules标记
      • for 循环all calls
      • dispatch async(dispatch async标记
        • [RCTBatchedBridge- handleRequestNumber:]
          • [RCTBridgeMethod invokeWithBridge:](invocation标记 这个标记会复杂点,子流程表细说) 

    analyze buffer标记:js传过来的buffer其实是一串calls的数组,一次性发过来好几个消息,需要OC处理,所以会解析buffer,分别识别出每一个call的module信息

    
     
    
     

    find modules标记:解析了buffer之后就要查找对应的module,不仅要找到RCTModuleData,同时还要取出RCTModuleData自己专属的串行GCD queue

    dispatch async标记:每一个module和queue都找到了就可以for循环了,去执行一段代码,尤其要注意,此处RN的处理是直接dispatch_async到系统随机某一个空闲线程,因为有模块专属queue的控制,还是可以保持不同模块内消息顺序的可控

    invocation标记:这个标记的作用就是真真正正的去调用并且执行对应module模块的native代码了,也就是JS最终调用了OC,这个标记内部还比较复杂,里面使用了NSInvocation去运行时查找module类进行反射调用

    invocation内部子流程如下

    解释一下,JS传给OC是可以把JS的回调当做参数一并传过来的,所以后面的流程中会特别梳理一下这种回调参数是如何实现的,

    • [RCTBridgeMethod-processMethodSignature](invocation预处理标记
      • argumentBlocks(参数处理标记
    • 循环压参(invocation压参标记
    • 反射执行Invocation调用oc

    invocation预处理标记:RN会提前把即将反射调用的selector进行分析,分析有几个参数每个参数都是什么类型,每种类型是否需要包装或者转化处理。 

    参数处理标记:argumentBlocks其实是包装或转化处理的block函数,每种参数都有自己专属的block,根据类型进行不同的包装转化策略

    此处别的参数处理不细说了,单说一下JS回调的这种参数是怎么操作的

    • JS回调通过bridge传过来的其实是一个数字,是js回调function的id
    • 我们在开发module的时候,RN让你声明JS回调的时候是声明一个输入参数为NSArray的block
    • js回调型参数的argumentBlocks的作用就是,把jsfunctionid进行记录,包装生成一个输入参数为NSArray的block,这个block会自动的调用[RCTBridge enqueueCallback:]在需要的时候回调JS,然后把这个block压入参数,等待传给module

    这块代码各种宏嵌套,还真是挺绕的,因为宏的形式,可读性非常之差,但是读完了后还是会觉得很风骚

    [RCTBridgeMethod processMethodSignature]这个方法,强烈推荐

    invocation压参标记:argumentBlocks可以理解为预处理专门准备的处理每个参数的函数,那么预处理结束后,就该循环调用argumentBlocks把每一个参数处理一下,然后压入invocation了

    后面就会直接调用到你写的业务模块的代码了,业务模块通过那个callback回调也能直接calljs了

    ReactNative OC call JS EventDispatcher代码分析

    我们编写module,纯源生native模块的时候,有时候会有主动要call js的需求,而不是通过js给的callback calljs

    这时候就需要RCTEventDispatcher了,可以看到他的头文件里都是各种sendEvent,sendXXXX的封装,看一下具体实现就会发现,无论是怎么封装,最后都走到了[RCTJSExecutor enqueueJSCall:],追中还是通过RCTJSExecutor,主动发起调用了JS

    他有两种方式

    • 直接立刻发送消息主动callJS
    • 把消息add进一个Event队列,然后通过flushEventsQueue一次性主动callJS

    ReactNative Displaylink 代码分析

    之前我们提到过一个RCTDisplayLink,没错他被添加到RCTJSExecutor的Thread所在的runloop之上,以渲染频率触发执行代码,执行frameupDate

    [RCTDisplaylink _jsThreadUpdate]

    在这个方法里,会拉取所有的需要执行frameUpdate的module,在module所在的队列里面dispatch_async执行didUpdateFrame方法

    在各自模块的didUpdateFrame方法内,会有自己的业务逻辑,以DisplayLink的频率,主动call js

    比如:RCTTimer模块

    RCTJSExecutor

    最后在强调下JSBridge这个管道的线程控制的情况

    刚才提到的无论是OC Call JS还是JS call OC,都只是在梳理代码流程,让你清楚,所有JS/OC之间的通信,都是通过RCTJSExecutor,都是在RCTJSExecutor内部所在的Thread里面进行

    如果发起调用方OC,并不是在JSThread执行,RCTJSExecutor就会把代码perform到JSThread去执行

    发起调用方是JS的话,所有JS都是在JSThread执行,所以handleBuffer也是在JSThread执行,只是在最终分发给各个module的时候,才进行了async+queue的控制分发。

    展开全文
  • 如果您曾经想过如何使用带有React Native的OpenCV处理图像,那么您来对地方了。 OpenCV与React Native一起使您能够在移动设备上处理图像 (最有可能要处理由设备相机拍摄的图像)。 这些的最大优点是: 易于实现。 ...
  • 本文原创首发于公众号:ReactNative开发圈,转载需注明出处。 React Native悬浮按钮组件:react-native-action-button,纯JS组件,支持安卓和IOS双平台,支持设置子按钮,支持自定义位置和样式和图标。 效果图 安装...
  • React Native 封装原生UI组件(iOS)

    千次阅读 2017-02-27 15:40:16
    React Native 在Facebook的React.js conf 2015上提出,至今一年多,组件数目肯定没得和原生的相比。 因此,在使用React Native开发App的过程中,我们可能需要调用RN没有实现的原生视图组件或第三方组件。甚至,...
  • React native 数据库 Realm的简单使用

    千次阅读 2018-08-28 17:01:50
    由于之前是做android 原生开发的,最近在学习混合开发,项目中也是用的react native 开发技术,在存储数据时简单点的比如说,用户的登录信息等,都可以存储在 AsyncStorage 中,这个的存储方式为key-value的形式,...
  • React Native 在Facebook的React.js conf 2015上提出,至今一年多,组件数目肯定没得和原生的相比。因此,在使用React Native开发App的过程中,我们可能需要调用RN没有实现的原生视图组件或第三方组件。甚至,我们...
  • 命令行进入到ReactNative项目根目录下执行 npm install react-native-sqlite-storage --save   2.进行全局Gradle设置 编辑 android/settings.gradle文件,添加以下内容 include ':react-native-s...
  • React Native制作圆形加载条

    千次阅读 2017-02-28 13:54:41
    先放运行截图说明做什么吧, ...当然对于大多数前端而言,这个并不是特别难的,可能思路众多,然而面对React Native似乎就有点相形见绌了。解决这样的问题,我们还是得回归前端本身,看看有什么可以嫁接的方案没。
  • React Native backgroundColor 的颜色值

    万次阅读 2016-07-21 14:00:23
    React Native 的 css 跟 web开发中css还是很相似的,其中 backgroundColor 支持的颜色值与 css 一致,为方便查找写在这里。 其他样式可以参考 CSS 样式网站 : http://css.doyoe.com/#basic Color ...
  • 最近一直在研究RN技术,想着找个项目练练手,之前就有使用vue、react技术开发过聊天项目,这次就使用reactNative技术开发个仿微信RN版。是基于 react-native+react-navigation+react-redux+react-native-image-...
  • Flutter VS ReactNative

    万次阅读 2018-07-25 14:45:47
    (2) 原生控件的跨平台抽象,如 ReactNative、Weex。 Flutter 走了不一样的路:自己开发了一套原生控件,每个平台实现一遍,然后把渲染引擎(这套控件)打包在每个应用里面,因此性能没有问题,平...
  • React Native从入门到放弃
  • 问题描述,github里react native社区的react-native-video 插件,一直以来存在安卓平台下无论怎么设props的rate={0}和pause={true}都会自动播放,不用琢磨了代码写对不对,其实是这个插件自己的问题。这个插件的问题...
  • React Native文件读写操作

    千次阅读 2020-06-11 17:01:25
    最近公司项目要求进行定时上传位置信息,及埋点,因为使用的是RN开发,一开始就是想到在Android和Ios原生里进行操作。...后来查找相关资料,发现了一个不错的第三方插件,react-native-fs,现在...
  • React Native利用Animated和SVG实现渐变进度条 根据项目需求,在React Native中实现渐变进度条的案例。 案例效果 案例分析 思路:将整个动画视图拆分成背景,进度,图文显示三个部分,分步实现,然后组合起来...
  • 1.react-native-fs是什么? 2.react-native-fs支持哪些功能? 3.react-native-fs如何使用? 4.react-native-fs功能介绍? 5.文件操作说明 5.1文件目录说明 5.2文件创建 5.3文件删除 5.4文件读取 5.5文件...
1 2 3 4 5 ... 20
收藏数 2,057
精华内容 822
热门标签
关键字:

db reactnative