进阶 reactnative.dev 2026-10-11 06:04:21 · 33 阅读
第39章 React Native Fabric 原生组件开发:iOS 篇
版本:0.77Fabric Native Components:iOS接下来我们要编写一些 iOS 平台代码来渲染 web 视图。需要完成的步骤有:
运行 Codegen。
编写 RCTWebView 代码
在应用中注册 RCTWebView
1. 运行 Codegen
你可以手动运行 Codegen,但更简单的做法是借助你用来演示组件的应用自动完成。
cd ios
bundle install
bundle exec pod install
重要的是,你会在日志输出中看到 Codegen 的信息,之后我们会在 Xcode 里基于它构建 WebView 原生组件。 warning注意不要把生成的代码提交到仓库里。生成的代码与特定版本的 React Native 绑定,建议通过 npm peerDependencies 来限制与 React Native 版本的兼容性。 3. 编写 RCTWebView 我们需要用 Xcode 完成 5 个步骤来准备 iOS 项目: 打开 CocoaPods 生成的 Xcode Workspace: cd ios
open Demo.xcworkspace
右键点击 app,选择 New Group,将新分组命名为 WebView。 在 WebView 分组中,选择 New→File from Template。 使用 Objective-C File 模板,命名为 RCTWebView 重复第 4 步,创建一个名为 RCTWebView.h 的头文件。 将 RCTWebView.m 重命名为 RCTWebView.mm,使其成为 Objective-C++ 文件。 Demo/iosPodfile
...
Demo
├── AppDelegate.swift
...
├── RCTWebView.h
└── RCTWebView.mm
创建好头文件和实现文件后,就可以开始编写具体实现了。 下面是 RCTWebView.h 文件的代码,用于声明组件接口。 Demo/RCTWebView/RCTWebView.h#import
#import
NS_ASSUME_NONNULL_BEGIN
@interface RCTWebView : RCTViewComponentView
// 你想从视图中访问的原生方法在这里声明
@end
NS_ASSUME_NONNULL_END
这个类定义了 RCTWebView,它继承自 RCTViewComponentView。这是所有原生组件的基类,由 React Native 提供。 实现文件(RCTWebView.mm)的代码如下: Demo/RCTWebView/RCTWebView.mm#import "RCTWebView.h"
#import
#import
#import
#import
#import
using namespace facebook::react;
@interface RCTWebView ()
@end
@implementation RCTWebView {
NSURL * _sourceURL;
WKWebView * _webView;
}
-(instancetype)init
{
if(self = [super init]) {
_webView = [WKWebView new];
_webView.navigationDelegate = self;
[self addSubview:_webView];
}
return self;
}
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &oldViewProps = *std::static_pointer_cast(_props);
const auto &newViewProps = *std::static_pointer_cast(props);
// Handle your props here
if (oldViewProps.sourceURL != newViewProps.sourceURL) {
NSString *urlString = [NSString stringWithCString:newViewProps.sourceURL.c_str() encoding:NSUTF8StringEncoding];
_sourceURL = [NSURL URLWithString:urlString];
if ([self urlIsValid:newViewProps.sourceURL]) {
[_webView loadRequest:[NSURLRequest requestWithURL:_sourceURL]];
}
}
[super updateProps:props oldProps:oldProps];
}
-(void)layoutSubviews
{
[super layoutSubviews];
_webView.frame = self.bounds;
}
#pragma mark - WKNavigationDelegate
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
CustomWebViewEventEmitter::OnScriptLoaded result = CustomWebViewEventEmitter::OnScriptLoaded{CustomWebViewEventEmitter::OnScriptLoadedResult::Success};
self.eventEmitter.onScriptLoaded(result);
}
- (BOOL)urlIsValid:(std::string)propString
{
if (propString.length() > 0 && !_sourceURL) {
CustomWebViewEventEmitter::OnScriptLoaded result = CustomWebViewEventEmitter::OnScriptLoaded{CustomWebViewEventEmitter::OnScriptLoadedResult::Error};
self.eventEmitter.onScriptLoaded(result);
return NO;
}
return YES;
}
// Event emitter convenience method
- (const CustomWebViewEventEmitter &)eventEmitter
{
return static_cast(*_eventEmitter);
}
+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider();
}
@end
这段代码使用 Objective-C++ 编写,包含以下关键细节: @interface 实现了两个协议: * RCTCustomWebViewViewProtocol,由 Codegen 生成; * WKNavigationDelegate,由 WebKit 框架提供,用于处理 web 视图的导航事件; * init 方法:实例化 WKWebView,将其添加为子视图,并设置 navigationDelegate; * updateProps 方法:当组件的 props 发生变化时,由 React Native 调用; * layoutSubviews 方法:描述自定义视图的布局方式; * webView:didFinishNavigation: 方法:处理 WKWebView 完成页面加载后的操作; * urlIsValid:(std::string)propString 方法:检查作为 prop 传入的 URL 是否有效; * eventEmitter 方法:用于获取强类型 eventEmitter 实例的工具方法; * componentDescriptorProvider:返回由 Codegen 生成的 ComponentDescriptor。 添加 WebKit 框架 注意,这一步仅在创建 Web 视图时需要。iOS 上的 Web 组件需要链接 Apple 提供的 WebKit 框架。如果你的组件不需要访问 web 相关功能,可以跳过此步。 web 视图需要使用 Apple 通过 Xcode 及设备自带框架提供的一项功能:WebKit。 你可以在原生代码中看到 RCTWebView.mm 里添加的 `#import` 这一行。
要在应用中链接 WebKit 框架,请按以下步骤操作:
1. 在 Xcode 中,点击你的项目
2. 选择 app target
3. 选择 General 标签页
4. 向下滚动直到找到 "Frameworks, Libraries, and Embedded Contents" 部分,然后点击 + 按钮
1. 在搜索栏中过滤 WebKit
2. 选择 WebKit 框架
3. 点击 Add
bundle install
bundle exec pod install
重要的是,你会在日志输出中看到 Codegen 的信息,之后我们会在 Xcode 里基于它构建 WebView 原生组件。 warning注意不要把生成的代码提交到仓库里。生成的代码与特定版本的 React Native 绑定,建议通过 npm peerDependencies 来限制与 React Native 版本的兼容性。 3. 编写 RCTWebView 我们需要用 Xcode 完成 5 个步骤来准备 iOS 项目: 打开 CocoaPods 生成的 Xcode Workspace: cd ios
open Demo.xcworkspace
右键点击 app,选择 New Group,将新分组命名为 WebView。 在 WebView 分组中,选择 New→File from Template。 使用 Objective-C File 模板,命名为 RCTWebView 重复第 4 步,创建一个名为 RCTWebView.h 的头文件。 将 RCTWebView.m 重命名为 RCTWebView.mm,使其成为 Objective-C++ 文件。 Demo/iosPodfile
...
Demo
├── AppDelegate.swift
...
├── RCTWebView.h
└── RCTWebView.mm
创建好头文件和实现文件后,就可以开始编写具体实现了。 下面是 RCTWebView.h 文件的代码,用于声明组件接口。 Demo/RCTWebView/RCTWebView.h#import
#import
NS_ASSUME_NONNULL_BEGIN
@interface RCTWebView : RCTViewComponentView
// 你想从视图中访问的原生方法在这里声明
@end
NS_ASSUME_NONNULL_END
这个类定义了 RCTWebView,它继承自 RCTViewComponentView。这是所有原生组件的基类,由 React Native 提供。 实现文件(RCTWebView.mm)的代码如下: Demo/RCTWebView/RCTWebView.mm#import "RCTWebView.h"
#import
#import
#import
#import
#import
using namespace facebook::react;
@interface RCTWebView ()
@end
@implementation RCTWebView {
NSURL * _sourceURL;
WKWebView * _webView;
}
-(instancetype)init
{
if(self = [super init]) {
_webView = [WKWebView new];
_webView.navigationDelegate = self;
[self addSubview:_webView];
}
return self;
}
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
{
const auto &oldViewProps = *std::static_pointer_cast
const auto &newViewProps = *std::static_pointer_cast
// Handle your props here
if (oldViewProps.sourceURL != newViewProps.sourceURL) {
NSString *urlString = [NSString stringWithCString:newViewProps.sourceURL.c_str() encoding:NSUTF8StringEncoding];
_sourceURL = [NSURL URLWithString:urlString];
if ([self urlIsValid:newViewProps.sourceURL]) {
[_webView loadRequest:[NSURLRequest requestWithURL:_sourceURL]];
}
}
[super updateProps:props oldProps:oldProps];
}
-(void)layoutSubviews
{
[super layoutSubviews];
_webView.frame = self.bounds;
}
#pragma mark - WKNavigationDelegate
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
CustomWebViewEventEmitter::OnScriptLoaded result = CustomWebViewEventEmitter::OnScriptLoaded{CustomWebViewEventEmitter::OnScriptLoadedResult::Success};
self.eventEmitter.onScriptLoaded(result);
}
- (BOOL)urlIsValid:(std::string)propString
{
if (propString.length() > 0 && !_sourceURL) {
CustomWebViewEventEmitter::OnScriptLoaded result = CustomWebViewEventEmitter::OnScriptLoaded{CustomWebViewEventEmitter::OnScriptLoadedResult::Error};
self.eventEmitter.onScriptLoaded(result);
return NO;
}
return YES;
}
// Event emitter convenience method
- (const CustomWebViewEventEmitter &)eventEmitter
{
return static_cast
}
+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider
}
@end
这段代码使用 Objective-C++ 编写,包含以下关键细节: @interface 实现了两个协议: * RCTCustomWebViewViewProtocol,由 Codegen 生成; * WKNavigationDelegate,由 WebKit 框架提供,用于处理 web 视图的导航事件; * init 方法:实例化 WKWebView,将其添加为子视图,并设置 navigationDelegate; * updateProps 方法:当组件的 props 发生变化时,由 React Native 调用; * layoutSubviews 方法:描述自定义视图的布局方式; * webView:didFinishNavigation: 方法:处理 WKWebView 完成页面加载后的操作; * urlIsValid:(std::string)propString 方法:检查作为 prop 传入的 URL 是否有效; * eventEmitter 方法:用于获取强类型 eventEmitter 实例的工具方法; * componentDescriptorProvider:返回由 Codegen 生成的 ComponentDescriptor。 添加 WebKit 框架 注意,这一步仅在创建 Web 视图时需要。iOS 上的 Web 组件需要链接 Apple 提供的 WebKit 框架。如果你的组件不需要访问 web 相关功能,可以跳过此步。 web 视图需要使用 Apple 通过 Xcode 及设备自带框架提供的一项功能:WebKit。 你可以在原生代码中看到 RCTWebView.mm 里添加的 `#import