官网没说的那些React-native 小心得

上个迭代用React-native做了一个一期新的需求,在开的过程中举步维艰,如履薄冰啊,做过react的同学也知道,现在React-native 版本才到0.43,还不算一个成熟的技术,开发的过程中就是不确定性,开发一个新需求用着一个心里没底的技术,还是真是有点小压力,不过还好,问题归问题,但都不是什么大问题,想必一个只有0.43 的版本到现在 其实已经不错了,我相信React-native 以后会有很大的潜力的,废话不多说,说一下自己的心得,供大家参考,如果有什么不对的,还请轻喷,然后咱们再共同商讨,一起学习。

一、调试

1.我开发Rn喜欢打log,项目里每个方法,每个生命周期 数据回调之类的通过log去看,执行的顺序一幕了然,我不用Debug 模式调试是一因为debug 速度慢,然后还得一层一层打开去找源码打断点,二是因为 Debug是个终极武器似的,现在的程度还用不到打断点的方式,除非出什么诡异的问题你看不到log ,这个时候Debug也是个很好的工具。关于打log 请看我的这篇文章React-native调试小技巧,在Logcat输出console的log,不用摇晃也能弹出Debug 弹窗

2.用数据线连接着真机去调试,而不是用无线。有人问 既然有无线了为什么还用数据线,这您有所不知,在我们公司,不知怎么得,公司的公共WiFi(集团级的无线网络)不知道封了什么东西,导致在同一个局域网下边的机器找不到另一个机器端口8081下的bundle包,Charles也不管用了,坑爹的WiFi啊,我们采取了用网线分一个自己的无线热点,然后这样就可以了,不过这两个各有优点,无线调试就是方便,不用限定在自己的工位,自由拿着手机随便跑,但是每次都得去debug弹窗输入ip:port,累觉不爱,但是基于自己开发的情况,在工位开发不用随便乱跑,然后数据线开发在终端输入一条命令就搞定
adb reverse tcp:8081 tcp:8081不过这个也有缺点,就是拔掉了数据线 然后再重新按上的时候还得重新adb reverse tcp:8081 tcp:8081,插入别的iPhone手机也会导致 必须重新reverse,总之要不是因为网络的原因 我也不会选择用数据线调试。

二、继承。

个人认为js也是面向对象型的编程语言,只不过不用提前编译运行。所以就大胆的想出了继承的想法。在js中 继承的目的主要是为了复用UI,并且逻辑性不大的UI,纯粹只是为了展示,并且UI差异不大的话,可以用继承的方式去减少代码的编写(ps:减少的那些写代码的时间还不够出问题调试的时间呢,唉),比如 我有个详情页的展示,只不过有个按钮的展示的ui和逻辑是不同的,大部分是可以复用的,所以父类的写法应该是

export default class BaseDetailPage extends Component {
    // 构造
    constructor(props) {
        super(props);
        // 初始状态
        this.state = {};
    }

    componentDidMount() {
        this.getData();//去获取详情页数据,每个详情页的接口是不一样的,所以这个方法应该是在子类里面
    }

    componentWillUnmount() {

    }

    /**
     * 数据成功的回调
     * @param response
     */
    handleAppDetailSuccess(response) {
        this.setState({
            appDetail: response.data,
        });
    }

    /**
     *  数据获取失败的回调
     * @param response
     */
    handleAppDetailFailed(response) {
//做些失败的处理
    }

    render() {

        return (
            <View style={styles.container}>
                {this.renderLogo()}
                {this.renderSizeInfo()}
                {this.renderButton()}//这一块是调用的子类的方法

            </View>)
    }

    /**
     * 自己去写Ui
     */
    renderLogo() {


    }

    /**
     * 自己去写Ui
     */
    renderSizeInfo() {

    }
}

子类A的代码:

export default class DetailA extends BaseDetailPage {
    // 构造
    constructor(props) {
        super(props);
        // 初始状态
        this.state = {};
    }

    /**
     * 如果子类也要用这个生命周期,必须先调用父类的
     * 不然就不会执行了
     */
    componentDidMount() {
        super.componentDidMount();
    }


