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

# **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

> android/app/build.gradle 文件不要删除，不要修改，为了 android 可以正常打包发布到 appcenter（热更新）

[管理中心](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)

#### 发布

```
    "update-ios-debug": "      appcenter codepush release-react --target-binary-version '*' --entry-file ./index.js --deployment-name Debug      --app fht360.com/fht-ios --development true",
    "update-ios-staging": "    appcenter codepush release-react --target-binary-version '*' --entry-file ./index.js --deployment-name Staging    --app fht360.com/fht-ios",
    "update-ios-release": "    appcenter codepush release-react --target-binary-version '*' --entry-file ./index.js --deployment-name Production --app fht360.com/fht-ios",
    "update-android-debug": "  appcenter codepush release-react --target-binary-version '*' --entry-file ./index.js --deployment-name Debug      --app fht360.com/fht-android --development true",
    "update-android-staging": "appcenter codepush release-react --target-binary-version '*' --entry-file ./index.js --deployment-name Staging    --app fht360.com/fht-android",
    "update-android-release": "appcenter codepush release-react --target-binary-version '*' --entry-file ./index.js --deployment-name Production --app fht360.com/fht-android",
```

### 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包的发布，每次push后都会自动往npm上publish
```
