1

我使用 RN 0.66.3 并希望在我的 iOS React Native 项目中直接同步调用从 javascript 到本机代码以共享数据,而不使用 React Native Bridge 来提高性能,因此我需要一个共享的全局对象并访问它来自javascript的属性和方法。

我知道 JSI(JavaScript 接口)可以做到这一点,但是没有文档和很少的教程,那么实现这个的简单步骤或示例代码是什么?

4

1 回答 1

1

要通过 React Native JSI 将您的对象公开给 javascript,您应该执行以下步骤:

  1. 让你的 c++ 类继承自HostObject
  2. 重写getset方法来实现对其属性和方法的访问。
  3. 在 React Native 运行时全局安装你的对象。

查看可以在应用程序启动期间NativeStorage持久存储键/值对的示例:NSUserDefaults

NativeStorage 类

#include <jsi/jsi.h>
#import <React/RCTBridge+Private.h>

using namespace facebook::jsi;
using namespace std;

// Store key-value pairs persistently across launches of your app.
class NativeStorage : public HostObject {
public:
  /// Stored property
  int expirationTime = 60 * 60 * 24; // 1 day
  
  // Helper function
  static NSString* stringValue(Runtime &runtime, const Value &value) {
    return value.isString()
      ? [NSString stringWithUTF8String:value.getString(runtime).utf8(runtime).c_str()]
      : nil;
  }
  
  Value get(Runtime &runtime, const PropNameID &name) override {
    auto methodName = name.utf8(runtime);
    
    // `expirationTime` property getter
    if (methodName == "expirationTime") {
      return this->expirationTime;
    }
    // `setObject` method
    else if (methodName == "setObject") {
      return Function::createFromHostFunction(runtime, PropNameID::forAscii(runtime, "setObject"), 2,
                                                        [](Runtime &runtime, const Value &thisValue,const Value *arguments, size_t count) -> Value {
        NSString* key = stringValue(runtime, arguments[0]);
        NSString* value = stringValue(runtime, arguments[1]);
        if (key.length && value.length) {
          [NSUserDefaults.standardUserDefaults setObject:value forKey:key];
          return true;
        }
        return false;
      });
    }
    // `object` method
    else if (methodName == "object") {
      return Function::createFromHostFunction(runtime, PropNameID::forAscii(runtime, "object"), 1,
                                                        [](Runtime &runtime, const Value &thisValue,const Value *arguments, size_t count) -> Value {
        NSString* key = stringValue(runtime, arguments[0]);
        NSString* value = [NSUserDefaults.standardUserDefaults stringForKey:key];
        return value.length
          ? Value(runtime, String::createFromUtf8(runtime, value.UTF8String))
          : Value::undefined();
      });
    }
    return Value::undefined();
  }
  
  void set(Runtime& runtime, const PropNameID& name, const Value& value) override {
    auto methodName = name.utf8(runtime);
    
    // ExpirationTime property setter
    if (methodName == "expirationTime") {
      if (value.isNumber()) {
        this->expirationTime = value.asNumber();
      }
    }
  }
  
  // Install `nativeStorage` globally to the runtime
  static void install(Runtime& runtime) {
    NativeStorage nativeStorage;
    shared_ptr<NativeStorage> binding = make_shared<NativeStorage>(move(nativeStorage));
    auto object = Object::createFromHostObject(runtime, binding);

    runtime.global().setProperty(runtime, "nativeStorage", object);
  }
};

AppDelegate.mm

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  ...
  
  // Runtime notification
  [NSNotificationCenter.defaultCenter addObserverForName:RCTJavaScriptDidLoadNotification object:nil queue:nil
                                              usingBlock:^(NSNotification* notification) {
    RCTCxxBridge* cxxbridge = (RCTCxxBridge*)notification.userInfo[@"bridge"];
    if (cxxbridge.runtime) {
      NativeStorage::install(*(Runtime*)cxxbridge.runtime);
    }
  }];
  
  return YES;
}

应用程序.js

nativeStorage.expirationTime = 1000;
console.log(nativeStorage.expirationTime);

const key = "greeting";
nativeStorage.setObject(key, "Hello JSI!");
const text = nativeStorage.object(key);
console.log(text);

输出:

1000
Hello JSI!

Future React Native 的 TurboModules 和 CodeGen 使这一切变得更干净、更容易,但它是原生模块的低级 JSI 实现,可以直接从 JavaScript 调用,而无需通过 React Native Bridge。

注意:由于示例使用 JSI 进行同步本地方法访问,远程调试(例如使用 Chrome)不再可能。相反,您应该使用 Flipper 来调试您的 JS 代码。

于 2022-01-26T11:16:46.923 回答