# **RN开发经验**

## 一、环境配置
关于环境配置,前辈已有完整的总结:http://tvrn.devops.letv.com/docs/Environment.html

**IDE准备:** [AndroidStudio](https://developer.android.com/studio/index.html),[VisualStudioCode](https://code.visualstudio.com/)

如何打造现代化RN开发环境:http://tvrn.devops.letv.com/docs/tools/vscode.html

## 二、TVRN Demo 运行
TVRN是为了方便TV上的开发者使用的组件库。详见:http://tvrn.devops.letv.com/ (每个人都可以修改更新文档,入库后第二天早上10点更新)

git地址如下:

`git clone ssh://username@athena.devops.letv.com:29418/LETVRD/tvrn-libs`

playground项目是一个标准的RN-app工程,只不过是嵌在tvrn-libs里面,同时直接引用了tvrn-libs。 用途:

1、开发时作为组件的host,能直接看到组件的运行效果
    2、作为tvrn组件库的演示examples-app

###### **运行playground**
    1、在根目录执行 npm install(package.json中列出了项目所有依赖);
    2、修改vi playground/android/gradle/wrapper/gradle-wrapper.properties,
      注释掉从网络下载,使用本地gradle-2.10-all.zip;
    3、拷贝一个gradle包到playground/android/gradle/wrapper/目录下:
      cp ../*****/gradle-2.10-all.zip playground/android/gradle/wrapper/;
    4、根目录下执行tvrn run-android --root playground(--root指定目录);
    注:此Demo目前暂只能在EUI6.0上跑通。

## 三、使用组件
```JavaScript
import React, { Component } from 'react';

import {
    AppRegistry,
    Navigator,
} from 'react-native';
import { FocusableView } from 'tvrn-libs';
import AccountCenter from './src/ui/AccountCenter';

```
如果export default class AccountCenter,则AccountCenter不需要大括号,如import AccountCenter from './AccountCenter';

如果export class AccountCenter则不需要大括号,如import {AccountCenter} from './AccountCenter';
可以export出多个组件。

## 四、使用导航器跳转页面
Navigator使用纯JavaScript实现了一个导航栈,因此可以跨平台工作。

Navigator可以在renderScene方法中根据当前路由渲染不同的组件。默认情况下新的场景会从屏幕右侧滑进来,但你也可以通过configureScene方法来管理这一行为。

```JavaScript
<Navigator
    initialRoute={{ component: AccountLogin }} //这个指定了默认的页面
    configureScene={this.configureScene} //跳转动画
    renderScene={this.renderScene} /> //加载页面
```
跳转:
```JavaScript
_gotoRegister() {
  this.props.navigator.push({
    title: 'RegisterView1',
    component: RegisterView1,
    params: {//跳转携带参数
      name: 'test'
    },
    // type: 'Bottom'
  });
}
```

## 五、component生命周期

**RN中的component跟Android中的activity,fragment等一样,存在生命周期。**
![声明周期图](/home/fan/图片/picture/reactnative-component.png)

如图,可以把组件生命周期大致分为三个阶段:

* 第一阶段:是组件第一次绘制阶段,如图中的上面虚线框内,在这里完成了组件的加载和初始化;
* 第二阶段:是组件在运行和交互阶段,如图中左下角虚线框,这个阶段组件可以处理用户交互,或者接收事件更新界面;
* 第三阶段:是组件卸载消亡的阶段,如图中右下角的虚线框中,这里做一些组件的清理工作。

**下面来详细介绍生命周期中的各回调函数。**
#### **getDefaultProps**
在组件创建之前,会先调用 getDefaultProps(),这是全局调用一次,严格地来说,这不是组件的生命周期的一部分。

#### **getInitialState**
在组件被创建并加载候,首先调用 getInitialState(),来初始化组件的状态。

#### **componentWillMount**
这个函数调用时机是在组件创建,并初始化了状态之后,在第一次绘制 render() 之前。可以在这里做一些业务初始化操作,也可以设置组件状态。这个函数在整个生命周期中只被调用一次。

#### **render**
render绘制组件到界面上

#### **componentDidMount**
在组件第一次绘制之后,会调用 componentDidMount(),通知组件已经加载完成。从这个函数开始,就可以和 JS 其他框架交互了,例如设置计时 setTimeout 或者 setInterval,或者发起网络请求。这个函数也是只被调用一次。

#### **componentWillReceiveProps**
如果组件收到新的属性(props),就会调用 componentWillReceiveProps()。在这个回调函数里面,你可以根据属性的变化,通过调用 this.setState() 来更新你的组件状态,这里调用更新状态是安全的,并不会触发额外的 render() 调用。

#### **shouldComponentUpdate**
当组件接收到新的属性和状态改变的话,都会触发调用 shouldComponentUpdate(...)。这个函数的返回值决定是否需要更新组件,如果 true 表示需要更新,继续走后面的更新流程。否者,则不更新,直接进入等待状态。
默认情况下,这个函数永远返回 true 用来保证数据变化的时候 UI 能够同步更新。你可以自己重载这个函数,通过检查变化前后属性和状态,来决定 UI 是否需要更新,能有效提高应用性能。

#### **componentWillUpdate**
如果组件状态或者属性改变,并且上面的 shouldComponentUpdate(...) 返回为 true,就会开始准更新组件,并调用 componentWillUpdate()。
需要特别注意的是,在这个函数里面,你就不能使用 this.setState 来修改状态。
紧接着这个函数,就会调用 render() 来更新界面了。

#### **componentDidUpdate**
调用了 render() 更新完成界面之后,会调用 componentDidUpdate() 来得到通知,

#### **componentWillUnmount**
当组件要被从界面上移除的时候,就会调用 componentWillUnmount()。在这个函数中,可以做一些组件相关的清理工作,例如取消计时器、网络请求等。

|生命周期|调用次数|能否使用setState()|
|--|--|--|
|getDefaultProps|1|否|
|getInitialState|1|否|
|componentWillMount|1|是|
|render|>=1|否|
|componentDidMount|1|是|
|componentWillReceiveProps|>=0|是|
|shouldComponentUpdate|>=0|否|
|componentWillUpdate|>=0|否|
|componentDidUpdate|>=0|否|
|componentWillUnmount|1|否|

## 六、back键处理
**安卓back键的处理主要就是一个事件监听:**
```JavaScript
BackAndroid.addEventListener('hardwareBackPress', this.onBackPressed);
BackAndroid.removeEventListener('hardwareBackPress', this.onBackPressed);
```
**根据当前界面决定作何动作**
有时候我们有这样的需求:当用户处于某些界面下时,back键要做特殊的动作,如:提示用户是否要保存数据,或者解锁界面禁止back键返回等等。此时,最佳实践是在route或route中对应的Component上保存关于如何处理back键的信息:
```JavaScript
onBackAndroid = () => {
   const nav = this.navigator;
   const routers = nav.getCurrentRoutes();
   if (routers.length > 1) {
     const top = routers[routers.length - 1];
     if (top.ignoreBack || top.component.ignoreBack){
       // 路由或组件上决定这个界面忽略back键
       return true;
     }
     const handleBack = top.handleBack || top.component.handleBack;
     if (handleBack) {
       // 路由或组件上决定这个界面自行处理back键
       return handleBack();
     }
     // 默认行为: 退出当前界面。
     nav.pop();
     return true;
   }
   return false;
 };
```

## 七、RN中JS如何调用Java代码
1. 首先新建一个类继承自ReactContextBaseJavaModule这个抽象类
```java
public class LetvAccountManager extends ReactContextBaseJavaModule {
```

2. 重写getName方法,命名一下扩展。以后可以在js里面按照名字找到这个扩展。
```java
@Override
public String getName() {
    return "LetvAccountManager";
}
```
3. 实现暴露给js的接口
```java
@ReactMethod
public void removeLocalAccount(String userName, Promise promise) {
    Log.d(TAG, "do removeLocalAccount");
    LocalAccountConfig.removeAccoutRecord(mContext, userName);
}
```
4. 书写注册接口待用。新建一个MyPackage,在createNativeModules中完成注册接口。
```Java
public class MyPackage implements ReactPackage {

@Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<NativeModule>();
        modules.add(new LetvAccountManager(reactContext));
        return modules;
    }
```

5. 将写好的接口注册到MainActivity中去,其中有一个已经写好的方法 --- getPackages,们在其中,加入我们写好的接口(MyPackage)。
```java
    @Override
    protected List<ReactPackage> getPackages() {
        return Arrays.<ReactPackage>asList(
            new MainReactPackage(),
            new TvrnLibsPackage(),
            new com.letv.tvrn.overseaaccountdemo.MyPackage()
        );
    }
```
6. 到这一步终于可以js调用了

```JavaScript
  import { NativeModules } from 'react-native';

const LetvAccountManager = NativeModules.LetvAccountManager;
  LetvAccountManager.removeLocalAccount;
 ```

## 八、国际化 I18N
 **先来个名词解释:**

I18N,其来源是英文单词 internationalization的首末字符i和n,18为中间的字符数,是“国际化”的简称。

I18N是使用native监听系统语言变化后,JS端自动刷新的一套国际化方案,用法如下:

1、在I18N下添加配置文件,格式如下:
  ```JavaScript
  const zh_CN = {
    app_name: '乐视账号',
    account_login: '立即登录',
    account_find_password: '找回密码',
  };

export default zh_CN;
  ```

2、 在入口js中引用多语言组件,在constructor()中调用init()方法,再在componentWillMount()中使用timer延迟50ms后调用getLang()方法,最后声明ContextProps,向子组件传递数据。
```JavaScript
constructor(props){
  super(props);
  ....
  Language.init();
}

componentWillMount() {
    this.timer = setTimeout(() => {
      lang = Language.getLang();
      this.setState({
          ready:true,
      });
      },50
    );
  }

Main.childContextTypes = {
  language: React.PropTypes.object
};
```

3、子组件使用国际化组件
```JavaScript
AccountLogin.contextTypes = {
  language: React.PropTypes.object,
};

<Text style={styles.title_text}>
 {this.context.language.app_name}
</Text>

```

## 九、集成方案
把react-native中公共的so和jar集成到系统中,以减小APP的空间占用,使用gradle编译,引用系统中的reactnative so和jar。

在项目根目录下执行:
```
cd android && ./gradlew assembleRelease
```
会在android/app/build/outputs/apk目录下会有app-release-unsigned.apk,然后把此apk进行对应系统签名即可。

## 十、常见错误

1.invariant violation:expected a component class,got[object object]

创建自定义组件首字母要大写,否则会报错.比如<login/>应该写成<Login/>

2.Module 0 is not a registered callable module.

将gradle升级成最新版本(cd Android 进入android目录执行:sudo ./gradlew clean) 或者通过android studio工具升级.

3.Element type is invalid: expected a string (for built-in components) or a class/function but got: object
   一般是你引用了无效的组件,如果组件确实正确,看下引用的组件是否正常导出:(export defalut)

4.react native  undefined is not an object (evaluating this....

发生该错误的一般是忘记bind(this),只要回调函数中需要用到this的,一般都需要bind.
  如:onPress={this.doRegister()} ==》 onPress={this.doRegister.bind(this)}

5.Could not get BatchedBridge, make sure your bundle is packaged corrrectly

adb reverse tcp:8081 tcp:8081

6.await is a reserved word

函数中有异步操作的时候,函数名前要加async,如async function getSystemAccount() {}

7.The android gradle plugin version 2.3.0-beta1 is too old, please update to the latest version.

修改playground/android/gradle/wrapper/gradle-wrapper.properties,注释掉从网络下载,使用本地gradle-2.10-all.zip;
```
# distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
  distributionUrl=./gradle-2.10-all.zip
```

8.Unexpected character ‘�’ (1:0) 图片加载错误

根目录下重启服务 tvrn start

## 图书分享

《React Native开发指南》

《React Native入门与实战》

链接: https://pan.baidu.com/s/1pLqFr2z 密码: pp65

转载请注明出处!

react-native开发经验的更多相关文章

  1. 📝 没 2 年 React Native 开发经验,你都遇不到这些坑

    如果你喜欢我的文章,希望点赞 收藏 评论 三连支持一下,谢谢你,这对我真的很重要! React Native 开发时,如果只是写些简单的页面,基本上按着官方文档 reactnative.dev就能写出 ...

  2. 【腾讯Bugly干货分享】React Native项目实战总结

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/577e16a7640ad7b4682c64a7 “8小时内拼工作,8小时外拼成长 ...

  3. 腾讯优测优分享 | 探索react native首屏渲染最佳实践

    腾讯优测是专业的移动云测试平台,旗下的优分享不定时提供大量移动研发及测试相关的干货~ 此文主要与以下内容相关,希望对大家有帮助. react native给了我们使用javascript开发原生app ...

  4. 探索react native首屏渲染最佳实践

    文 / 腾讯 龚麒 0.前言 react native给了我们使用javascript开发原生app的能力,在使用react native完成兴趣部落安卓端发现tab改造后,我们开始对由react n ...

  5. 使用React Native来撰写跨平台的App

    React Native 是一个 JavaScript 的框架,用来撰写实时的.可原生呈现 iOS 和 Android 的应用.其是基于 React的,而 React 是 Facebook 的用于构建 ...

  6. React Native 系列(一) -- JS入门知识

    前言 本系列是基于React Native版本号0.44.3写的,最初学习React Native的时候,完全没有接触过React和JS,本文的目的是为了给那些JS和React小白提供一个快速入门,让 ...

  7. React Native 系列(二) -- React入门知识

    前言 本系列是基于React Native版本号0.44.3写的,最初学习React Native的时候,完全没有接触过React和JS,本文的目的是为了给那些JS和React小白提供一个快速入门,让 ...

  8. react native中一次错误排查 Error:Error: Duplicate resources

    最近一直在使用react native中,遇到了很多的坑,同时也学习到了一些移动端的开发经验. 今天在做一个打包的测试时,遇到了一个问题,打包过程中报错“Error:Error: Duplicate ...

  9. React Native基础&入门教程:初步使用Flexbox布局

    在上篇中,笔者分享了部分安装并调试React Native应用过程里的一点经验,如果还没有看过的同学请点击<React Native基础&入门教程:调试React Native应用的一小 ...

  10. 一次掌握 React 与 React Native 两个框架

    此系列文章将整合我的 React 视频教程与 React Native 书籍中的精华部分,给大家介绍 React 与 React Native 结合学习的方法. 1. 软件开发语言与框架的学习本质 我 ...

随机推荐

  1. spring boot使用guava缓存

    1.pom中插入依赖: <!--guava缓存cache--> <dependency> <groupId>com.google.guava</groupId ...

  2. Elasticsearch(7) --- 复合查询

    Elasticsearch(7) ---复合查询 复合查询有:bool query(布尔查询).boosting query(提高查询).constant_score(固定分数查询).dis_max( ...

  3. Android程序员接下来的路该如何走?

    随着“5G”(第五代移动通信技术)商用进程越来越快,各个芯片和终端厂商们都已经开始布局准备,想必智能手机会是消费者最先能够接触到5G的重要终端,而和其相辅相生的移动互联网也势必会有新的发展. 但是和行 ...

  4. spring 事务配置方式以及事务的传播性、隔离级别

    在前面的文章中总结了spring事务的5中配置方式,但是很多方式都不用而且当时的配置使用的所有参数都是默认的参数,这篇文章就看常用的两种事务配置方式并信息配置事务的传播性.隔离级别.以及超时等问题,废 ...

  5. 设置composer镜像地址为阿里云的方法

    所有项目都会使用该镜像地址: composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ 取消配置: ...

  6. 基于 HTML5 WebGL 的医疗物流系统

    前言 物联网( IoT ),简单的理解就是物体之间通过互联网进行链接.世界上的万事万物,都可以通过数据的改变进行智能化管理.ioT 的兴起在医疗行业中具有拯救生命的潜在作用.不断的收集用户信息并且实时 ...

  7. Android 本地化适配:RTL(right-to-left) 适配清单

    本文首发自公众号:承香墨影(ID:cxmyDev),欢迎关注. 一. 序 越来越多的公司 App,都开始淘金海外,寻找更多的机会.然而海外市场千差万别,无论是市场还是用户的使用习惯,都有诸多的不同. ...

  8. 在IIS上启用WordPress子域名模式多站点功能

    昨天负责网站的支持人员向我反馈在我们负责托管网站的WordPress在启动多站点功能后,浏览新站点或访问新站点的"Dashboard"时,都会反馈“404”错误.我检查了WordP ...

  9. [Leetcode] 第290题 单词模式

    一.题目描述 给定一种 pattern(模式) 和一个字符串 str ,判断 str 是否遵循相同的模式. 这里的遵循指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词 ...

  10. 关于svn更新失败,clearup异常解决

    直接上主题: 1. 下载sqlite3工具(https://files.cnblogs.com/files/eric-fang/sqlite-tools-win32-x86-3210000.zip), ...