接着上文,原生模块sync之后如果没有报错信息,那么我们就可以进入下一步了。我们还是分两大部门来介绍;rn端和原生端。
npx react-native init AwesomeProject
现在,我们来对它添加一些文件,以完善rn端执行js/ts代码所需要的功能。
我们需要添加两个文件babel.config.js和metro.config.js。首先,babel.config.js是babel转换器的相关配置,因为es2015使用了一些高级语法,babel则是将这些高级语法转换成浏览器引擎能够识别的语法;metro.config.js则是metro打包相关的配置信息。笔者对这些配置信息也没有深入的了解太多,如果只限于搭建rn环境这个需求,大家如下配置即可。
//在babel.config.js文件中copy下面这段代码
module.exports = {
presets: ['module:metro-react-native-babel-preset']
};
//在metro.config.js文件中copy下面这段代码
module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
};
细心的同学一定发现了上图中有一个tsconfig.json文件。这个文件是配置相关ts的信息的。因为rn端的代码执行的是js代码,而js代码是动态语言,它最令人恶心的一点是:它是不会报错的。因此,如果用来开发大型项目分分钟搞死开发者,笔者使用的是ts来代替js。因此,需要添加tsconfig.json文件。
//tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"isolatedModules": true,
"jsx": "react-native",
"module": "commonjs",
"lib": ["es6"],
"moduleResolution": "node",
"noEmit": true,
"strict": true,
"target": "esnext",
"baseUrl": ".",
"paths": {
"*": ["src/*"],
"tests": ["tests/*"],
"@components/*": ["src/components/*"],
}
},
"exclude": [
"node_modules",
"babel.config.js",
"metro.config.js",
"jest.config.js"
]
}
具体细节,感兴趣的同学可以查阅相关的资料,我们这里就不介绍了。
接着,我们还需要更改的一个文件就是package.json,我们之前刚初始化成功时生成的内容如下所示:
{
"name": "AwesomeProject",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start"
},
"dependencies": {
"react-native": "0.70.3"
}
}
更改后:
{
"name": "AwesomeProject",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "yarn react-native start",
"android": "npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/main.bundle --assets-dest android/app/src/main/res/"
},
//我们这里改下react和react-native的版本,因为0.70.3版本使用的是新架构,笔者暂时还不熟悉,所以我们用旧架构来讲解,记住,react和react-native都需要
"dependencies": {
"react": "16.9.0",
"react-native": "^0.63.4",
},
//devDependencies里面的内容,大家copy即可,有些依赖例如jest,项目中没有用到的可以移除,当然你添加进去不用它也没有问题
"devDependencies": {
"@types/jest": "^27.0.1",
"@types/react": "^17.0.21",
"@types/react-native": "^0.65.0",
"@types/react-test-renderer": "^17.0.1",
"babel-plugin-module-resolver": "^4.1.0",
"typescript": "^4.4.3",
"webpack": "^5.53.0",
"webpack-cli": "^4.8.0"
}
}
dependencies和devDependencies这两个东西到底是做什么的呢?其实这里的东西就有点像是Android原生gradle中的dependencies闭包,如果我们在原生中想要引入某个依赖一般会如下添加:
implementation 'com.google.android.material:material:1.4.0'
当我们在命令行终端执行yarn命令时,node就会下载dependencies和devDependencies所指定的依赖到node_modules文件夹下面。
这里有一个小提醒:每次我们进行开发时,最好删掉我们本地的node_modules文件夹,然后重新yarn一下,以确保我们本地的依赖与dependencies和devDependencies块所指定的依赖时一致的。
官网上是使用npm来下载依赖的,个人不建议使用npm,主要原因就是太慢了,所以大家尽量使用yarn吧!而且有趣的一点是,我们使用npx命令初始化一个rn项目的时候,其实也是用yarn来下载相关的依赖的,因为初始化的工程有一个yarn.lock文件,这就是最好的见证。
我们改动最大的就是scripts块里面的东西
"scripts": {
"start": "yarn react-native start",
"android": "npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/main.bundle --assets-dest android/app/src/main/res/"
},
这样,我们就完成了rn端环境的搭建了。现在,我们来写下rn页面的代码来测试下。首先,我们新建一个src文件夹,用来存放我们缩写的rn代码。如下所示:
然后,我们在src建立一个HelloWorld.tsx文件。
import React from "react";
import {StyleSheet, Text, View} from "react-native";
const styles = StyleSheet.create({
container: {
// flex: 1,
justifyContent: 'center',
alignItems:'center'
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10
}
});
export class HelloWorld extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>{"setData"}</Text>
</View>
);
}
}
然后,在index.js文件中添加如下代码
import React from 'react';
import {AppRegistry,} from 'react-native';
import {HelloWorld} from "./src/view/HelloWorld";
//如果原生端想要引用rn端的控件,则必须要使用registerComponent方法注册,如果单纯只是给rn端使用,那么就不必使用registerComponent方法注册。
//例如:我们原生想要调用HelloWorld这个rn组件,那么原生就必须通过“MyReactNativeApp”这个键找到HelloWorld这个控件,如果HelloWorld只是被rn端的其他ts文件引用,那么就不必在这里注册了
AppRegistry.registerComponent(
'MyReactNativeApp',//这个名字可以随便定义,但是必须是字符串
() => HelloWorld
);
这样,我们rn端的第一个页面就写好了。
package com.example.demo;
import android.app.Application;
import android.content.Context;
import com.facebook.soloader.SoLoader;
/**
* Created by Brett.li on 2021/9/21.
*/
public class MyReactApplication extends Application implements ReactApplication {
@Override
public ReactNativeHost getReactNativeHost() {
return new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return new PackageList(this).getPackages();
}
@Nullable
@Override
protected String getBundleAssetName() {
//就是我们打包出来的bundle的名字,不能写错,不然就加载不到bundle
return "main.bundle";//bundle的名字,默认是index.android.bundle
}
@Override
protected String getJSMainModuleName() {
//即打包脚本中--entry-file后面的参数名。不能写错
return "index";
}
};
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
//application中需要做两件事
//1.实现getReactNativeHost接口
//2.添加SoLoader.init(this, /* native exopackage */ false);这句代码
接着,我们在activity做如下修改:
public class MainActivity extends AppCompatActivity {
private BaseRNActivityDelegate mDelegate;
protected BaseRNActivityDelegate createReactActivityDelegate() {
return new BaseRNActivityDelegate(this, "MyReactNativeApp");//表示,我们要加载MyReactNativeApp所对应的rn控件
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDelegate = createReactActivityDelegate();
mDelegate.onCreate( savedInstanceState);
}
//ReactActivityDelegate其实就是个代理,这个类里面帮助我们做了大量的工作,我们,只要在其构造方法中传入index.js中注册的控件的键名即可调用对应的控件。至于为什么要继承ReactActivityDelegate类,是因为该类里面很多方法都是protected的,我们根本不能直接调用
class BaseRNActivityDelegate extends ReactActivityDelegate{
public BaseRNActivityDelegate(Activity activity, @Nullable String mainComponentName) {
super(activity, mainComponentName);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
}
这样,我们运行程序,MainActivity页面就能够加载到HelloWorld.tsx文件里面的内容了。
记得要修改androidManifest.xml文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.demo">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".MyReactApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Demo"
android:usesCleartextTraffic="true">
<activity android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>
好了,到这里我们已经基本搭建好了rn的环境的。后面,笔者会继续讲解如何封装原生的代码的。