There is a classic problem in web development: "What from the URL input to the page rendering in the "
As far as I textual this issue at least, there years history. In the ever-changing front-end circle, this question can be asked all the time because it is a very good question, involving a lot of knowledge points, and usually doing some performance optimization, you can start from this problem and analyze the performance bottleneck. , And then optimize the remedy.
But today we will not talk about the performance optimization of the Web. We just use the analysis of the classic problem just now, from the startup of React Native to the completion of the first rendering of the page, combined with the source code of React Native and the new 1.0 architecture, one by one Analyze the startup performance optimization road of React Native .
If you like my article, I hope to like it 👍 Collection 📁 Comment 💬 Three consecutive support, thank you, this is really important to me!
Reading reminder :
1. The source code content in the article is RN 0.64 version
2. The content of source code analysis involvesObjective-C
,Java
,C++
,JavaScript
. I try to speak it in an easy-to-understand manner. If you really don’t understand, you can directly see the conclusion.
0.React Native startup process
React Native, as a web front-end friendly hybrid development framework, can be roughly divided into two parts at startup:
- Native container operation
- JavaScript code running
Among them, the Native container is started in the existing architecture (the version number is less than 1.0.0): it can be roughly divided into three parts:
- Native container initialization
- Native Modules full binding
- Initialization of JSEngine
After the container is initialized, the stage is handed over to JavaScript, and the process can be subdivided into 2 parts:
- Loading, parsing and execution of JavaScript code
- Construction of JS Component
Finally, JS Thread sends the calculated layout information to the Native side, calculates the Shadow Tree, and finally layout and rendering by UI Thread.
About rendering performance optimization section can see written before me "React Native Performance Tuning Guide" , I'm from rendering , picture , animation , long list and other direction describes common routine RN rendering optimization , Interested readers can check it out, I won’t introduce it here.
For the above steps, I drew a picture. Below I use this picture as a table of contents to introduce the optimization direction of each step from left to right:
Tip : React Native initialization time, there may be multiple tasks parallel execution , so the only figure shows a schematic flow React Native initialization, and does not correspond to the actual code execution timings.
1. Upgrade React Native
If you want to improve the performance of React Native applications, the most once and for all method is to to the large version of RN . After our app was upgraded from 0.59 to 0.62, our app did not do any performance optimization work, and the startup time was directly reduced by 1/2. When the new architecture of React Native is released, the startup speed and rendering speed will be greatly enhanced.
Of course, the RN version upgrade is not easy (across the iOS Android JS three ends, compatible with destructive updates), I wrote an article "React Native Upgrade Guide (0.59 -> 0.62)" , if there is an upgrade The old iron of ideas can read it for reference.
2. Native container initialization
Initialization container is certainly the beginning of the analysis from the entrance APP file, the following I will pick some critical code , comb the initialization process.
iOS source code analysis
1.AppDelegate.m
AppDelegate.m
is the entry file of iOS. The code is very streamlined. The main contents are as follows:
// AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 1.初始化一个 RCTBridge 实现加载 jsbundle 的方法
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
// 2.利用 RCTBridge 初始化一个 RCTRootView
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"RN64"
initialProperties:nil];
// 3.初始化 UIViewController
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
// 4.将 RCTRootView 赋值给 UIViewController 的 view
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
In general, looking at the entry file, three things are done:
- Initialize a
RCTBridge
method to load jsbundle - Use
RCTBridge
initialize aRCTRootView
- Assign
RCTRootView
toUIViewController
to implement UI mounting
From the entry source code, we can find that all the initialization work points to RCTRootView
, so let's take a look at what RCTRootView
did.
2.RCTRootView
Let's take a look at RCTRootView
, we only look at some of the methods we are concerned about:
// RCTRootView.h
@interface RCTRootView : UIView
// AppDelegate.m 中用到的初始化方法
- (instancetype)initWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initialProperties:(nullable NSDictionary *)initialProperties NS_DESIGNATED_INITIALIZER;
Seen from the header file:
RCTRootView
inherits fromUIView
, so it is essentially a UI component;RCTRootView
callsinitWithBridge
initialize, you must pass in an already initializedRCTBridge
In the RCTRootView.m
file, initWithBridge
will monitor a series of JS loading monitoring functions when it is initialized. After monitoring the loading of the JS Bundle file, it will call AppRegistry.runApplication()
in the JS to start the RN application.
Analysis here, we found RCTRootView.m
just realized the RCTBridge
of events listener, not initialize core , so we have to go to RCTBridge
this file up.
3.RCTBridge.m
RCTBridge.m
, the initial call path is a bit long, and the full paste source code is a bit longer. In short, the last call is (void)setUp
. The core code is as follows:
- (Class)bridgeClass
{
return [RCTCxxBridge class];
}
- (void)setUp {
// 获取bridgeClass 默认是 RCTCxxBridge
Class bridgeClass = self.bridgeClass;
// 初始化 RTCxxBridge
self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
// 启动 RTCxxBridge
[self.batchedBridge start];
}
We can see, RCTBridge
initialization and pointed RTCxxBridge
.
4.RTCxxBridge.mm
RTCxxBridge
can be said to be the core initialized by React Native. I checked some information and it seems that RTCxxBridge
used to RCTBatchedBridge
, so these two categories can be roughly regarded as the same thing.
Because the start
RCTBridge
is called in RTCxxBridge
, let's start
method to see what has been done.
// RTCxxBridge.mm
- (void)start {
// 1.初始化 JSThread,后续所有的 js 代码都在这个线程里面执行
_jsThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(runRunLoop) object:nil];
[_jsThread start];
// 创建并行队列
dispatch_group_t prepareBridge = dispatch_group_create();
// 2.注册所有的 native modules
[self registerExtraModules];
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
// 3.初始化 JSExecutorFactory 实例
std::shared_ptr<JSExecutorFactory> executorFactory;
// 4.初始化底层 Instance 实例,也就是 _reactInstance
dispatch_group_enter(prepareBridge);
[self ensureOnJavaScriptThread:^{
[weakSelf _initializeBridge:executorFactory];
dispatch_group_leave(prepareBridge);
}];
// 5.加载 js 代码
dispatch_group_enter(prepareBridge);
__block NSData *sourceCode;
[self
loadSource:^(NSError *error, RCTSource *source) {
if (error) {
[weakSelf handleError:error];
}
sourceCode = source.data;
dispatch_group_leave(prepareBridge);
}
onProgress:^(RCTLoadingProgress *progressData) {
}
];
// 6.等待 native moudle 和 JS 代码加载完毕后就执行 JS
dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
RCTCxxBridge *strongSelf = weakSelf;
if (sourceCode && strongSelf.loading) {
[strongSelf executeSourceCode:sourceCode sync:NO];
}
});
}
The above code is relatively long, and it uses GCD multi-thread . The process described in words is roughly as follows:
- Initialize js thread
_jsThread
native modules
on the main thread- Prepare the bridge and js runtime environment between
js
andNative
- Create a message queue
RCTMessageThread
on the JS thread, initialize_reactInstance
- Load the JS Bundle on the JS thread
- After all the above things are done, execute the JS code
In fact, the above six points can be dig deep, but the source code content involved in this section is enough. Interested readers can combine the reference materials I gave at the end and the React Native source code to dig deeper and explore.
Android source code analysis
1.MainActivity.java & MainApplication.java
Like iOS, we start with the entry file to analyze the startup process. Let’s look at MainActivity.java
first:
MainActivity
inherited from ReactActivity
, ReactActivity
inherited from AppCompatActivity
:
// MainActivity.java
public class MainActivity extends ReactActivity {
// 返回组件名,和 js 入口注册名字一致
@Override
protected String getMainComponentName() {
return "rn_performance_demo";
}
}
Let's start the analysis from the Android entry file MainApplication.java
// MainApplication.java
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost =
new ReactNativeHost(this) {
// 返回 app 需要的 ReactPackage,添加需要加载的模块,
// 这个地方就是我们在项目中添加依赖包时需要添加第三方 package 的地方
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
return packages;
}
// js bundle 入口文件,设置为 index.js
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
// SoLoader:加载C++底层库
SoLoader.init(this, /* native exopackage */ false);
}
}
ReactApplication
interface is very simple, requiring us to create a ReactNativeHost
object:
public interface ReactApplication {
ReactNativeHost getReactNativeHost();
}
From the above analysis, we can see that everything points to the ReactNativeHost
, let's take a look at it below.
2.ReactNativeHost.java
ReactNativeHost
main job of ReactInstanceManager
is to create 06076b623b584a:
public abstract class ReactNativeHost {
protected ReactInstanceManager createReactInstanceManager() {
ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_START);
ReactInstanceManagerBuilder builder =
ReactInstanceManager.builder()
// 应用上下文
.setApplication(mApplication)
// JSMainModulePath 相当于应用首页的 js Bundle,可以传递 url 从服务器拉取 js Bundle
// 当然这个只在 dev 模式下可以使用
.setJSMainModulePath(getJSMainModuleName())
// 是否开启 dev 模式
.setUseDeveloperSupport(getUseDeveloperSupport())
// 红盒的回调
.setRedBoxHandler(getRedBoxHandler())
.setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
.setUIImplementationProvider(getUIImplementationProvider())
.setJSIModulesPackage(getJSIModulePackage())
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE);
// 添加 ReactPackage
for (ReactPackage reactPackage : getPackages()) {
builder.addPackage(reactPackage);
}
// 获取 js Bundle 的加载路径
String jsBundleFile = getJSBundleFile();
if (jsBundleFile != null) {
builder.setJSBundleFile(jsBundleFile);
} else {
builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
}
ReactInstanceManager reactInstanceManager = builder.build();
return reactInstanceManager;
}
}
3.ReactActivityDelegate.java
Let's go back to ReactActivity
. It doesn't do anything on its own. All functions are ReactActivityDelegate
, so we directly look at ReactActivityDelegate
is implemented:
public class ReactActivityDelegate {
protected void onCreate(Bundle savedInstanceState) {
String mainComponentName = getMainComponentName();
mReactDelegate =
new ReactDelegate(
getPlainActivity(), getReactNativeHost(), mainComponentName, getLaunchOptions()) {
@Override
protected ReactRootView createRootView() {
return ReactActivityDelegate.this.createRootView();
}
};
if (mMainComponentName != null) {
// 载入 app 页面
loadApp(mainComponentName);
}
}
protected void loadApp(String appKey) {
mReactDelegate.loadApp(appKey);
// Activity 的 setContentView() 方法
getPlainActivity().setContentView(mReactDelegate.getReactRootView());
}
}
onCreate()
when they instantiates a ReactDelegate
, we look at its implementation.
4.ReactDelegate.java
In ReactDelegate.java
, I did not see it do two things:
- Create
ReactRootView
as the root view - Call
getReactNativeHost().getReactInstanceManager()
start the RN application
public class ReactDelegate {
public void loadApp(String appKey) {
if (mReactRootView != null) {
throw new IllegalStateException("Cannot loadApp while app is already running.");
}
// 创建 ReactRootView 作为根视图
mReactRootView = createRootView();
// 启动 RN 应用
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(), appKey, mLaunchOptions);
}
}
Basic startup process The source code content involved in this section is enough. Interested readers can combine the reference materials I gave at the end and dig deeper and explore the React Native source code.
Optimization suggestion
For applications with React Native as the mainstay, the RN container must be initialized immediately after the APP is started. Basically, there is no optimization idea; but the Native-based hybrid development APP has tricks:
Since initialization takes the longest time, why not initialize in advance before we officially enter the React Native container?
This method is very common, because many H5 containers also do this. Before entering the WebView webpage formally, first make a WebView container pool, initialize the WebView in advance, and directly load the data rendering after entering the H5 container to achieve the effect that the webpage opens in seconds.
RN container pool looks very mysterious, in fact it is a Map
, key
is componentName
(that is, AppRegistry.registerComponent(appName, Component)
passed in appName
), value
is an already instantiated RCTRootView/ReactRootView
.
After the APP is started, find a trigger time to initialize in advance, read the container pool before entering the RN container, if there is a matching container, just use it directly, and reinitialize if there is no match.
To write two very simple cases, iOS can build an RN container pool as shown in the figure below:
@property (nonatomic, strong) NSMutableDictionary<NSString *, RCTRootView *> *rootViewRool;
// 容器池
-(NSMutableDictionary<NSString *, RCTRootView *> *)rootViewRool {
if (!_rootViewRool) {
_rootViewRool = @{}.mutableCopy;
}
return _rootViewRool;
}
// 缓存 RCTRootView
-(void)cacheRootView:(NSString *)componentName path:(NSString *)path props:(NSDictionary *)props bridge:(RCTBridge *)bridge {
// 初始化
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:componentName
initialProperties:props];
// 实例化后要加载到屏幕的最下面,否则不能触发视图渲染
[[UIApplication sharedApplication].keyWindow.rootViewController.view insertSubview:rootView atIndex:0];
rootView.frame = [UIScreen mainScreen].bounds;
// 把缓存好的 RCTRootView 放到容器池中
NSString *key = [NSString stringWithFormat:@"%@_%@", componentName, path];
self.rootViewRool[key] = rootView;
}
// 读取容器
-(RCTRootView *)getRootView:(NSString *)componentName path:(NSString *)path props:(NSDictionary *)props bridge:(RCTBridge *)bridge {
NSString *key = [NSString stringWithFormat:@"%@_%@", componentName, path];
RCTRootView *rootView = self.rootViewRool[key];
if (rootView) {
return rootView;
}
// 兜底逻辑
return [[RCTRootView alloc] initWithBridge:bridge moduleName:componentName initialProperties:props];
}
Android builds the RN container pool as follows:
private HashMap<String, ReactRootView> rootViewPool = new HashMap<>();
// 创建容器
private ReactRootView createRootView(String componentName, String path, Bundle props, Context context) {
ReactInstanceManager bridgeInstance = ((ReactApplication) application).getReactNativeHost().getReactInstanceManager();
ReactRootView rootView = new ReactRootView(context);
if(props == null) {
props = new Bundle();
}
props.putString("path", path);
rootView.startReactApplication(bridgeInstance, componentName, props);
return rootView;
}
// 缓存容器
public void cahceRootView(String componentName, String path, Bundle props, Context context) {
ReactRootView rootView = createRootView(componentName, path, props, context);
String key = componentName + "_" + path;
// 把缓存好的 RCTRootView 放到容器池中
rootViewPool.put(key, rootView);
}
// 读取容器
public ReactRootView getRootView(String componentName, String path, Bundle props, Context context) {
String key = componentName + "_" + path;
ReactRootView rootView = rootViewPool.get(key);
if (rootView != null) {
rootView.setAppProperties(newProps);
rootViewPool.remove(key);
return rootView;
}
// 兜底逻辑
return createRootView(componentName, path, props, context);
}
Of course, because RCTRootView/ReactRootView
takes up a certain amount of memory each time, when to instantiate, instantiate several containers, the size limit of the pool, and when to clear the container, it is necessary to practice and explore in combination with the business.
3.Native Modules binding
iOS source code analysis
The Native Modules of iOS has 3 pieces of content, the _initializeModules
function 06076b623b5b0c in the middle:
// RCTCxxBridge.mm
- (void)start {
// 初始化 RCTBridge 时调用 initWithBundleURL_moduleProvider_launchOptions 中的 moduleProvider 返回的 native modules
[self registerExtraModules];
// 注册所有的自定义 Native Module
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
// 初始化所有懒加载的 native module,只有用 Chrome debug 时才会调用
[self registerExtraLazyModules];
}
Let's see what the _initializeModules
function does:
// RCTCxxBridge.mm
- (NSArray<RCTModuleData *> *)_initializeModules:(NSArray<Class> *)modules
withDispatchGroup:(dispatch_group_t)dispatchGroup
lazilyDiscovered:(BOOL)lazilyDiscovered
{
for (RCTModuleData *moduleData in _moduleDataByID) {
if (moduleData.hasInstance && (!moduleData.requiresMainQueueSetup || RCTIsMainQueue())) {
// Modules that were pre-initialized should ideally be set up before
// bridge init has finished, otherwise the caller may try to access the
// module directly rather than via `[bridge moduleForClass:]`, which won't
// trigger the lazy initialization process. If the module cannot safely be
// set up on the current thread, it will instead be async dispatched
// to the main thread to be set up in _prepareModulesWithDispatchGroup:.
(void)[moduleData instance];
}
}
_moduleSetupComplete = YES;
[self _prepareModulesWithDispatchGroup:dispatchGroup];
}
According to _initializeModules
and _prepareModulesWithDispatchGroup
, it can be seen that iOS is in the process of loading JS Bundle (in JSThead thread ), and at the same time in main thread initializes all Native Modules.
Combined with the previous source code analysis, we can see that when the React Native iOS container is initialized, will initialize all Native Modules . If Native Modules
more 06076b623b5b9d, it will affect the startup time of the Android RN container.
Android source code analysis
Regarding the registration of Native Modules, clues have been given MainApplication.java
// MainApplication.java
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
return packages;
}
Since React Native has enabled auto link
after 0.60, the installed third-party Native Modules are all in PackageList
, so we can get the modules of auto link
getPackages()
Source, in the ReactInstanceManager.java
this file, run createReactContext()
create ReactContext
, and there are step is to register nativeModules
registry:
// ReactInstanceManager.java
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
// 注册 nativeModules 注册表
NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);
}
According to the function call, we trace to processPackages()
, and use a for loop to add all the Native Modules in mPackages to the registry:
// ReactInstanceManager.java
private NativeModuleRegistry processPackages(
ReactApplicationContext reactContext,
List<ReactPackage> packages,
boolean checkAndUpdatePackageMembership) {
// 创建 JavaModule 注册表 Builder,用来创建 JavaModule 注册表,
// JavaModule 注册表将所有的 JavaModule 注册到 CatalystInstance 中
NativeModuleRegistryBuilder nativeModuleRegistryBuilder =
new NativeModuleRegistryBuilder(reactContext, this);
// 给 mPackages 加锁
// mPackages 类型为 List<ReactPackage>,与 MainApplication.java 里的 packages 对应
synchronized (mPackages) {
for (ReactPackage reactPackage : packages) {
try {
// 循环处理我们在 Application 里注入的 ReactPackage,处理的过程就是把各自的 Module 添加到对应的注册表中
processPackage(reactPackage, nativeModuleRegistryBuilder);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
}
NativeModuleRegistry nativeModuleRegistry;
try {
// 生成 Java Module 注册表
nativeModuleRegistry = nativeModuleRegistryBuilder.build();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
}
return nativeModuleRegistry;
}
Finally, call processPackage()
for real registration:
// ReactInstanceManager.java
private void processPackage(
ReactPackage reactPackage,
NativeModuleRegistryBuilder nativeModuleRegistryBuilder
) {
nativeModuleRegistryBuilder.processPackage(reactPackage);
}
It can be seen from the above process that when Android registers Native Modules
, is registered with in full synchronization. If Native Modules
more 06076b623b5cd8, it will affect the startup time of the Android RN container.
Optimization suggestion
To be honest, Native Modules are completely bound in the existing architecture and there is no solution. : Regardless of whether you use this Native Methods or not, initialize all of them when the container starts. In the new RN architecture, TurboModules will solve this problem (described in the next section of this article).
If you have to talk about optimization, there is actually another idea. Don’t you initialize it at all, can I reduce the number of Native Modules? There is a step in the new architecture called Lean Core , which is to streamline the React Native core and remove some functions/components from the main RN project (for example, the WebView
component), hand it over to the community for maintenance, and download it separately when you want to use it. integrated.
The main advantages of this are as follows:
- The core is more streamlined, and RN maintainers have more energy to maintain the main functions
- Reduce the binding time of Native Modules and redundant JS loading time, reduce the package size, and be more friendly to the initialization performance (we upgraded the RN version to 0.62 and doubled the initialization speed, which is basically due to Lean Core)
- Speed up the iteration speed, optimize the development experience, etc.
Now Lean Core has been basically completed. For more discussion, official issues forum . As long as we upgrade the React Native version simultaneously, we can enjoy the results of Lean Core
4. How the new RN architecture optimizes startup performance
The new React Native architecture has been bounced for nearly two years. Every time I ask about the progress, the official reply is "Don't rush, don't rush, do it, do it".
I personally looked forward to a whole year last year, but I didn't wait for anything, so when RN will be updated to version 1.0.0, I don't care anymore. Although the official RN has always been a pigeon, I have to say that there is something about their new architecture. I have basically read the articles and videos about the new architecture of RN on the market, so I still have an overall understanding of the new architecture. .
Because the new architecture has not been officially released, there must be some differences in the specific details. The specific implementation details still have to wait for the official React Native.
JSI
The full name of JSI is JavaScript Interface , a framework written in C++, and its function is that supports JS to directly call Native method instead of asynchronous communication through Bridge.
How does JS directly call Native? Let's take the simplest example. setTimeout
document.getElementById
on the browser, the Native Code is actually called directly on the JS side. We can verify it in the browser console:
For example, I executed a command:
let el = document.createElement('div')
The variable el
holds not a JS object, but an object instantiated in C++. For the object held by el, we set the relevant properties:
el.setAttribute('width', 100)
At this time, it is actually JS synchronously calling setWidth
C++ to change the width of this element.
The JSI in React Native's new architecture mainly plays this role. With JSI, we can use JS to directly obtain references to C++ objects ( Host Objects ), and then directly control the UI, directly call the Native Modules method, and save the bridge The overhead of asynchronous communication.
Let's take a small example to see how Java/OC uses JSI to expose synchronous call methods to JS.
#pragma once
#include <string>
#include <unordered_map>
#include <jsi/jsi.h>
// SampleJSIObject 继承自 HostObject,表示这个一个暴露给 JS 的对象
// 对于 JS 来说,JS 可以直接同步调用这个对象上的属性和方法
class JSI_EXPORT SampleJSIObject : public facebook::jsi::HostObject {
public:
// 第一步
// 将 window.__SampleJSIObject 暴露给JavaScript
// 这是一个静态函数,一般在应用初始化时从 ObjC/Java 中调用
static void SampleJSIObject::install(jsi::Runtime &runtime) {
runtime.global().setProperty(
runtime,
"__sampleJSIObject",
jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "__SampleJSIObject"),
1,
[binding](jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count) {
// 返回调用 window.__SampleJSIObject 时得到的内容
return std::make_shared<SampleJSIObject>();
}));
}
// 类似于 getter,每次 JS 访问这个对象的时候,都要经过这个方法,作用类似于一个包装器
// 比如说我们调用 window.__sampleJSIObject.method1(),这个方法就会被调用
jsi::Value TurboModule::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) {
// 调用方法名
// 比如说调用 window.__sampleJSIObject.method1() 时,propNameUtf8 就是 method1
std::string propNameUtf8 = propName.utf8(runtime);
return jsi::Function::createFromHostFunction(
runtime,
propName,
argCount,
[](facebook::jsi::Runtime &rt, const facebook::jsi::Value &thisVal, const facebook::jsi::Value *args, size_t count) {
if (propNameUtf8 == 'method1') {
// 调用 method1 时,相关的函数处理逻辑
}
});
}
std::vector<PropNameID> getPropertyNames(Runtime& rt){
}
}
The above example is relatively short. If you want to learn more about JSI, you can read the "React Native JSI Challenge" or read the source code directly.
TurboModules
Through the previous source code analysis, we can know that in the existing architecture, will fully load the native modules when Native is initialized. With the iteration of the business, there will only be more and more native modules, and the time-consuming here will be more and more. long.
TurboModules can solve this problem all at once. In the new architecture, the native modules are lazily loaded , which means that the loading will be initialized only when you call the corresponding native modules. This solves the problem of long initial full loading.
The calling path of TurboModules is roughly like this:
- First use JSI to create a top-level "Native Modules Proxy", call it
global.__turboModuleProxy
- To access a Native Modules, for example, to access
SampleTurboModule
, we first executerequire('NativeSampleTurboModule')
- In the NativeSampleTurboModule.js file, we first call
TurboModuleRegistry.getEnforcing()
and thenglobal.__turboModuleProxy("SampleTurboModule")
- When
global.__turboModuleProxy
is called, the Native method exposed by JSI in the first step is called. At this time, the C++ layer finds the implementation of ObjC/Java through the incoming string "SampleTurboModule", and finally returns a corresponding JSI object - Now that we get the
SampleTurboModule
, we can use JavaScript to synchronously call the properties and methods on the JSI object
Through the above steps, we can see that with TurboModules, Native Modules will only be loaded when calls fully loads Native Modules when the React Native container is initialized; at the same time, we can use JSI to achieve Synchronous calls between JS and Native are less time-consuming and more efficient.
to sum up
This article from Native point of view, the analysis React Native from source existing infrastructure start the process, summed up the performance optimization point of several Native layer; and finally a brief introduction about React Native of new architecture . In the next article, I will explain how to start with JavaScript optimize the startup speed of React Native.
If you like my article, I hope to like it 👍 Collection 📁 Comment 💬 Three consecutive support, thank you, this is really important to me!
Welcome everyone to pay attention to my WeChat public account: Halogen Lab, currently focusing on front-end technology, and also doing some small research on graphics.
Original link 👉 ⚡️ React Native startup speed optimization-Native article (including source code analysis) : Updates are more timely, and the reading experience is better
reference
React Native Performance Optimization Guide
React Native Upgrade Guide (0.59 -> 0.62)
Chain React 2019 - Ram Narasimhan - Performance in React Native
React Native's new architecture - Glossary of terms
ReactNative and iOS native communication principle analysis-initialization
React Native iOS source code analysis
ReactNative source code articles:
How to use the React Native preloading solution to solve the white screen problem
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。