React Native 入门实践

React Native

Learn once, write anywhere.
React Native为前端开发工程师开发Native应用提供了一种能力。保障开发效率,同时兼顾平台的性能。

React

相比于传统的DOM的优势

  • VirtualDOM 高效diff算法(O(n^3) -> O(n)),实现局部刷新
  • 对外暴露Component,js & css 统一管理

相比于传统WebView H5优势

  • 不需要兼容各个WebView了
  • 效率会更高,它最终会调用native组件本地的渲染。

Get Started

参考官网或者中文网的Get Started,里面介绍了如何配置环境,以及通过简单的命令很快就能run出一个简单的示例工程,然后在index.ios.js或者index.android.js里简单改吧改吧就能开启React Native开发之旅了。

NPM(Yarn)

npm 全称是 Node Package Manager,是随同NodeJS一起安装的包管理工具,包括包(模块)的下载,安装, 和发布,以及管理。Yarn是Facebook提供的npm的替代工具,一般我们通过一个package.json文件做配置。当我们执行npm init命令的时候,会自动提示我们输入以下信息,得到package.json

{
  "name": "ls",
  "version": "1.0.0",
  "description": "this is a test",
  "main": "index.js",
  "dependencies": {},
  "devDependencies": {},
  "scripts": {
    "test": "test"
  },
  "repository": {
    "type": "git",
    "url": "\"\""
  },
  "keywords": [
    "123"
  ],
  "author": "sl",
  "license": "UNLICENSED"
}

对应我们在React Native中的配置,举例

{
  "name": "RN",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start"
  },
  "dependencies": {
    "react": "15.4.2",
    "react-native": "0.44.0",
    "react-native-linear-gradient": "^2.0.0"
  }
}

当我们执行npm start命令的时候,其实执行的就是node node_modules/react-native/local-cli/cli.js start,其中cliCommand line interface的缩写,在local-cli里相当丰富的命令。

构建Android应用

1、新建一个普通的Adroid工程,以ReactApplication为例。
2、在工程目录里配置package.json,如上配置即可,执行npm install,安装npm组件和下载react native 包。
3、在Project的build.gradle配置,上步会把react-native的相关代码打成arr,jar,pom等格式放到/node_modules/react-native/android目录下,新版React Native只通过npm发布,所以,需要在jcenter外配置本地仓库,获取依赖包。

repositories {
     jcenter()
     maven {
         // All of React Native (JS, Android binaries) is installed from npm
         url "$projectDir/../node_modules/react-native/android"
     }
}

4、在主module里配置

compile "com.facebook.react:react-native:0.44.0"  

5、 工程目录下,新建index.android.js

import React, { Component } from 'react';
import {
   AppRegistry,
   StyleSheet,
   Text,
   View,
   Image
} from 'react-native';

class App extends Component {
  render() {
    return (
      
        Welcome to React Native
        
      
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  note: {
    fontSize: 20
    },
  sss: {
    width : 200,
    height : 100
  },
});

AppRegistry.registerComponent('react-native-module', () => App);

在React Native中,JS端对外暴露的是一个个Component(组件),AppRegistry模块是React Native应用运行JS的入口,所有的根组件都可以registerComponent来注册,这里是App,react-native-module是对外(Native)暴露的组件的名称。

6、自定义ReactInstanceManager可以理解成React的超级管家,我们可以进行各种配置。 然后内部会做一系列的初始化工作:包括index.android.bundle的加载,js和native module的注册等等,在调试模式下会生成DevSupportManager,所有调试相关的如:调试弹窗(RedBox),与本地server端的通信,bundle的更新与加载都由它统一管理。

@Override
protected ReactInstanceManager createReactInstanceManager() {
    ReactInstanceManager reactInstanceManager = ReactInstanceManager.builder().setApplication(getApplication())
            .setBundleAssetName("index.android.bundle")
            .setJSMainModuleName("index.android")
            .setJSBundleFile("XXX")
            .addPackage(new MainReactPackage())
            .addPackage(new ReactCellPackage())
            .setUseDeveloperSupport(true)
            .setInitialLifecycleState(LifecycleState.RESUMED)
            .setUIImplementationProvider(new MyUIImplementationProvider())
            .build();
    return reactInstanceManager;

简单来说index.android.bundle就是对React Native里JS端代码的打包。
1) 一般release打包会放到assets目录下,然后它的资源文件也会放到assets目录下。通过设置BundleAssetName就可以利用AssetManager读取bundle,看到XX工程里的bundle接近1MB。这也是我们后面可以优化的点。

  • bundle包的拆分,按需加载,优化页面的加载时间,携程专门有篇文章是讲bundle的拆分的。
  • 提前加载bundle
  • 动态下发

2)通过设置JSBundleFile,可以读取本地的bundle,RN的热更新就可以通过这个下发,然后Native重新加载一下即可。
3)ReactCellPackage就是我们自定义的Module和组件。