    /**
     * 子类获取详情页信息,但是最终的数据还是要回传给父类,所以调用父类的回调方法
     * 比如
     */
    getData() {
        NetUtil.get("http://www.baidu.com", this.handleAppDetailSuccess.bind(this),
            this.handleAppDetailFailed.bind(this))
    }

    /**

     * 每个子类必须重写这个方法,因为父类会调用
     * 重写这个空方法表明这个子类什么都不做
     * @returns {XML}
     */
    renderButton() {
        return <TouchableHighlight onPress={()=> {

        }
        }>
            <Text> 我是子类A 的button</Text>

        </TouchableHighlight>
    }
}

子类B的实现:

export default class DetailB extends BaseDetailPage {
    // 构造
    constructor(props) {
        super(props);
        // 初始状态
        this.state = {};
    }

    /**
     * 如果子类也要用这个生命周期,必须先调用父类的
     * 不然就不会执行了
     */
    componentDidMount() {
        super.componentDidMount();
    }
    
    /**
     * 子类获取详情页信息,但是最终的数据还是要回传给父类,所以调用父类的回调方法
     * 比如
     */
    getData() {
        NetUtil.get("http://www.baidu.com", this.handleAppDetailSuccess.bind(this),
            this.handleAppDetailFailed.bind(this))
    }

    /**

     * 每个子类必须重写这个方法,因为父类会调用
     * 重写这个空方法表明这个子类什么都不做
     * @returns {XML}
     */
    renderButton() {

        return <TouchableHighlight onPress={()=> {

        }
        }>
            <View>
                <Text> 我是子类B 的button</Text>
                <Text> 我比A多了好几个view</Text>
                <Text> 我们都复用这个方法</Text>
            </View>
        </TouchableHighlight>
    }
}

总结:

1.注释大体说了一下子类父类之间的关系,其实这样写还是有很对优点的,比如在一个很复杂的页面中,复用父类是很简单的一个事情,每块代码逻辑简单明了,没有业务的耦合,不用标志位字段判断 。
2.复用父类会出现一些问题,比如那个生命周期的复用,必须调用父类。其实和android中的生命周期有点类似。特别说明一些state,子类和父类的state都是相互可见的,但是必须先定义才能调用,比如父类定义this.setState({appDetail:null}),子类如果想用这个父类的字段,必须先定义这个字段。赋值的事情就交给父类就行了。
3.本人亲测,在用父类的情况下,Rn的hot-loading 貌似失效了,修改了子类只会弹出toast,然后页面没有变化,必须重新退出之后然后在进入,才能看到。如果修改了父类会报错

undefined is not an object (evaluating ‘internallnstance._pendingForceUpdate’)

解决办法 重新reload就行了,这个我不能解释。

三、DeviceEventEmitter的正确用法

说DeviceEventEmitter 这个类之前先介绍一些这个类是干嘛的。DeviceEventEmitter类似于android的广播,只要注册了要监听的东西,并且有相应的发送消息的地方,就会收到这个广播。
主要用在两个地方:

  • native 像js发送信息
  • js 向js发送消息
    一般我们会在js初始化的函数componenDidMount()去注册监听
//第一步 
 componentDidMount() {
        this.addProgressListener();
    }
//第二步
  addProgressListener() {
        this.progerss = DeviceEventEmitter.addListener('onProgress', this.progressListener.bind(this));
    }
//第三步
  progressListener(params) {
      
    }

当然你也可以一气呵成的去完成这个注册监听的方法,看方法名也知道这个是个下载的过程,我把下载的任务放到了 native层面去做,需要rn实时的去展示,
看第三部的param,这个数据结构是jsonObject还是jsonOBjectArray 还是要看native怎么拼装的。

 public void sendProgress(String s, int progress) {
   
    // 发送参数
    WritableMap map = Arguments.createMap();
    map.putInt(PARAM_PROGRESS, progress);
    map.putString(APP_ID, s);
//{"appId":124,"progerss":30%} 这是一个jsonObject 当然你可以根据业务拼接 //Arguments.createArray()
    getReactApplicationContext()
        .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
        .emit("onProgress", map);//这个消息发送只会发送到监听`onProgress `这个字段的地方

  }

