key警告 native react_react native map key - CSDN
  • 我们在项目里面,经常会用的批次渲染,比如一个列表渲染很多个item,或者一个横排或者竖排同时渲染多个数据。 例如: render1(){ var arr = []; for(var i=0;i;i++){ arr.push( ... return

    参考:http://blog.csdn.net/pz789as/article/details/52537028


    我们在项目里面,经常会用的批次渲染,比如一个列表渲染很多个item,或者一个横排或者竖排同时渲染多个数据。

    例如:

      render1(){
        var arr = [];
        for(var i=0;i<5;i++){
          arr.push(
            <Text style={{fontSize:20, color: 'red'}}>
              这是{i}
            </Text>
          );
        }
        return (
          <View style={[styles.container, styles.center]}>
            {<span style="font-family: Arial, Helvetica, sans-serif;">arr</span>}
          </View>
        );
      }

    这样写,RN都会报一个警告,需要你对每个item添加一个key,在Text里添加一个属性key:

    <Text key={i} style={{fontSize:20, color: 'red'}}>

    但是这个key有什么作用呢?我们在代码后面加一个console.log输出一下看看结果:

        for(var i=0;i<5;i++){
          ...
        }
        console.log(arr);
    输出结果显示,arr所有的内容,包括那个key:


    看到key和那个props了吗,我们可在未渲染之前,根据要求再次更改array里面<Text>的属性。我们现在来改一改试试看!

    先看看上面代码运行的效果:


    然后我们在for之后这么改看看结果如何:

        for(var i=0;i<arr.length;i++){
          if (arr[i].key == 2){
            arr[i].props.style.fontSize = 40;
            arr[i].props.style.color = 'green';
            arr[i].props.children[0] = '改变了哦';
          }
        }


    结果我们可以看到,中间那个key等于2的已经改变了哦。

    既然这样,那我们继续改一下,把这个arr改为这个组件的一个属性:

      constructor(props){
        super(props);
        this.state = {
          blnUpdate: false,
        };
        this.testArr = [];
        for(var i=0;i<5;i++){
          this.testArr.push(
            <Text key={i} style={{fontSize:20, color: 'red'}} onPress={this.changeChild.bind(this, i)}>
              这是{i}
            </Text>
          );
        }
        console.log(this.testArr);
      }
    定义在constructor里面,并且附初始值,我们还绑定了一个按键响应, 可以看到临时变化。

    另外加一个state变量,用于刷新render(为什么这么做,是RN的刷新机制,需要调用this.setState才会调用,所以弄了一个bln值,而不是把arr放在state里面

    然后在绑定还按钮回调中这样做:

      changeChild(key){
        console.log(key);
        if (this.testArr[key].props.children[0] == '我变了'){
          this.testArr[key].props.style = {fontSize : 20, color : 'red'};
          this.testArr[key].props.children[0] = '这是';
        }else{
          this.testArr[key].props.style = {fontSize : 30, color : 'green'};
          this.testArr[key].props.children[0] = '我变了';
          //这里要说说text的结构,如果text是纯文字,children就只有一个,如果中间夹杂着其他变量,react是将text分段保存的。
        }
        this.setUpdate();
      }
      setUpdate(){
        this.setState({
          blnUpdate: !this.state.blnUpdate
        });
      }
    这样我们来看效果:

    点击之前:

    点击之后:

    哈哈,都变了哦!!不过这样做有点延时。其实改变渲染之后的东西,在学习RN之后会有一个专门的参数可供使用,就是每个组件的自带属性ref,在上面的截图我们也看到了,现在ref是null,因为没有设置,如果要使用需要这样做:

    <Text key={i} ref={'text'+i} style={{fontSize:20, color: 'red'}} onPress={this.changeChild.bind(this, i)}>
    然后使用的时候就是this.refs.text0.xxx,这个RN给我们提供一个很有用的函数,this.refs.text0.setNativeProps({style:{xxx},xxx:xxx}),这个是直接改变原生组件的属性,是不会经过render的,有时候可以提高性能。

    不过这个ref有个缺点,它必须要在render之后才会产生,也就是一开始初始化后,使用this.refs.text0 会报错,这点一定要弄清楚哦!


    以上这些就是今天发现key的一个用处!






    展开全文
  • console.ignoredYellowBox = ['Warning: BackAndroid is ... Please use BackHandler instead.','source.uri should not be an empty string','Invalid props.style key']; console.disableYellowBox = tr...
    console.ignoredYellowBox = ['Warning: BackAndroid is deprecated. Please use BackHandler instead.','source.uri should not be an empty string','Invalid props.style key'];
    
    console.disableYellowBox = true // 关闭全部黄色警告
    
    

    将这两句话加在index.js文件中,放在AppRegistry.registerComponent('App', () => App)之前。

    展开全文
  • React Native 充分利用了 Facebook 的现有轮子,是一个很优秀的集成作品,使用 RN 即可做到无需编译就能远程热更新 App,再加上友好的开发方式、快到爆炸的开发效率、RN 已经完爆了其他的 App 开发方式,即使是一个...

    课程介绍

    React Native 充分利用了 Facebook 的现有轮子,是一个很优秀的集成作品,使用 RN 即可做到无需编译就能远程热更新 App,再加上友好的开发方式、快到爆炸的开发效率、RN 已经完爆了其他的 App 开发方式,即使是一个初入前端的开发者也能迅速开发一套 iOS、Android 双平台的 App。

    文章中将分三个部分讲解 RN 的开发。通过开发一个比较完整的电商 App 来逐步带领读者走入 React Native 的世界。读者可以从中汲取到完整的项目经验,从菜鸟到精通也只需要学完这个达人课即可。

    第一部分主要讲述封装基本组件、简单页面的开发:这个阶段主要解决开发中遇到的兼容问题,在文中会提出解决方案、避免无用的代码。

    第二个部分将讲述复杂的业务逻辑:让第一次做开发的读者也能非常快速的适应业务形态,让开发有底气、不再受制于产品的约束。

    第三部分将讲述性能的极致优化、热更新、统计等这些 App 必须的东西,让开发的 App 能够真正的比得上原生 App 和混合 App。

    作者介绍

    郭方超,技术总监、架构师、产品、运营。现担任泽旭商贸技术总监、达令前端架构师职务,有过多年的前端、后端开发经验,擅长 Node.js、.Net、Java 等开发技术。

    开发(参与)过的项目如下: 泽旭商贸 PC、移动、微信、App 项目,达令家 App 的开发,心燃灵动前端库、微信端电商框架的开发,来吧旅行前端项目。开源了以下项目:React-Native 的多个组件、React-Native 的监控系统、Node 爬虫框架、模块化前端脚手架、微信小游戏引擎。

    课程内容

    导读:为什么选择 React Native

    在所有的技术选型之前都有一个为什么。

    为什么我选择了 React-Native?选择这个技术到底给我带来了什么样的技术福利?如果你正在考虑 React-Native,或者还在研究选择什么样的技术实现自家的 App,不妨看看这个课程,说不定你就有了不一样的感受。

    注:React-Native 以下简称 RN。

    优点

    选择 RN 之前我也看过了其他的几种技术,之前也使用过其他的技术做 App,比如 HBuilder 的框架,非常的方便、非常的好用、什么都不需要管,但是开发完之后发现性能很差,做一个稍微复杂点的项目就非常上火,后来切了原生,但是使用 Java 做开发也有非常多的问题,开发效率很低。

    后来我选择了 RN,它使用 JS 更新虚拟 DOM,通过一个桥接器将需要更新的结果通知到 UI 层,让 Native 执行 UI 的改变。简单来说,就是用 JS 做驱动开发一个类原生的 App,所有的渲染都是和原生一样的,一下子就将原生开发和 JS 开发连接起来。通过这么一个模式,将传统的 Java 者 OC 开发转变成了简单易懂的前端 JS 开发,这是移动开发的一大进步,避免了一个 App 多个平台多套代码的尴尬,同时提升了开发效率,将移动开发带入了一个新的层面。

    enter image description here

    性能相似

    很多人说 RN 的性能比不上原生的 App,这个说法是要看具体的场景。

    在一般的应用场景下 RN 的表现和原生 App 是没有太大的差别的,一个 App 也不会到处都是复杂的交互效果。一些简单的点缀动画再加上列表图片等才是一个 App 最常见的内容,这种情况下它们之间的表现是一样的。

    RN 本身只是使用 JS 处理了 UI 渲染之前的一些逻辑,在最终的渲染上其实使用的还是原生的逻辑,尤其是渲染完成之后更是和原生的没有半点区别。

    我们的案例是一个电商项目,主要渲染逻辑是首页的自定义模板、无限加载的列表等。目前最大的性能瓶颈其实是在事件的优化上,优化之后用户已经感知不到和原生的区别了,我们会在后面的部分提到性能优化,将一个粗糙的 App 通过简单的方法提高 10 倍性能,再通过另外一个稍微复杂的方法减低内存占用。

    开发效率高

    通常情况开发一款 App 需要发布在 Android 和 iOS 两个平台,导致的结果就是一个 App 有两个团队、两套代码,界面几乎一样,为什么不能使用一套代码呢?之前也有大神使用各种手段达成这个目标,但是并不是很理想。

    由于使用熟悉的 React 和 JSX 的模式,开发者只需要有前端知识就可以很迅速的上手一个 RN 项目,如果再学一些实战的例子,稍微复杂一些的项目也难不倒各位前端开发者。

    Debug 超级方便,一边开发一边看效果再也不是梦。

    快速热更新

    RN 生成的 JS 文件,只要不涉及原生功能的增减,已经发布的 App 完全不需要重新安装即可完成新版功能的上线,用户只需打开 App 就能体验到最新的 App,省去了下载重装的各种麻烦,把 App 的更新做到了和网页更新一样的方便快捷。

    使用 RN 就能达到既有原生的所有能力,又有类似浏览器上的快速更新能力,同时还可以接入各种定制好的网页,将 App 的自由度提高到一个非常高的地步。

    大公司背书

    RN 的开发者是 Facebook,背靠大树好乘凉,社区更疯狂,Facebook 本身也在尝试使用 RN 技术开发自己的 App,RN 一定会越来越完善,截止写这篇文章的时候 RN 已经更新到了 0.53.0。

    RN 本身也是开源的,所有的源代码都是可以看到的,社区的讨论也是比较热烈的,现在可能中文文档还比较少,未来随着开发者的努力,这些坑都会填起来的。

    其实最开始的时候也没有想很多,仅仅是冲着 RN 可以快速开发,上线快体验好,经过了这么长时间的开发,我更加喜欢 RN 的这种开发方式,项目中也填了各种各样的坑,后面就用一个实际的例子来展示 RN 是怎样开发的吧。

    RN 的缺点

    升级快

    RN 本身其实还处于测试版,开发组经常会升级 RN,解决一些遗留或者隐藏的 bug,在这个过程中就导致了 RN 本身升级非常快,开发者在使用 RN 开发 App 的过程中应该尽量提高自己的版本,不需要一直是最新的,只要能够跟的上 Facebook 的节奏即可。

    自己搞定的问题也是可以合并入 RN 的源代码里的,不要一味的等 RN 更新,有些问题自己解决更方便,建议会 Android 或者 iOS 同学自己动手。

    动画难

    这里的难指的是复杂的动画在开发中很难去优化,尤其是开发者懂前端但是不懂原生的情况下,好在常见的 App 也不需要多么复杂的动画,一般使用位移变换就足够了,太复杂的动画建议使用 RN 的 SVG 组件来做。

    WebView 难用

    RN 自带的 WebView 跟浏览器有一定的差别,App 经常要打开一些网页,可能在开发的时候一切正常,但是到了 RN 里面就会有一些奇怪的问题,主要还是受到系统浏览器的影响,会有一些兼容方面的问题,这种情况下不如微信使用自己研发的浏览器,可以畅快的使用 ES 6 之类的新技术。

    需要原生支持

    简单的东西和界面的展现已经完全放手给了开发者,但是还是有一些功能只能原生去实现,如果原生部分的开发者对 RN 不太了解可能会给 App 带来不可预知的 bug,好在大多数开源看只需要执行 Link 命令就可以把原生部分也安装好。

    技术都会有优点和缺点,选择合适的技术才能给项目带来长久的生命力。

    其他技术

    Weex

    核心思想上,这两家其实并没有什么区别,Weex 也可以算是站在 RN 的肩膀上起步的,目前活跃度不高,大多数是在观望中。

    开发框架

    Weex 使用 Vue,熟悉 Vue 的开发者可能会更熟悉。

    RN 使用 React,都是 Facebook 出品,框架融合上会更方便一些,它们都是组件化开发,都属数据绑定,都有虚拟 DOM,社区同样活跃,使用人数也都非常多。

    学习成本

    React 的 JSX 初期会比较难上手,CSS 的写法也跟前端的样式写法不一样,Weex 使用模板的形式,直接 html+css 开发,上手会稍微简单一些。

    异步

    Weex 只支持 callback 的形式,RN 支持 promise 的形式,这些都是可以解决的,不是什么问题。

    社区

    RN 开源早,有 Facebook 支持,社区的组件库已经比较丰富,社区活跃度比较高。Weex 开源晚,社区活跃不高,以阿里系比较多。

    Flutter

    Flutter 是 Google 推出的一个跨平台(Android 和 iOS)移动开发框架,使用的是 Dart 语言。

    Flutter 的目标是用来创建高性能、高稳定性、高帧率、低延迟的 Android 和 iOS 应用,并且开发出来的应用在不同的平台用起来跟原生应用具有一样的体验。不同的平台的原生体验应该得到保留,让该应用看起来同整个系统更加协调;不同平台的滚动操作、字体、图标 等特殊的特性应该和该平台上的其他应用保持一致,让用户感觉就像操作原生应用一样。比如,返回图标 Android 和 iOS 是不一样的;滚动内容滚动到底的反馈也是不一样的。

    兼容

    Flutter 不使用系统提供的组件,自己实现了一套渲染机制,所以在性能优化、跨平台方面表现优秀。实际体验上,性能比 RN 要高不少。

    enter image description here

    RN 最终调用的还是系统的组件,虽然 Facebook 已经很努力了,但是在某些时候还是会有兼容性需要处理。

    组件

    Flutter 内置了对 Material Design 的支持,给开发者提供了丰富的 UI 控件库选择,同时所有的组件都有扩展,保持了很高的灵活性。

    RN 通过 React 也做到了组件式开发,跟 Flutter 相比,多了一个桥接器的转换,性能上肯定不如 Flutter。

    开发语言

    Flutter 使用 Dart 实现,Dart 号称要完全取代 JS,不过目前离这个目标还非常远,初期上手还是有一些难度的。

    RN 使用 JS 开发,做过前端的都非常熟悉,上手很容易。

    enter image description here

    Flutter 现在还在实验阶段,不排除 Google 使用别的框架替换它的可能性,Dart 语言也处于成长阶段,只有 Google 的浏览器在支持,或许在 Flutter 持续发展到一个阶段之后,才会有很多支持者。

    在写文章的时候 Google 放出了第一个测试版,感兴趣的可以下载下来玩玩儿。

    相比于其他几种技术,RN 是目前社区最活跃,开发效率最高的一种选择,选择 RN 也是需要在一个比较短的时间内能够完成 App 的开发。尤其现在前端开发者可以非常容易的从网页开发转到 App 开发上。对于我们包含 App、微信、小程序这样的三个平台更是需要 RN 这样的技术,一个团队就可以维护项目的持续增长。

    如果需要 RN 来开发自己的项目,那就继续往下看吧,我们将从简单的界面开发、数据更新等开始逐步深入,后面涉及到性能优化、自定义原生部分等。

    第01课:项目初始化

    知识点:

    • 快速创建 RN 项目
    • 添加使用路由功能
    • 不要在 RN 中使用的功能

    从这里开始,我们将一步一步的创建一个可以真正使用的 App:第一部分讲述开发一个 App 的大致过程,第二部分将开始优化性能、开发效率等,第三部分介绍添加热更新、支付、分享这些功能。大多数第三方组件可以很方便的 link 到项目里,部分需要手动导入甚至主动开发一些东西,这里也会在用到的时候讲出来。

    创建项目的前提条件

    使用 React-Native 创建一个新的项目请确保电脑上已经满足下面的这些条件。

    • nodejs:RN 的所有库都是从 NPM 上安装的,请确保电脑上已经安装了 NPM,可以使用npm -v来查看当前是否已安装,当前项目使用的 Node 版本是 8.9.3,NPM 的版本是 5.5.3。
    • react-native-cli:这个通常用作 RN 的初始化和启动模拟器等,使用 NPM 可以安装到电脑上。
    • Python:RN 里面有些脚本是使用 Python 写的,请确保电脑上已经安装了 Python 2.7 以上的版本。
    • JDK 1.8:安卓项目需要使用 JDK 1.8,请在电脑上安装好,安装的教程可以搜索其他资料。
    • Android Studio:调试以及编译安卓代码需要使用到,请在安装 Android Studio 之后安装好 Android SDK 以及模拟器,模拟器可以使用市面上的安卓模拟器,它们普遍比自带的模拟器要快,闲麻烦的读者可以直接连接手机调试。在安装好 IED 之后请下载好需要使用到的 Android SDK,下载 SDK 需要科学上网或者把下载地址替换成国内的几个下载源。
    • Git:后面的项目会加入到 Git 中,使用 Git 做版本管理的好处不言而喻。
    • Xcode(仅 iOS 项目中):安装了 Xcode 才能使用 iOS 模拟器,iOS 模拟器下的开发速度要明显优于安卓下,后面的开发过程大多数也是在 iOS 模拟器中进行的。在模拟器中的表现都差不多,有些不一样的效果可以在看到的时候单独调试。
    • Watchman(仅 Mac 系统用到):Watchman 用来监听文件变动等,Mac 下必须安装 Watchman。
    • VSCode:文中演示项目使用的编辑器是 VSCode,这里也推荐使用 VSCode 开发前端项目。

    创建项目

    我们先在本地创建一个可运行的项目,同时这个项目会加入到 Git 的版本管理中。

    (1)执行react-native init anxintao --version 0.53.0

    enter image description here

    • 图中1代表之前安装的 react-native-cli 的命令。
    • 图中2代表初始化命令。
    • 图中3代表项目的名称,这里是anxintao
    • 图中4代表指定 RN 的版本号,这个参数不传默认使用最新版。
    • 图中5代表 RN 具体使用的版本号。

    (2)使用 VSCode 打开项目,在项目根目录下执行命令启动初始化之后的项目,Mac 下推荐react-native run-ios,Window 下推荐react-native run-android启动默认的项目。如果能启动说明项目初始化完成,否则说明项目的某些东西没有安装好。

    enter image description here

    这里推荐把启动的命令写入到 package.json 文件中,比如,输入npm run ios即可代替原来的react-native run-ios,输入命令的速度快了不少,也可以给 VSCode 安装一个启动 RN 的插件,不过效果跟输命令差不多,具体要看个人习惯了。

    (3)到这里就说明项目创建成功了,这个项目现在还很简单,它的原生部分只有一个简单的空壳,这个空壳仅仅是初始化了一个 RN 的 Activity,所有的 JS 都是运行在根视图上的。

    (4)这里注意一下,新建的项目提示了按键可以刷新页面或者调出菜单,这显示的是 iOS 模拟器,按键为command R刷新和command D调出菜单。

    添加路由

    一个完整的项目不能没有路由,这里使用 React Navigation

    在写文章的时候已经有一个路由组件react-native-navigation热度超过了 react-navigation,它更多的使用的是原生的路由切换,效果更好,想用的读者可以去尝试一下。

    • 安装路由,在根目录下执行npm install --save react-navigation

    • 在根目录下新建src目录,所有页面放入这个文件夹下。

    • 新建一个首页,给后面的路由调用,页面路径为根目录/src/home/index.js

      'use strict';import React from 'react';import {    StyleSheet,    View,    Text} from 'react-native';export default class extends React.Component {    render() {        return <View style={{ marginTop: 200 }}>            <Text>这是首页</Text>        </View>    }}

    修改根目录下的index.js,添加整个项目的路由。

    import { AppRegistry } from 'react-native';import Pages from './src';//启动AppRegistry.registerComponent('anxintao', () => Pages);

    在 src 目录下新建index.js文件,在这个文件里添加路由,这里从简单的一个页面开始。

    'use strict';import React from 'react';import {    StyleSheet} from 'react-native';//添加路由组件import Navigation from 'react-navigation';//添加展示用的首页import Home from './home/index'//创建路由const Pages = Navigation.StackNavigator({    'Home': {        screen: Home    }}, {    //这里做了一个页面跳转的动画    transitionConfig: () => ({        screenInterpolator: sceneProps => {            const { layout, position, scene } = sceneProps;            const { index } = scene;            //设置页面跳转的动画            const translateX = position.interpolate({                inputRange: [index - 1, index, index + 1],                outputRange: [layout.initWidth, 0, 0]            });            const opacity = position.interpolate({                inputRange: [index - 1, index - 0.99, index, index + 0.99, index + 1],                outputRange: [0, 1, 1, 0.3, 0]            });            return { opacity, transform: [{ translateX }] };        }    }),    navigationOptions: {        header: null    }});//创建一个自己的容器,方便以后对路由做一些处理export default class extends React.Component{    constructor(props) {        super(props);    }    render() {        return <Pages onNavigationStateChange={this.listenChange.bind(this)}></Pages>;    }    //监听路由的跳转    listenChange(state1, state2, action) {    }}

    添加完成之后删除掉初始化项目之后的 App.js,这个时候在模拟器中使用快捷键command+R即可刷新刷新页面。

    479e89e0-1876-11e8-8087-9b6b3b447adf

    至此就完成了简单的路由设置,之后只需要添加页面并在路由中注册即可使用。

    路由升级版

    简单的路由并不能起到很好的作用,我们还是创建一个更实用的路由吧,比如带 3 个 tab 切换的首页,这也是大多数 App 使用套路。

    添加 4 个 tab 切换页,我们假定未来需要 4 个切换页,分别是首页、分类页、购物车、个人中心,在 home 下分别创建他们。

    084841b0-187a-11e8-8087-9b6b3b447adf

    修改路由所在的 index 文件,引入下面要用到的几个组件和页面。

    添加新加入的页面:

    import React from 'react';import {    StyleSheet,    Image} from 'react-native';//添加路由组件import Navigation from 'react-navigation';//添加展示用的首页import Home from './home/index'import Products from './home/products'import Shop_Cart from './home/shop_cart'import My from './home/my'

    创建底部的样式:

    //创建tab页的顶部样式const styles = StyleSheet.create({    tab: {        height: 40,        backgroundColor: '#fbfafc',        borderTopColor: '#efefef'    },    tabIcon: {        width: 20,        height: 20    },    tabLabel: {        marginBottom: 4    }})

    创建一个 tab 路由,为了简单这里只展示 2 个页面的,具体的代码可以去 Git 仓库查看。

    //创建首页的tab页const Tabs = Navigation.TabNavigator({    'Home': {        screen: Home,        navigationOptions: ({ navigation, screenProps }) => {            return {                tabBarLabel: '首页',                tabBarIcon: (opt) => {                    if (opt.focused) return <Image source={{ uri: require('./images/tab-home-active') }} style={styles.tabIcon}></Image>;                    return <Image source={{ uri: require('./images/tab-home') }} style={styles.tabIcon}></Image>;                }            }        }    },    'Products': {        screen: Products,        navigationOptions: ({ navigation, screenProps }) => {            return {                tabBarLabel: '产品分类',                tabBarIcon: (opt) => {                    if (opt.focused) return <Image source={{ uri: require('./images/tab-products-active') }} style={styles.tabIcon}></Image>;                    return <Image source={{ uri: require('./images/tab-products') }} style={styles.tabIcon}></Image>;                }            }        }    },}, {    //设置tab使用的组件    tabBarComponent: Navigation.TabBarBottom,    //点击哪个才加载哪个tab里的页面    lazy: true,    //设置tab放在界面的底部    tabBarPosition: 'bottom',    //设置tab里面的样式    tabBarOptions: {        style: styles.tab,        labelStyle: styles.tabLabel,        activeTintColor: '#d0648f'    }});

    替换 Pages 里的第一个页面为刚才创建的 tab 路由。由于默认加载第一个,所以需要将第一个设置成 tab 页。

    'Tabs': {        screen: Tabs }

    现在再刷新模拟器,就会发现底部的 tab 切换已经好了,点击可以切换不同的页面。

    da200e80-187e-11e8-8087-9b6b3b447adf

    这里将图片转化成 base 64 的方式再引入到图片组件中,好处是打包之后会变成一个整体,坏处是打包之后的 bundle 文件会变大,做增量更新也比较麻烦。

    不推荐使用的东西

    • 投影:安卓不支持投影,在开发的时候如果没有必要就使用别的方式代替吧,比如使用图片代替投影。
    • 边框色:在长列表中尽量不要使用边框色,在某些安卓手机下会闪退。
    • 使用了圆角的情况再使用背景色:iOS 手机会出现边框颜色异常或者异常色块,去掉背景即可。
    • 过于深层次的结构。
    • 过于频繁的刷新 state。
    第02课:封装自己的库

    知识点:

    • 封装一个自己的日志,代替原生的 console
    • 封装一个自己的请求库,代替原生的 fetch
    • 自适应方法,兼容各种手机屏幕
    • 封装一个本地存储
    • 集成 Toast 弹出提示
    • 让 iOS 支持 HTTP 地址
    • 安卓需要证书才能编译

    在正式开始之前,我们先封装几个要用到的库。

    自定义本地日志

    自定义日志的一个好处就是省的每次都要手动注释 console,而且还可以同时将日志存在本地,或者发到日志服务器,一个方法一举多得了。

    在 src 目录下新建一个 utils 文件夹,封装一个日志输出类,开发模式下使用console.log命令,正式情况下记录在变量中,方便在手机上查看日志。

    新建一个 log.js 文件,路径为根目录/src/utils/log.js

    80223590-1886-11e8-8087-9b6b3b447adf

    新建一个数组变量logs,用来临时存放日志信息。

    98604390-1886-11e8-8087-9b6b3b447adf

    将日志分成信息、警告和错误 3 种,分别给出 3 个可调用的方法,同时给第一个参数加一个好看的颜色。

    cc840ad0-1886-11e8-8087-9b6b3b447adf

    在 index 中引入日志组件,写几个方法来看看调用的结果。

    enter image description here

    这里稍微定义一下日志的要求,参数 0,字符串;参数 1,对象;参数 2,字符串。

    enter image description here

    自定义请求

    RN 默认提供了 fetch 方法去请求远程数据,我们再封装一次,这个方法将会针对现有的项目做封装,在使用请求的时候能够更适合、更方便。这里使用 header 保存了一些临时的变量,算是一个小小的全局缓存吧。

    创建 request.js 文件,目录:根目录/src/utils/request.js

    80223590-1886-11e8-8087-9b6b3b447adf

    将请求 header 里的信息单独出来,每次请求都需要带上这个共享 header 数据。

    29975590-1889-11e8-8087-9b6b3b447adf

    创建一个 Request 类,并将这个类对外公开,这里将请求初始化一次,以后用到别的请求的时候也可以单独实例化一次。

    /** * 请求库 */class Request {}export default new Request();

    每次请求都将 header 中的内容带入请求中,单独检测 httpcode 和后端返回的 code 值,这里可以直接做权限检测,在需要的时候跳转到登录页。

    /** * 请求库 */class Request {    /**    * 检测返回状态码    * @param {*} status     * @param {*} res     */    async _checkStatus(status, res, url) {        if (status !== 200) {            logWarm('请求失败参数', await res.text(), url, headers);            throw new Error('网络连接失败,请检查网络');        }    }    /**    * 检查后端返回的状态码    * @param {*} status     */    _checkAppStatus(json, url) {        if (json.status != 0) {            logWarm('返回状态报错', json, url);            throw new Error(`${json.errorMsg}`);        }    }    /**     * 内部实现网络请求     * @param {*} url      * @param {*} options      */    async _request(url, options, type) {        url = url.indexOf('http') == 0 ? url : url.indexOf('/api') == 0 ? domain + url : baseUrl + url;        let res = await fetch(url, options);        this._checkStatus(res.status, res, url)        if (type === 'json') return await this._jsonFactory(res, url, options)        return await this._jsonFactory(res, url, options)    }    /**         * 处理json数据         * @param {*} res          * @param {*} url          */    async _jsonFactory(res, url, options) {        let json;        let txt = '';        try {            txt = await res.text();        } catch (e) {            log('未拿到返回字符串', { url: url, txt: txt });            throw new Error('数据格式错误');        }        try {            json = JSON.parse(txt);        } catch (e) {            logErr('返回数据格式错误', { url: url, txt: txt });            throw new Error('数据格式错误');        }        this._checkAppStatus(json, url)        log("请求返回", json, url, options);        return json.data;    }    /**     * get请求     * @param {*} url      */    async get(url, data) {        if (data) data = urlEncoded(data);        if (url.indexOf('?') < 0 && data) url += '?' + data;        return this._request(url, {            method: 'GET',            headers: headers,            timeout: 10000        }, 'json')    }    /**     * post请求     * @param {*} url      * @param {*} data      */    async post(url, data) {        return this._request(url, {            method: 'POST',            headers: Object.assign(headers, { 'Content-Type': 'application/x-www-form-urlencoded' }),            timeout: 10000,            body: urlEncoded(data)        }, 'json')    }}

    调用一次远程端口并查看日志输出,这里调用的也是案例中要使用到的获取 banner 的接口,该接口不需要用户权限,后面还会遇到需要用户权限的接口。

    enter image description here

    自适应方法

    这里推荐一种自适应的方法,同时也是前端在开发移动端页面的时候常用的方法,将手机屏幕宽度默认为 750 像素,然后将所有的宽高按照这个比例去缩放,这要求设计出的设计稿宽度也是 750 像素。

    在 utils 下新建一个px.js文件,按照出入的大小根据当前屏幕的宽度获取到缩放的比例并返回结果。

    ab16ed00-188a-11e8-8087-9b6b3b447adf

    在首页引入 px 方法,查看使用 px 之后的效果。

    d4992580-188a-11e8-8087-9b6b3b447adf

    d97950c0-188a-11e8-8087-9b6b3b447adf

    可以看到使用 px 将 500 像素缩放之后的效果和最开始设置的纯数字 200 效果是一致的,这里使用的是 iOS 模拟器,真实的屏幕宽是 375,按照 750 宽去算的话会把传入的参数统统除以 2。

    封装本地存储

    RN 提供的 AsyncStorage 可以根据 key 存储相应的字符串,我们这里改进一下,让它可以存储所有类型的字段,利用的是将传入的参数改造成对象,然后使用 JSON 的方法将对象转化成一个可以存储的字符串。

    在 utils 下新建一个Storage.js

    3ddb9950-188c-11e8-8087-9b6b3b447adf

    将传入的对象转化为字符串并存入 AsyncStorage。

    'use strict';import { AsyncStorage } from 'react-native';/** * 获取存储的数据 * @param {*} key  */exports.getItem = async (key) => {    let item = await AsyncStorage.getItem(key);    if (!item) {        return null;    }    return JSON.parse(item).v || null;}/** * 存入数据 * @param {*} key  * @param {*} value  */exports.setItem = (key, value) => AsyncStorage.setItem(key, JSON.stringify({    v: value}));/** * 删除已经存在的数据 * @param {*} key  */exports.removeItem = (key) => AsyncStorage.removeItem(key);/** * 清除所有 */exports.clear = () => AsyncStorage.clear();/** * 获取所有的key */exports.getAllKeys = () => AsyncStorage.getAllKeys();

    在首页使用 setItem 存入数据,然后第二次进入页面再使用 getItem 获取数据。

    e9e272a0-188c-11e8-8087-9b6b3b447adf

    这里用到了 componentDidMount 这个方法,该方法是在组件生命周期中的初始化完成之后执行的。

    enter image description here

    添加弹出 Toast

    之前公司使用的是自己开发的提示方法,该方法需要改变原生代码,非常的不方便,这里推荐使用第三方的开源组件react-native-root-toast,只需要安装一下就好了。

    执行命令,安装 Toast:npm i --save react-native-root-toast

    在 utils 目录下新建 toast.js 文件,添加 Toast 的默认方法并填入默认参数,这里设置显示时间为 1000 毫秒,背景颜色是一个半透明的黑色,其目的也是为了方便调用,如果需要多种效果的就定义多个即可。

    enter image description here

    在首页引入 Toast 并看看实际的效果。

    41c80400-18b3-11e8-8087-9b6b3b447adf

    让 iOS 支持 Http 协议

    苹果之前推荐使用 Https 协议,现在默认是不支持 Http 的,如果需要支持 Http 则要单独设置,案例中的项目也用到了 Http,所以需要修改 info.plist 文件,让 iOS 可以访问 Http 的地址。

    使用 Xcode 选择打开其他项目。

    0da257a0-1919-11e8-8087-9b6b3b447adf

    打开项目下的 iOS 文件夹,选择项目文件并打开。

    4d6ce8a0-1919-11e8-8087-9b6b3b447adf

    选择 info.plist 文件,在右边选择第一行并点击 + 号添加一项。

    enter image description here

    选择 App Transport Security Setting 这一项,会弹出提示,单击“确定”按钮即可自动刷新。

    01e614a0-191a-11e8-8087-9b6b3b447adf

    在上面添加的新配置中添加一个新的配置 Allow Arbitrary Loads,同时设置为 YES。

    enter image description here

    改完配置还需要编译一次,单击左上角的三角形或者菜单中 product 下的 build 选项。

    enter image description here

    这里我使用的是 Xcode 修改配置文件,如果你发现配置文件没有变化,也可以自己改 info.plist 文件的内容。

    编译安卓客户端

    使用 Android Studio 打开根项目下的 Android 目录,打开 build.gradle 文件,这个就是项目的 Gradle 配置文件,通常使用这个文件对整个项目进行描述。

    3d6d4f10-204b-11e8-94c8-43a1e6ab60e0

    经过一定时间的等待,IDE 就会初始化整个项目,如果有一些需要下载的文件也会在这个时间通知下载。

    单击菜单build/gennerate signed apk,这个就是编译一个可以安装在安卓手机上的安装包,也可以通过单击make project来看看项目是否可以编译通过。

    enter image description here

    单击 Next 按钮,IDE 会提示需要选择一个证书,这里可以选择一个已有的并输入密码,也可以通过单击create new来创建一个,后面一直单击 Next 按钮就可以了,IDE 会在生成 APK 之后弹出通知。

    021b3160-204c-11e8-8063-17489f4fcf26

    选择创建一个新的证书,根据提示填入相应的内容,之后单击 OK 按钮即可生成,记得选择 remember password,下次直接填入密码。

    8d33ddb0-204c-11e8-a1dd-77d9e3f43337

    经过一整机器躁动之后,IDE 弹出编译结果,点击蓝色字可以快速打开 APK 的地址。

    enter image description here

    将 APK 文件发送到手机上安装即可,一个自己开发的 App 就安装好了。

    14e47fd0-204d-11e8-94c8-43a1e6ab60e0

    这里要注意,在打包之前要把 RN 打包生成的 bundle 文件放入 Android 文件下的 assets 目录中,否则 Android 会因为找不到启动文件而报错。

    第03课:首页模板和商品列表
    第04课:组件和商品详情
    第05课:使用 WebView 和运营页通讯
    第06课:个人中心设置及 Debug 日志
    第07课:添加自定义组件
    第08课:注册登录
    第09课:全局状态管理购物车
    第10课:实现购买(1)
    第11课:实现购买(2)
    第12课:订单管理
    第13课:添加优化动画
    第14课:优化 App,提升10倍效率
    第15课:使用 ESlint 规范化代码
    第16课:使用 TypeScript 编写代码
    第17课:集成极光
    第18课:热更新
    第19课:总结

    阅读全文: http://gitbook.cn/gitchat/column/5aa8a68b0bb9e857450e2308

    展开全文
  • 前言: 完成项目时,我们需要将项目打包成一个apk,方便测试以及发布...离线包就是把 ReactNative 和你写的 js文件、图片等资源都打包放入 App ,不需要走网络下载。 首先看一下官方给的参数(中文版): react-native...

     

    前言:

    完成项目时,我们需要将项目打包成一个apk,方便测试以及发布版本.

    这时,需要把js代码和图片资源都放进apk中, 并且发布版本还需要签名,今天把这一系列操作记录下来.

     

    一.生成离线bundle包

    离线包就是把 ReactNative 和你写的 js文件、图片等资源都打包放入 App ,不需要走网络下载。

    首先看一下官方给的参数(中文版):

    react-native bundle [参数]
      构建 js 离线包 


    参数:


        -h, --help                   输出如何使用的信息
        --entry-file <path>          RN入口文件的路径, 绝对路径或相对路径
        --platform [string]          ios 或 andorid
        --transformer [string]       Specify a custom transformer to be used
        --dev [boolean]              如果为false, 警告会不显示并且打出的包的大小会变小
        --prepack                    当通过时, 打包输出将使用Prepack格式化
        --bridge-config [string]     使用Prepack的一个json格式的文件__fbBatchedBridgeConfig 例如: ./bridgeconfig.json
        --bundle-output <string>     打包后的文件输出目录, 例: /tmp/groups.bundle
        --bundle-encoding [string]   打离线包的格式 可参考链接https://nodejs.org/api/buffer.html#buffer_buffer.
        --sourcemap-output [string]  生成Source Map,但0.14之后不再自动生成source map,需要手动指定这个参数。例: /tmp/groups.map
        --assets-dest [string]       打包时图片资源的存储路径
        --verbose                    显示打包过程
        --reset-cache                移除缓存文件
        --config [string]            命令行的配置文件路径

    接下来,就开始我们的离线包打包命令:

     

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

    打安卓包的话,react-native bundle 可以替换为 react-native unbundle 做到拆分功能 

    react-native unbundle --entry-file index.js --platform android --dev false --bundle-output ./android/app/src/main/assets/index.android.bundle --assets-dest ./android/app/src/main/res/
    

     

     

     

     

    翻译成我们能理解的意思就是:入口文件是index.js(0.49以前是index.android.js 注意改一下代码) ,平台是安卓,不显示警告,bundle包输出路径(保存在)react-./android/app/src/main/assets/index.android.bundle  ,图片资源路径是: ./android/app/src/main/res/ .

    很容易理解,但是要确保有assets这个文件夹,如果没有请先新建这个文件夹.成功之后会生成index.android.bundle文件.

     

    二 生成签名文件

    1.命令行方式:

     

    你可以用keytool命令生成一个私有密钥。在Windows上keytool命令放在JDK的bin目录中(比如C:\Program Files\Java\jdkx.x.x_x\bin),你可能需要在命令行中先进入那个目录才能执行此命令。

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

    这条命令会要求你输入密钥库(keystore)和对应密钥的密码,然后设置一些发行相关的信息。最后它会生成一个叫做my-release-key.keystore的密钥库文件。

    在运行上面这条语句之后,密钥库里应该已经生成了一个单独的密钥,有效期为10000天。--alias参数后面的别名是你将来为应用签名时所需要用到的,所以记得记录这个别名。

    注意:请记得妥善地保管好你的密钥库文件,不要上传到版本库或者其它的地方。

    生成的密钥文件在android目录下,记得要将它保存到android/app目录下. 如图所示:

     

     

    2. 用android studio 生成签名文件:

    点击android stuido 菜单栏中的 build, 找到“Generate Signed APK”.

    然后

    ”Create new…”新建一个签名文件

    Choose existing…”选择一个已经存在的签名文件

    如果已经有签名文件,可以直接选择使用,没有的话就新建一个.

    点击新建之后会有一个弹窗, 需要写很多信息:

    Key store path : 签名文件路径
    Password : 签名密码
    Confirm : 确认密码
    Alias : 别名
    Validity ( years ) : 有限期 (年)
    First and Last Name : 全名
    Organizational Unit : 组织单位
    Organization : 组织
    City or Locality : 城市或地方
    State or Province : 州或省
    Country Code(XX) : 国家代码

     

    要记住你填写的东西,有一些不是必填的可以不填.

    填写完成之后回到上一个页面,将你填好的信息填进去即可生成一个签名文件.

     

     

    3 . 设置gradle变量:

    编辑~/.gradle/gradle.properties(没有这个文件你就创建一个),添加如下的代码(注意把其中的****替换为相应密码)
    注意:~表示用户目录,比如windows上可能是C:\Users\用户名,而mac上可能是/Users/用户名。


    MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
    MYAPP_RELEASE_KEY_ALIAS=my-key-alias
    MYAPP_RELEASE_STORE_PASSWORD=*****
    MYAPP_RELEASE_KEY_PASSWORD=*****
    上面的这些会作为全局的gradle变量,我们在后面的步骤中可以用来给应用签名。

    如上图所示:

     

    添加签名到项目的gradle配置文件

    编辑你项目目录下的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
            }
        }
    }
    ...

     

     

     

     

    4.生成签名apk

    在RN项目根目录运行:

    cd android && ./gradlew assembleRelease     ---生产包

    cd android && ./gradlewassembleDebug  ---测试包

    cd android表示进入android目录(如果你已经在android目录中了那就不用输入了)。

    但是要注意:

    ./gradlew assembleRelease在macOS、Linux或是windows的PowerShell环境中表示执行当前目录下的名为gradlew的脚本文件,且其运行参数为assembleRelease,注意这个./不可省略;而在windows的传统CMD命令行下则需要去掉./。

    Gradle的assembleRelease参数会把所有用到的JavaScript代码都打包到一起,然后内置到APK包中。如果你想调整下这个行为(比如js代码以及静态资源打包的默认文件名或是目录结构等),可以看看android/app/build.gradle文件,然后琢磨下应该怎么修改以满足你的需求。

    生成的APK文件位于android/app/build/outputs/apk/app-release.apk,它已经可以用来发布了。

    也可以通过命令行直接安装到手机上:

    adb install (apk在PC上的路径/)*.apk

     

    ok 那么今天打包apk的过程就到此结束了~

     

     

    总结就是:
    1:
    运行: react-native bundle --entry-file index.android.js --platform android --dev false --bundle-output ./android/app/src/main/assets/index.android.bundle --assets-dest ./android/app/src/main/res/
    生成bundle包
    2:
    设置gradle变量, 修改gradle.propertites和build.gradle文件.
    3:

    Linux&&Mac:
    运行: cd android && ./gradlew assembleRelease 生成rease版本的apk 或者

    cd android && ./gradlew assembleDebug 生成debug版本的apk

    Windows:

    运行: cd android && gradlew assembleRelease 生成rease版本的apk 或者

    cd android && gradlew assembleDebug 生成debug版本的apk

     

    如果有flavor的话,则是 gradlew assemble渠道名Release

     

    昨天我升级android studio 3.0之后,Gradle版本也同样升级了,但是升级之后打包出现node_modules_reactnavigation_src_views_assets_backicon.png图片重复问题,这个原因我在Github上面找了好久,才发现是因为Gradle2.3之后,离线打包的路径都会在drawable-xxx-v4中,原版的离线路径在drawable-xxx中,所以导致图片重复问题,怎么解决这个问题呢:

    1,修改assetPathUtils.js

    assetPathUtils.js文件路径:node_modules\react-native\local-cli\bundle\assetPathUtils.js

    修改:getAndroidAssetSuffix方法

    修改前:

     function getAndroidAssetSuffix(scale) {
       switch (scale) {
        case 0.75: return 'ldpi';
        case 1: return 'mdpi';
        case 1.5: return 'hdpi';
        case 2: return 'xhdpi';
        case 3: return 'xxhdpi';
        case 4: return 'xxxhdpi';
       }
     }

    修改后:

     function getAndroidAssetSuffix(scale) {
       switch (scale) {
         case 0.75: return 'ldpi-v4';
        case 1: return 'mdpi-v4';
        case 1.5: return 'hdpi-v4';
        case 2: return 'xhdpi-v4';
        case 3: return 'xxhdpi-v4';
        case 4: return 'xxxhdpi-v4';
       }
     }

    展开全文
  • React Native 继续之前的升级的频率,差不多一个月升级一次。6月7号React Native 0.45.0正式发布。该版本有如下的重大变更: 1,删除 React 的转发和错误导入警告(Remove React forwarding and wrong import ...
  • 2019独角兽企业重金招聘Python工程师标准>>> ...
  • 调试react native的项目有一个报错: there is no route defined for key XXXX 它发生在我调试TabNavigator选项卡路由器的时候,我把如下代码的Agreement路由转移到了另一个StackNavigator中。明明我在...
  • 出现Warning:Failed child context type: Invalid child context 'virtualizedCell.cellKey' of type 'number' supplied to 'CellRenderer',expected 'string'的错误(如下图示) 这里是引用flatlist出现需要...
  • React Native(简称RN)列表是基于ScrollView实现的,也就是可以滚动的,然而RN并没有直接使用IOS或Android的原生列表组件,这是因为RN真正调用native代码的过程是异步的,二Native的渲染要求必须同步渲染的。...
  • 作者简介李焱,2010 年毕业于北京大学信息管理系,获得信息管理学士学位及计算机软件学士学位;...课程简介本课程主要讲解 React 的基础知识及应用案例,包括 props、state、生命周期函数等,样式和 F
  • 每当在模拟器上完成了开发,都想到真机上秀秀,正好前段时候买了一个mac,哈哈有机会了。...2.首先react native的环境要正确安装,还未完成这一步的,请到官网或中文站查看具体流程 3.xcode等环境安装完
  • React Native 语法指南

    2017-01-22 16:58:32
    React Native真的是越来越流行,没使用React Native开发项目都不好意思说自己是搞客户端开发的。对于纯Native开发者来说,刚上手React Native有一定的适应期,如果JavaScript也不熟练的话那就更悲催了。React Native...
  • 我们在项目里面,经常会用的批次渲染,比如一个列表渲染很多个item,或者一个横排或者竖排同时渲染多个数据等这种情况,一个个写显然是最笨的做法,当数据多时根本无法做到,此时很多人都会想到以下做法:render(){ ...
  • console.ignoredYellowBox = [‘Warning: BackAndroid is ... Please use BackHandler instead.’,‘source.uri should not be an empty string’,‘Invalid props.style key’]; console.disableYellowBox = true...
  • 项目中React Native ListView的长按删除功能分享: /** * Sample React Native App * https://github.com/facebook/react-native * @flow */ import React, {Component} from 'react'; import { AppRegistry, ...
  • js的严格模式下,当用数组来存储JSX标签的时候,如果jsx标签相同(dataSource当然不同) ,就会提示prop警告,解决办法就是为每个标签加一个不同的key,这样可以消除警告,同时也可以消除react自带的标签缓存。
  • 汇聚了RN常见的错误及解决方案,挺不错的
  • React-Native 打安卓包

    2018-11-14 21:35:18
    react-native 打包apk 之 安卓离线包全过程 2017年08月14日 15:47:03 __卓原 阅读数:10178更多 所属专栏: react native 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net...
  • 最初开发ReactNative的时候,完全没接触过React,大学那会简单学了点JS。毕业后一直在某五百强外企上班。做的东西也乱七八糟的,搞过大数据,搞过物联网,不过看我博客的同学应该知道,我对iOS和Android还是比较喜欢...
  • 转载:http://blog.csdn.net/pz789as/article/details/52537028 我们在项目里面,经常会用的批次渲染,比如一个列表渲染很多个item,或者一个横排或者竖排同时渲染多个数据。 例如: ... plain copy ... f
1 2 3 4 5 ... 20
收藏数 842
精华内容 336
关键字:

key警告 native react