原生组件
如果你想构建 新的 React Native 组件,用于包裹一个 Host Component,例如 Android 上一种独特的 CheckBox,或者 iOS 上的 UIButton,你应该使用 Fabric 原生组件。
本指南将通过实现一个 web view 组件来展示如何构建 Fabric 原生组件。具体步骤如下:
- 使用 Flow 或 TypeScript 定义 JavaScript 规范。
- 配置依赖管理系统,以便根据提供的规范生成代码并自动链接。
- 实现原生代码。
- 在应用中使用该组件。
你需要一个由普通模板生成的应用来使用该组件:
npx @react-native-community/cli@latest init Demo --install-pods false
创建 WebView 组件
本指南将展示如何创建一个 Web View 组件。我们将使用 Android 的 WebView 组件,以及 iOS 的 WKWebView 组件来创建该组件。
让我们先创建用于存放组件代码的文件夹结构:
mkdir -p Demo/{specs,android/app/src/main/java/com/webview}
这会得到如下的工作目录布局:
Demo
├── android/app/src/main/java/com/webview
└── ios
└── specs
android/app/src/main/java/com/webview文件夹将包含我们的 Android 代码。ios文件夹将包含我们的 iOS 代码。specs文件夹将包含 Codegen 的规范文件。
1. 为 Codegen 定义规范
你的规范必须使用 TypeScript 或 Flow 定义(更多细节请参见 Codegen 文档)。Codegen 会使用它生成 C++、Objective-C++ 和 Java 代码,以将你的平台代码连接到 React 运行所在的 JavaScript 运行时。
规范文件必须命名为 <MODULE_NAME>NativeComponent.{ts|js} 才能与 Codegen 配合使用。后缀 NativeComponent 不仅仅是一种约定,它实际上会被 Codegen 用来检测规范文件。
为我们的 WebView 组件使用以下规范:
- TypeScript
- Flow
import type {
CodegenTypes,
HostComponent,
ViewProps,
} from 'react-native';
import {codegenNativeComponent} from 'react-native';
type WebViewScriptLoadedEvent = {
result: 'success' | 'error';
};
export interface NativeProps extends ViewProps {
sourceURL?: string;
onScriptLoaded?: CodegenTypes.BubblingEventHandler<WebViewScriptLoadedEvent> | null;
}
export default codegenNativeComponent<NativeProps>(
'CustomWebView',
) as HostComponent<NativeProps>;
// @flow strict-local
import type {CodegenTypes, HostComponent, ViewProps} from 'react-native';
import {codegenNativeComponent} from 'react-native';
type WebViewScriptLoadedEvent = $ReadOnly<{|
result: "success" | "error",
|}>;
type NativeProps = $ReadOnly<{|
...ViewProps,
sourceURL?: string;
onScriptLoaded?: CodegenTypes.BubblingEventHandler<WebViewScriptLoadedEvent>?;
|}>;
export default (codegenNativeComponent<NativeProps>(
'CustomWebView',
): HostComponent<NativeProps>);
该规范由三部分主要内容组成,不包括导入:
WebViewScriptLoadedEvent是一个辅助数据类型,用于承载事件从原生传递到 JavaScript 所需的数据。NativeProps是我们可以设置在组件上的 props 定义。codegenNativeComponent语句允许我们为自定义组件生成代码,并定义一个用于匹配原生实现的组件名称。
与原生模块一样,你可以在 specs/ 目录中放置多个规范文件。有关你可以使用的类型以及它们映射到的平台类型的更多信息,请参见 附录。
2. 配置 Codegen 运行
该规范会被 React Native 的 Codegen 工具用于为我们生成平台特定的接口和样板代码。为此,Codegen 需要知道去哪里找到我们的规范,以及如何处理它。请更新你的 package.json,添加以下内容:
"start": "react-native start",
"test": "jest"
},
"codegenConfig": {
"name": "AppSpec",
"type": "components",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.webview"
},
"ios": {
"componentProvider": {
"CustomWebView": "RCTWebView"
}
}
},
"dependencies": {
完成 Codegen 的所有连接后,我们需要准备原生代码,以接入生成的代码。
注意,对于 iOS,我们以声明式方式将规范导出的 JS 组件名称(CustomWebView)映射到将原生实现该组件的 iOS 类。
2. 构建你的原生代码
现在该编写原生平台代码了,这样当 React 需要渲染一个视图时,平台就可以创建正确的原生视图并将其渲染到屏幕上。
你需要同时处理 Android 和 iOS 两个平台。
本指南展示的是如何创建一个只适用于新架构的原生组件。如果你需要同时支持新架构和旧架构,请参阅我们的向后兼容指南。
- Android
- iOS
Now it's time to write some Android platform code to be able to render the web view. The steps you need to follow are:
- Running Codegen
- Write the code for the
ReactWebView - Write the code for the
ReactWebViewManager - Write the code for the
ReactWebViewPackage - Register the
ReactWebViewPackagein the application
1. Run Codegen through Gradle
Run this once to generate boilerplate that your IDE of choice can use.
cd android
./gradlew generateCodegenArtifactsFromSchema
Codegen will generate the ViewManager interface you need to implement and the ViewManager delegate for the web view.
2. Write the ReactWebView
The ReactWebView is the component that wraps the Android native view that React Native will render when using our custom Component.
Create a ReactWebView.java or a ReactWebView.kt file in the android/src/main/java/com/webview folder with this code:
- Java
- Kotlin
package com.webview;
import android.content.Context;
import android.util.AttributeSet;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.uimanager.UIManagerHelper;
import com.facebook.react.uimanager.events.Event;
public class ReactWebView extends WebView {
public ReactWebView(Context context) {
super(context);
configureComponent();
}
public ReactWebView(Context context, AttributeSet attrs) {
super(context, attrs);
configureComponent();
}
public ReactWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
configureComponent();
}
private void configureComponent() {
this.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
this.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
emitOnScriptLoaded(OnScriptLoadedEventResult.success);
}
});
}
public void emitOnScriptLoaded(OnScriptLoadedEventResult result) {
ReactContext reactContext = (ReactContext) context;
int surfaceId = UIManagerHelper.getSurfaceId(reactContext);
EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId());
WritableMap payload = Arguments.createMap();
payload.putString("result", result.name());
OnScriptLoadedEvent event = new OnScriptLoadedEvent(surfaceId, getId(), payload);
if (eventDispatcher != null) {
eventDispatcher.dispatchEvent(event);
}
}
public enum OnScriptLoadedEventResult {
success,
error
}
private class OnScriptLoadedEvent extends Event<OnScriptLoadedEvent> {
private final WritableMap payload;
OnScriptLoadedEvent(int surfaceId, int viewId, WritableMap payload) {
super(surfaceId, viewId);
this.payload = payload;
}
@Override
public String getEventName() {
return "onScriptLoaded";
}
@Override
public WritableMap getEventData() {
return payload;
}
}
}
package com.webview
import android.content.Context
import android.util.AttributeSet
import android.webkit.WebView
import android.webkit.WebViewClient
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.bridge.ReactContext
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.events.Event
class ReactWebView: WebView {
constructor(context: Context) : super(context) {
configureComponent()
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
configureComponent()
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
configureComponent()
}
private fun configureComponent() {
this.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
this.webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView, url: String) {
emitOnScriptLoaded(OnScriptLoadedEventResult.success)
}
}
}
fun emitOnScriptLoaded(result: OnScriptLoadedEventResult) {
val reactContext = context as ReactContext
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
val payload =
Arguments.createMap().apply {
putString("result", result.name)
}
val event = OnScriptLoadedEvent(surfaceId, id, payload)
eventDispatcher?.dispatchEvent(event)
}
enum class OnScriptLoadedEventResult {
success,
error;
}
inner class OnScriptLoadedEvent(
surfaceId: Int,
viewId: Int,
private val payload: WritableMap
) : Event<OnScriptLoadedEvent>(surfaceId, viewId) {
override fun getEventName() = "onScriptLoaded"
override fun getEventData() = payload
}
}
The ReactWebView extends the Android WebView so you can reuse all the properties already defined by the platform with ease.
The class defines the three Android constructors but defers their actual implementation to the private configureComponent function. This function takes care of initializing all the components specific properties: in this case you are setting the layout of the WebView and you are defining the WebClient that you use to customize the behavior of the WebView. In this code, the ReactWebView emits an event when the page finishes loading, by implementing the WebClient's onPageFinished method.
The code then defines a helper function to actually emit an event. To emit an event, you have to:
- grab a reference to the
ReactContext; - retrieve the
surfaceIdof the view that you are presenting; - grab a reference to the
eventDispatcherassociated with the view; - build the payload for the event using a
WritableMapobject; - create the event object that you need to send to JavaScript;
- call the
eventDispatcher.dispatchEventto send the event.
The last part of the file contains the definition of the data types you need to send the event:
- The
OnScriptLoadedEventResult, with the possible outcomes of theOnScriptLoadedevent. - The actual
OnScriptLoadedEventthat needs to extend the React Native'sEventclass.
3. Write the WebViewManager
The WebViewManager is the class that connects the React Native runtime with the native view.
When React receives the instruction from the app to render a specific component, React uses the registered view manager to create the view and to pass all the required properties.
This is the code of the ReactWebViewManager.
- Java
- Kotlin
package com.webview;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManagerDelegate;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.viewmanagers.CustomWebViewManagerInterface;
import com.facebook.react.viewmanagers.CustomWebViewManagerDelegate;
import java.util.HashMap;
import java.util.Map;
@ReactModule(name = ReactWebViewManager.REACT_CLASS)
class ReactWebViewManager extends SimpleViewManager<ReactWebView> implements CustomWebViewManagerInterface<ReactWebView> {
private final CustomWebViewManagerDelegate<ReactWebView, ReactWebViewManager> delegate =
new CustomWebViewManagerDelegate<>(this);
@Override
public ViewManagerDelegate<ReactWebView> getDelegate() {
return delegate;
}
@Override
public String getName() {
return REACT_CLASS;
}
@Override
public ReactWebView createViewInstance(ThemedReactContext context) {
return new ReactWebView(context);
}
@ReactProp(name = "sourceUrl")
@Override
public void setSourceURL(ReactWebView view, String sourceURL) {
if (sourceURL == null) {
view.emitOnScriptLoaded(ReactWebView.OnScriptLoadedEventResult.error);
return;
}
view.loadUrl(sourceURL, new HashMap<>());
}
public static final String REACT_CLASS = "CustomWebView";
@Override
public Map<String, Object> getExportedCustomBubblingEventTypeConstants() {
Map<String, Object> map = new HashMap<>();
Map<String, Object> bubblingMap = new HashMap<>();
bubblingMap.put("phasedRegistrationNames", new HashMap<String, String>() {{
put("bubbled", "onScriptLoaded");
put("captured", "onScriptLoadedCapture");
}});
map.put("onScriptLoaded", bubblingMap);
return map;
}
}
package com.webview
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManagerDelegate;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.viewmanagers.CustomWebViewManagerInterface;
import com.facebook.react.viewmanagers.CustomWebViewManagerDelegate;
@ReactModule(name = ReactWebViewManager.REACT_CLASS)
class ReactWebViewManager(context: ReactApplicationContext) : SimpleViewManager<ReactWebView>(), CustomWebViewManagerInterface<ReactWebView> {
private val delegate: CustomWebViewManagerDelegate<ReactWebView, ReactWebViewManager> =
CustomWebViewManagerDelegate(this)
override fun getDelegate(): ViewManagerDelegate<ReactWebView> = delegate
override fun getName(): String = REACT_CLASS
override fun createViewInstance(context: ThemedReactContext): ReactWebView = ReactWebView(context)
@ReactProp(name = "sourceUrl")
override fun setSourceURL(view: ReactWebView, sourceURL: String?) {
if (sourceURL == null) {
view.emitOnScriptLoaded(ReactWebView.OnScriptLoadedEventResult.error)
return;
}
view.loadUrl(sourceURL, emptyMap())
}
companion object {
const val REACT_CLASS = "CustomWebView"
}
override fun getExportedCustomBubblingEventTypeConstants(): Map<String, Any> =
mapOf(
"onScriptLoaded" to
mapOf(
"phasedRegistrationNames" to
mapOf(
"bubbled" to "onScriptLoaded",
"captured" to "onScriptLoadedCapture"
)))
}
The ReactWebViewManager extends the SimpleViewManager class from React and implements the CustomWebViewManagerInterface, generated by Codegen.
It holds a reference of the CustomWebViewManagerDelegate, another element generated by Codegen.
It then overrides the getName function, which must return the same name used in the spec's codegenNativeComponent function call.
The createViewInstance function is responsible to instantiate a new ReactWebView.
Then, the ViewManager needs to define how all the React's components props will update the native view. In the example, you need to decide how to handle the sourceURL property that React will set on the WebView.
Finally, if the component can emit an event, you need to map the event name by overriding the getExportedCustomBubblingEventTypeConstants for bubbling events, or the getExportedCustomDirectEventTypeConstants for direct events.
4. Write the ReactWebViewPackage
As you do with Native Modules, Native Components also need to implement the ReactPackage class. This is an object that you can use to register the component in the React Native runtime.
This is the code for the ReactWebViewPackage:
- Java
- Kotlin
package com.webview;
import com.facebook.react.BaseReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.react.module.model.ReactModuleInfoProvider;
import com.facebook.react.uimanager.ViewManager;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ReactWebViewPackage extends BaseReactPackage {
@Override
public List<ViewManager<?, ?>> createViewManagers(ReactApplicationContext reactContext) {
return Collections.singletonList(new ReactWebViewManager(reactContext));
}
@Override
public NativeModule getModule(String s, ReactApplicationContext reactApplicationContext) {
if (ReactWebViewManager.REACT_CLASS.equals(s)) {
return new ReactWebViewManager(reactApplicationContext);
}
return null;
}
@Override
public ReactModuleInfoProvider getReactModuleInfoProvider() {
return new ReactModuleInfoProvider() {
@Override
public Map<String, ReactModuleInfo> getReactModuleInfos() {
Map<String, ReactModuleInfo> map = new HashMap<>();
map.put(ReactWebViewManager.REACT_CLASS, new ReactModuleInfo(
ReactWebViewManager.REACT_CLASS, // name
ReactWebViewManager.REACT_CLASS, // className
false, // canOverrideExistingModule
false, // needsEagerInit
false, // isCxxModule
true // isTurboModule
));
return map;
}
};
}
}
package com.webview
import com.facebook.react.BaseReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.module.model.ReactModuleInfo
import com.facebook.react.module.model.ReactModuleInfoProvider
import com.facebook.react.uimanager.ViewManager
class ReactWebViewPackage : BaseReactPackage() {
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return listOf(ReactWebViewManager(reactContext))
}
override fun getModule(s: String, reactApplicationContext: ReactApplicationContext): NativeModule? {
when (s) {
ReactWebViewManager.REACT_CLASS -> ReactWebViewManager(reactApplicationContext)
}
return null
}
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider {
mapOf(ReactWebViewManager.REACT_CLASS to ReactModuleInfo(
name = ReactWebViewManager.REACT_CLASS,
className = ReactWebViewManager.REACT_CLASS,
canOverrideExistingModule = false,
needsEagerInit = false,
isCxxModule = false,
isTurboModule = true,
)
)
}
}
The ReactWebViewPackage extends the BaseReactPackage and implements all the methods required to properly register our component.
- the
createViewManagersmethod is the factory method that creates theViewManagerthat manage the custom views. - the
getModulemethod returns the proper ViewManager depending on the View that React Native needs to render. - the
getReactModuleInfoProviderprovides all the information required when registering the module in the runtime,
5. Register the ReactWebViewPackage in the application
Finally, you need to register the ReactWebViewPackage in the application. We do that by modifying the MainApplication file by adding the ReactWebViewPackage to the list of packages returned by the getPackages function.
package com.demo
import android.app.Application
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.soloader.OpenSourceMergedSoMapping
import com.facebook.soloader.SoLoader
import com.webview.ReactWebViewPackage
class MainApplication : Application(), ReactApplication {
override val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
add(ReactWebViewPackage())
}
override fun getJSMainModuleName(): String = "index"
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
}
override val reactHost: ReactHost
get() = getDefaultReactHost(applicationContext, reactNativeHost)
override fun onCreate() {
super.onCreate()
SoLoader.init(this, OpenSourceMergedSoMapping)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
load()
}
}
}
Now it's time to write some iOS platform code to be able to render the web view. The steps you need to follow are:
- Run Codegen.
- Write the code for the
RCTWebView - Register the
RCTWebViewin the application
1. Run Codegen
You can manually run the Codegen, however it's simpler to use the application you're going to demo the component in to do this for you.
cd ios
bundle install
bundle exec pod install
Importantly you will see logging output from Codegen, which we're going to use in Xcode to build our WebView native component.
You should be careful about committing generated code to your repository. Generated code is specific to each version of React Native. Use npm peerDependencies to restrict compatibility with version of React Native.
3. Write the RCTWebView
We need to prepare your iOS project using Xcode by completing these 5 steps:
- Open the CocoaPods generated Xcode Workspace:
cd ios
open Demo.xcworkspace
- Right click on app and select
New Group, call the new groupWebView.
- In the
WebViewgroup, createNew→File from Template.
- Use the
Objective-C Filetemplate, and name itRCTWebView.
-
Repeat step 4 and create a header file named
RCTWebView.h. -
Rename
RCTWebView.m→RCTWebView.mmmaking it an Objective-C++ file.
Podfile
...
Demo
├── AppDelegate.swift
...
├── RCTWebView.h
└── RCTWebView.mm
After creating the header file and the implementation file, you can start implementing them.
This is the code for the RCTWebView.h file, which declares the component interface.
#import <React/RCTViewComponentView.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface RCTWebView : RCTViewComponentView
// You would declare native methods you'd want to access from the view here
@end
NS_ASSUME_NONNULL_END
This class defines an RCTWebView which extends the RCTViewComponentView class. This is the base class for all the native components and it is provided by React Native.
The code for the implementation file (RCTWebView.mm) is the following:
#import "RCTWebView.h"
#import <react/renderer/components/AppSpec/ComponentDescriptors.h>
#import <react/renderer/components/AppSpec/EventEmitters.h>
#import <react/renderer/components/AppSpec/Props.h>
#import <react/renderer/components/AppSpec/RCTComponentViewHelpers.h>
#import <WebKit/WebKit.h>
using namespace facebook::react;
@interface RCTWebView () <RCTCustomWebViewViewProtocol, WKNavigationDelegate>
@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<CustomWebViewProps const>(_props);
const auto &newViewProps = *std::static_pointer_cast<CustomWebViewProps const>(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<const CustomWebViewEventEmitter &>(*_eventEmitter);
}
+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider<CustomWebViewComponentDescriptor>();
}
@end
This code is written in Objective-C++ and contains various details:
- the
@interfaceimplements two protocols:RCTCustomWebViewViewProtocol, generated by Codegen;WKNavigationDelegate, provided by the WebKit framework to handle the web view navigation events;
- the
initmethod that instantiates theWKWebView, adds it to the subviews and that sets thenavigationDelegate; - the
updatePropsmethod that is called by React Native when the component's props change; - the
layoutSubviewsmethod that describes how the custom view needs to be laid out; - the
webView:didFinishNavigation:method that lets you handle what to do when theWKWebViewfinishes loading the page; - the
urlIsValid:(std::string)propStringmethod that checks whether the URL received as prop is valid; - the
eventEmittermethod which is a utility to retrieve a strongly typedeventEmitterinstance - the
componentDescriptorProviderwhich returns theComponentDescriptorgenerated by Codegen;
Add WebKit framework
This step is only required because we are creating a Web view. Web components on iOS needs to be linked against the WebKit framework provided by Apple. If your component doesn't need to access web-specific features, you can skip this step.
A web view requires access to some features that Apple provides through one of the frameworks shipped with Xcode and the devices: WebKit.
You can see it in the native code by the #import <WebKit/WebKit.h> line added in the RCTWebView.mm.
To link the WebKit framework in your app, follow these steps:
- In Xcode, Click on your project
- Select the app target
- Select the General tab
- Scroll down until you find the "Frameworks, Libraries, and Embedded Contents" section, and press the
+button
- In the search bar, filter for WebKit
- Select the WebKit framework
- Click on Add.

3. 使用你的原生组件
最后,你可以在应用中使用这个新组件。将你生成的 App.tsx 更新为:
import {Alert, StyleSheet, View} from 'react-native';
import WebView from './specs/WebViewNativeComponent';
function App(): React.JSX.Element {
return (
<View style={styles.container}>
<WebView
sourceURL="https://react.dev/"
style={styles.webview}
onScriptLoaded={() => {
Alert.alert('页面已加载');
}}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
alignContent: 'center',
},
webview: {
width: '100%',
height: '100%',
},
});
export default App;
这段代码创建了一个应用,它使用我们创建的新的 WebView 组件来加载 react.dev 网站。
当网页加载完成时,应用还会显示一个警报。
4. 使用 WebView 组件运行你的应用
- Android
- iOS
yarn run android
yarn run ios
| Android | iOS |
|---|---|
![]() | ![]() |