个人感觉Rn的这个消息机制还是不错的,但是如果你感到满足了而不继续学习,接下来会让你很痛苦的。
为什么呢?因为你学会了注册,但是没学会移除监听,会引发很多的问题。

比如这个

setState(…): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op

其实 这个不算问题,只能算一个warning,但是有warning 了那么离error还远吗。其实这个warning是因为你页面都卸载了,但是这个监听还存在着,就和android的内存泄露一样,一直刷着没有mount的组件。那么只需要正确的移除就行了,楼主当你就是没有用到正确的移除方法,导致遇到了一个问题查了两天,竟然没查到,最后才想到是移除监听的方法写错了。

正确的移除方式

this.progress.remove();//这个值请看第二步

也可以这样

 DeviceEventEmitter.removeAllListeners();// 如果这个有back的监听,
//也会把 back健的监听也除去,不建议用这个方法

四、小知识

1.项目中用了一个第三方库react-native-progress这个库的star有700多

progress
progress

但是这个项目有个问题 ,就是在debug的模式下 会使用indeterminate属性为true会崩溃,所以 就考虑到了有没有一个全局的变量能判断是在release模式还是在debug模式。其实是有的 这个值就是__DEV__

  if(__DEV__){
//Debug 模式
  }else{
//Release模式
  }

2.如果在一个方法中调用this.setState()两次,那么整个页面也会render两次。

五、工具

作为开发,各种工具必须的玩的666的,如果我作为面试官,对于工具的使用必须会在我的面试范围之内,一个不爱瞎折腾,一个不愿接受新事物的人谈什么创造力。
1.Charles 一个很牛逼的抓包神器。这次主要用了他的抓Https、模拟慢网速、

模拟网速
Paste_Image.png

1、入口:Proxy--Throttle Settings
2、可在“Throttle Preset”下选择 预置的网络配置(28.8~256kpbs、3G等)
3、可调节带宽、利用%比、延迟,来模拟网络环境。
备注:用的多的就是可用Throttle Preset设置2G 3G 4G,模拟不同网速的移动网络下app运行情况; 设置延迟,查看高延迟情况下,app提示及处理是否正常。

Https抓包
  1. charles的设置
    1.1 选择 help | Install Charles CA SSL Certificate
Paste_Image.png

1.2 然后会密码输入框,然后输入点击确定,Charles会把证书写进你的钥匙串了

Paste_Image.png

1.3 选择Proxy | Proxy Settings,弹出proxy设置选项卡,勾选Enabling transparent HTTP proxying

Paste_Image.png

1.4 选择ssl,勾选Enable SSL Proxying,在Location部份选择add,按如下图添加,抓取任意站点、443端口的数据

Paste_Image.png

1.手机安装证书。
下载证书 http://www.charlesproxy.com/documentation/additional/legacy-ssl-proxying/

Paste_Image.png

2.安装证书

2.1、Android:把证书放到储存设备上,然后 设置-安全-从SD卡安装

怎么把证书放到存储设备上,这个是我要说的,看着测试没还要打开文件存储,然后复制粘贴真是费劲

发送文件

adb push charles-proxy-ssl-proxying-certificate.crt /sdcard/   

[100%] /sdcard/charles-proxy-ssl-proxying-certificate.crt

支持重命名

拷贝文件

adb pull /sdcard/charles-proxy-ssl-proxying-certificate.crt

[100%] /sdcard/charles-proxy-ssl-proxying-certificate.crt

2.2、ios:先用邮件把证书发到IPHONE上的邮箱,用iFile打开安装

其实Charles还有很多强大的功能,比如Map、Rewrite等功能,其实还都用过,但是在这就不多说了,要说的话就要另开新帖了,这儿重点是讲rn

最后提醒大家一定要仔细,往往稍微一不留神就会造成一个很小的bug但是你不得不花费时间去调试bug,到时候回过头来你会恨自己为什么当初不仔细一点,这也证明了很多bug就是因为开发的时候不仔细造成的。

今天是五一假期啊,我却在这码字~祝大家有个好的假期。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容