> 以下内容都是以 iOS 平台的角度来说的，相应的第三方库， Android 需要按照官网进行与 Android 原生的集成。

# **react-native 项目说明**

- [react-native 官网](http://facebook.github.io/react-native/)
- [react-native 中文网](https://reactnative.cn/)
- [react-native github](https://github.com/facebook/react-native)
- [react-native guide](https://github.com/reactnativecn/react-native-guide)
- [react-native awesome](https://github.com/jondot/awesome-react-native)
- [typescript 中文网](https://www.tslang.cn/docs/home.html)
- [storybook 官网](https://storybook.js.org/docs/basics/writing-stories/)
- [react 中文官网](https://react.docschina.org/docs/hello-world.html)
- [redux 官网](http://cn.redux.js.org/)
- [code-push 官网](https://appcenter.ms/apps)

### 原生端（iOS、Android）需要集成的第三方库

- [React](https://reactnative.cn/docs/integration-with-existing-apps/)
- [react-native-gesture-handler](https://docs.swmansion.com/react-native-gesture-handler/docs/)
- [code-push](https://github.com/microsoft/react-native-code-push)
- [react-natvie-linear-gradient](https://github.com/react-native-community/react-native-linear-gradient)
- [react-native-charts-wrapper](https://github.com/wuxudong/react-native-charts-wrapper/blob/master/installation_guide/README.md)
- [react-native-reanimated](https://docs.swmansion.com/react-native-reanimated/docs/1.x.x/getting_started)

### react-native 集成到 iOS 项目中

> 中文网上给的[文档](https://reactnative.cn/docs/integration-with-existing-apps/)是以 react-native 项目为主目录，把 iOS、Android 项目为子目录。我这里的做法是把 iOS、Android、react-native 分别作为独立的项目，iOS、Android 项目通过 npm 包的方式依赖 react-native 项目。

- 建立 fht-react-native 文件夹，以后所有的 react-native 相关文件都放在其中
- 进入 fht-react-native 目录，执行以下命令，创建 package.json 文件，暂时不需要修改内容都使用默认值

    `npm init -y`

- 使用 npm 安装 react-native

    `npm install react-native`

- 根据上一步给出的警告信息，添加对应版本的 react

    `npm install react@version`

- 这个 react-native 的项目最终是作为一个npm包供 iOS、Android使用的，我们使用 Github Actions 来自动化这个过程，每次push后，都会自动发布一个新版本的npm包

- 进入到 iOS 项目目录 fht-iOS 下，在 Podfile 文件中加入react-native依赖库，并安装`pod install`（首先要安装上一步产生的npm包，可以查看 iOS 项目中的 README ）

- 我们项目使用typescript，所以按照官网配置一下

- 在 iOS 中需要接入 react-native 的 viewController 中加入以下代码，注意 moduleName 与 react-native 中导出的模块名称要一致

    ```
    NSURL *bundleUrl = [NSURL URLWithString:@"http://ip:8081/index.bundle?platform=ios"];
    RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:bundleUrl moduleName:@"FHTTrafficStatistics" initialProperties:@{} launchOptions:@{}];
    rootView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:rootView];

    // 需要设置rootView的上下左右约束
    ```

- 创建以.ts/.tsx 为后缀的文件，编写业务代码
- 创建 index.js 入口文件，并加入以下代码

    ```
    import {AppRegistry} from 'react-native';
    import TrafficStatisticsContainer from './TrafficStatistics/Container';
    AppRegistry.registerComponent("FHTTrafficStatistics", () => TrafficStatisticsContainer);
    ```

- 开启 node 服务

    `npm start`

- 运行 iOS 项目，进入到加入了 react-native 项目的页面，可以看到运行的效果。

### 对项目目录的解释

![项目目录](./readme/dir.png)

- **_api.ts_**：所有的 api，由 swagger_gen.sh 脚本生成
- **_src_**：项目代码
- **_dist_**：生成的bundle包和资源
- **_index.js_**：react-native 项目入口文件
- **_swagger_gen.sh_**：生成 api.ts 的脚本
- **_tsconfig.json_**：typescripe 配置文件

### 对 FHTNativeModules.ts 文件的解释

**_是模块的声明文件，模块是指原生（iOS/android）项目暴露给 react-native 项目的模块。_**

**先看一下 iOS 端写的要导出的模块有哪些：**

![iOS暴露的模块](./readme/ios_module.png)

1. **_FHTRNEventEmitManager_**：负责发送事件到 react-native
2. **_FHTRNEventHandleManager_**：负责处理 react-native 的事件，比如点击
3. **_FHTRNMethodManager_**：负责导出方法供 react-native 使用
4. **_FHTRNConstManager_**：负责导出常量供 react-native 使用

**以 FHTRNConstManager 为例进行解释**

下图红色字体表示的为 iOS 端导出的常量名称
![FHTRNConstManager](./readme/const_manager.png)

**不创建 FHTNativeModules.ts 时的使用方式**

使用过程中是完全没有提示的
![FHTRNConstManager](./readme/old_use.png)

**创建 FHTNativeModules.ts 后的使用方式**

先看看 FHTNativeModules.ts 的内容
![FHTRNConstManager](./readme/native_modules.png)

使用过程中会有提示
![FHTRNConstManager](./readme/new_use.png)

**_需要注意的是，每当原生端有新的模块导出，都要在 FHTNativeModules.ts 中增加对应的声明_**

### 对 Color.ts 的解释

> 文件在./Const/Color.ts

**_导出的一个字符串枚举，枚举内容为 16 进制颜色字符串，这么做的目的是可以方便有提示的使用颜色。对前端理解不深，不知道正常应该如何处理，这是我自己的想法_**

Color.ts 文件的内容
![FHTRNConstManager](./readme/color.png)

**没有 Color.ts 使用颜色时，且没有提示**

```
...
backgroundColor: '#FC3A0D',
...

```

**有 Color.ts 使用颜色时，且有提示**

```
import Color from './Color'

...
backgroundColor: Color.cf03ad,
...

```

### 对 NetworkTool.ts 的解释

**_包装一下请求，对每个请求加入我们自己的 headers，比如 Authorization 等等_**

NetworkTool.ts 文件的内容
![NetworkTool](./readme/network_tool.png)

使用方法

```

import NetworkTool from './NetworkTool'
import \* as Api from './api'

...
getMobileProductVisitDetail = async () => {
const api = await NetworkTool.restfulApi(Api.StatisticsApiApi);
const result = await api.statisticsApiMobileProductVisitStatistics({timeRange: 'Today', size: 50});
}
...

```

### chart 的选择

在 github 上找了几个跨平台的画图的库

1. [react-native-chart](https://github.com/tomauty/react-native-chart)
2. [victory-native](https://github.com/FormidableLabs/victory-native)
3. [react-native-charts-wrapper](https://github.com/wuxudong/react-native-charts-wrapper)
4. [react-native-svg-charts](https://github.com/JesperLekland/react-native-svg-charts)
5. [react-native-pathjs-charts](https://github.com/capitalone/react-native-pathjs-charts)

简单的使用过以后，综合考虑易用性、库本身是否维护、文档等，最终选择了`react-native-charts-wrapper`做为我们项目中使用的库。

### 热更新服务

> 热更新服务简单点说就是一个云服务器，用于存放 reactnative 代码打包后的 bundle 包，供原生端去下载。

目前比较常用的热更新服务

- [微软热更新服务](https://github.com/Microsoft/react-native-code-push)（现已成为[appcenter](https://github.com/microsoft/appcenter)的一部分）
- [react-native 中文网热更新服务](https://github.com/reactnativecn/react-native-pushy)
- [搭建自己的 react-native 热更新服务](https://github.com/lisong/code-push-server)

我首先尝试了中文网提供的热更新服务，按照其[github](https://github.com/Microsoft/react-native-code-push)上的教程集成时（因为我们是在原有项目基础上集成，所以只能手动链接），结果失败了；后来通过自己创建 RCTHotUpdate.podspec 文件才算集成成功，使用后发现并不是那么好用，所以最终选择了微软提供的热更新服务。

#### 微软热更新 code-push

[管理中心](https://appcenter.ms/apps)  
[文档](https://docs.microsoft.com/zh-cn/appcenter/distribution/codepush/)

> 先明白几个概念
> - operatingSystem: 操作系统，比如 iOS、Android
> - platform: 运行平台，比如 React-Naitve、Cordova
> - app: 一个应用
> - deployment: 可以理解为app的不同环境
> - release: 可以理解为deployment的不同版本
> 
> 创建app时要指定operatingSystem和platform，而一个app可以包含多个deployment，而一个deployment可以包含多个release，而最终的bundle包可以理解为release。

- 我们把.ipa(iOS)、.apk(Android)称为二进制文件，这个二进制文件是有版本的，把react-native打包后js称为bundle包，这个bundle包也是有版本的，热更新就是在不修改二进制文件版本的情况下修改bundle包的版本。  
- 实现的原理就是在二进制文件中埋入指定代码，也就是code-push提供的sdk，这个sdk会检查当前二进制文件有没有更新的bundle包，如果有就会把bundle包下载下来，这样bundle包就更新了，内容也就更新了。  
- 这样就决定了，发布bundle包时要指定其有效的二进制文件的版本，具体为什么要这样做，可以查看[版本说明](https://docs.microsoft.com/zh-cn/appcenter/distribution/codepush/using-ui#target-version)，不过指定的二进制版本可以是一个范围表达式，只要在这个范围的二进制都会检测到新的bundle包。
![目标版本](./readme/target_version.png)

### Android 引入 node_modules 中库时需注意的点

1、使用时需在项目根目录 setting.gradle 中配置引入的 module 名称，例如：
include ':app',':react-native-code-push'
project(':react-native-code-push').projectDir = new File(rootProject.projectDir, FHT_REACT_NODE_MODULE_PATH + 'react-native-code-push/android/app')

（1）第一行 include 表示 app 下 build.gradle 中引入该 module 的别名，implementation project(':react-native-code-push')
（2）第二行表示引入的 module 的名称及其 android 项目路径，所选择路径为 src 文件夹的上一级路径；

2、fht 项目 buildType 有三种，所以引入 module 时需检查该 module 的 build.gradle 是否也配置了，如果未配置，则需手动添加
buildTypes {
release {}
dev {}
debug {}
}

备注：
方式(1)：使用fht/fix_react_native_lib_build_gradle.js配置node_modules下的相关库【该方式已废弃，建议使用方式（2）】
方式(2)：使用fht-react-native/fix_module_lib_build_gradle.js配置nodules下的相关库

3、由于项目已迁移至Androidx,fht-react-native中引入的库react-native-gesture-handler、react-native-charts-wrapper、
react-native-code-push以及react-native-linear-gradient中的部分文件中android.support.xxxx的引用需要修改至androidx引用，这个每次node_modules重新生成的时候需要检查下文件是否正确（目前node_modules生成后运行项目会因这个问题报错）
// TODO nodejs 脚本检测库引用中android.support.xxx的引用并替换为androidx功能

4、node_modules 下的库引用时，大部分 module 都会从根目录中获取 compileSdkVersion 等信息，项目配置时最好能在根目录
的 build.gradle 中配置全局变量：
ext {
compileSdkVersion = 28
buildToolsVersion = "28.0.3"
minSdkVersion = 19
targetSdkVersion = 28
}


4、【重要】每次重新 npm install or yarn install 后需重新检查 setting.gradle 中引入的 node_modules 下的对应的 module 的
build.gradle 是否仍然是添加注意点 2 中的 buildType...
```


5、【重要】
1、@zhangmenglai windows上 目前只能使用node 10运行react(index_android_bundle包也需要使用node 10),node 12及以上暂时用不了,调试及打包时
需要注意node版本切换；
2、@zhangmenglai windows上不要使用cnpm install安装包，会出现打包失败情况，错误很奇葩，使用npm install;
3、

## 注意事项

- react-native项目是独立存在的，Android、iOS项目对react-native的引用是通过npm包的方式实现的，我们通过github action的方式进行自动npm包的发布，不过触发条件是有新的tag，但是由于bump-version的原因，我们的tag不要打成x.x.x的方式，而是写成vx.x.x方式