7、加载React布局

1)继承ReactActiviy

public class MyReactActivity extends ReactActivity {

    @Override
    protected String getMainComponentName() {
        return "react-native-module";
    }
}

ReactActivity是FB为我们提供的基础类,内部会在根布局里设置成ReactRootView(实际上就是一个FrameLayout),并做一些RN的初始化工作。JS端的Component最终会被加载成为ReactRootView的子布局。

  1. 自定义ReactRootView加载
    ReactRootView可以设置成根布局或者页面的一个子View。startReactApplication方法首次就会异步做一些初始化工作,ReactActivity里也是调用这个方法。
public abstract class BaseActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler{

    private ReactRootView mReactRootView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mReactRootView = new ReactRootView(this);
        mReactRootView.startReactApplication(SSApplication.inst().getReactNativeHost().getReactInstanceManager(), getRNComponentName(), null);
        setContentView(mReactRootView);
    }

    protected abstract String getRNComponentName();


    @Override
    protected void onResume() {
        super.onResume();
        SSApplication.inst().getReactNativeHost().getReactInstanceManager().onHostResume(this, this);
    }

    @Override
    protected void onPause() {
        SSApplication.inst().getReactNativeHost().getReactInstanceManager().onHostPause();
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        SSApplication.inst().getReactNativeHost().getReactInstanceManager().onHostDestroy(this);
        SSApplication.inst().getReactNativeHost().getReactInstanceManager().detachRootView(mReactRootView);
        super.onDestroy();
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        SSApplication.inst().getReactNativeHost().getReactInstanceManager().onBackPressed();
    }

    @Override
    public void invokeDefaultOnBackPressed() {
        super.onBackPressed();
    }
}

8、自定义ReactPackage

public class ReactCellPackage implements ReactPackage {
    //需要注册的自定义的NativeModule列表
    @Override
    public List createNativeModules(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
    
    //JS Module 接口,需要在JS中做对等实现,并打包的bundle中。
    @Override
    public List> createJSModules() {
        return Collections.emptyList();
    }

    //自定义View的ViewManager列表
    @Override
    public List createViewManagers(ReactApplicationContext reactContext) {
        return Arrays.asList(new ReactCellViewManager()
                , new MyReactImageViewManager()
                , new MyReactTextViewManager()
                , new RealRecyclerItemViewManager()
                , new RealRecyclerViewManager());
    }
}

至此我们就可以开始玩React Native了。

React Native 入门实践_第1张图片

React Native 通信

React Native 入门实践_第2张图片
config

这张图比较省略,Java和JS之间还有一个Bridge(C/C++层),简单理解来说,Java和JS各自维护了一份对等的映射config,比如Java传入A就可以调用js config表里,A对应的模块。反之亦然。这个配置是在初始化React Native 上下文的时候生成的。

初始化流程

1)生成ReactApplicationContext实例.
2)初始化3个线程,UI线程,NativeModule线程,JSThread,以及 jsExecutor。
3)处理各类Package,解析并在Java侧维护了各个JavaScriptModule和NativeModule表

  1. initBridge,将NativeModule表下发给C/C++层
  2. runJSBundle 加载JSBundle

Java -> JS

可以这样理解,JavaScriptModule就是JS暴露给Java的调用接口。当Java调用JS方法的时候:

  1. 先找到对应的JS Module,并填充相应的参数。
    2)Proxy通过jni调用生成JS Module的实例
    3)jni调用 实例,方法,参数作为产生 下发的到Bridge层
    4)Bridge层 执行对应的 JS 方法

JS -> JAVA

  1. JS Module的方法调用时,会把module,methond,params放入到MessageQueue里
    2)当message的消息间隔大于5ms时,触发Queue的情况操作。
    3)Bridge层 通过JNI找到 对应的NativeModule
    4)NativeModule执行相应的method.

View(UIManagerModule)

JS端触发view刷新等都会走到UIManagerModule中,这里会将这些“消息”封装成一个一个Operation,由UIViewOperationQueue统一调度。这个就回到在UI Thread里了,后续处理的就是Native本地的渲染了。

痛点

性能

React Native相比于Native性能还是偏弱。包括渲染效率,加载延时,以及View的复用等等,大厂们纷纷都在对React Native 做特定场景的优化。比如是listview。目前我们也在做listview的优化。

稳定性

1、crash
2、国产手机的兼容性

AnyWay

React Native 确实为Native 开发提供一种思路,也许后面性能不再是瓶颈,那么未来是光明的。眼下,我们还是要多踩坑。

参考

React 源码剖析系列 - 不可思议的 react diff
React Native Android 从学车到补胎和成功发车经历
React Native Android 源码框架浅析
其实没那么复杂!探究react-native通信机制

你可能感兴趣的:(React Native 入门实践)