Ta strona została przetłumaczona przez PageTurner AI (beta). Nie jest oficjalnie zatwierdzona przez projekt. Znalazłeś błąd? Zgłoś problem →
Komponenty natywne
Jeśli chcesz tworzyć nowe komponenty React Native opakowujące komponent hosta, jak np. niestandardowy CheckBox na Androidzie czy UIButton na iOS, powinieneś użyć natywnego komponentu Fabric.
Ten przewodnik pokaże ci, jak budować natywne komponenty Fabric poprzez implementację komponentu web view. Kroki do wykonania:
-
Zdefiniuj specyfikację JavaScript używając Flow lub TypeScript.
-
Skonfiguruj system zarządzania zależnościami do generowania kodu z dostarczonej specyfikacji i auto-linkowania.
-
Zaimplementuj kod natywny.
-
Użyj komponentu w aplikacji.
Będziesz potrzebować wygenerowanej aplikacji szablonowej do użycia komponentu:
npx @react-native-community/cli@latest init Demo --install-pods false
Tworzenie komponentu WebView
Ten przewodnik pokaże, jak stworzyć komponent Web View. Utworzymy go używając komponentu WebView na Androidzie oraz WKWebView na iOS.
Zacznijmy od stworzenia struktury katalogów dla naszego komponentu:
mkdir -p Demo/{specs,android/app/src/main/java/com/webview}
Otrzymasz następującą strukturę, w której będziesz pracować:
Demo
├── android/app/src/main/java/com/webview
└── ios
└── specs
-
Katalog
android/app/src/main/java/com/webviewbędzie zawierał nasz kod Androida. -
Katalog
iosbędzie zawierał nasz kod iOS. -
Katalog
specsbędzie zawierał plik specyfikacji dla Codegen.
1. Definiowanie specyfikacji dla Codegen
Twoja specyfikacja musi być zdefiniowana w TypeScript lub Flow (więcej w dokumentacji Codegen). Służy Codegen do generowania kodu C++, Objective-C++ i Java, który łączy twój kod platformowy ze środowiskiem JavaScript, w którym działa React.
Plik specyfikacji musi mieć nazwę <MODULE_NAME>NativeComponent.{ts|js}, aby działał z Codegen. Przyrostek NativeComponent to nie tylko konwencja - jest faktycznie używany przez Codegen do wykrywania pliku specyfikacji.
Użyj tej specyfikacji dla naszego komponentu WebView:
- TypeScript
- Flow
import type {HostComponent, ViewProps} from 'react-native';
import type {BubblingEventHandler} from 'react-native/Libraries/Types/CodegenTypes';
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
type WebViewScriptLoadedEvent = {
result: 'success' | 'error';
};
export interface NativeProps extends ViewProps {
sourceURL?: string;
onScriptLoaded?: BubblingEventHandler<WebViewScriptLoadedEvent> | null;
}
export default codegenNativeComponent<NativeProps>(
'CustomWebView',
) as HostComponent<NativeProps>;
// @flow strict-local
import type {HostComponent, ViewProps} from 'react-native';
import type {BubblingEventHandler} from 'react-native/Libraries/Types/CodegenTypes';
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
type WebViewScriptLoadedEvent = $ReadOnly<{|
result: "success" | "error",
|}>;
type NativeProps = $ReadOnly<{|
...ViewProps,
sourceURL?: string;
onScriptLoaded?: BubblingEventHandler<WebViewScriptLoadedEvent>?;
|}>;
export default (codegenNativeComponent<NativeProps>(
'CustomWebView',
): HostComponent<NativeProps>);
Ta specyfikacja składa się z trzech głównych części (bez importów):
-
WebViewScriptLoadedEventto typ pomocniczy dla danych przekazywanych z natywnego kodu do JavaScript. -
NativePropsdefiniuje propsy, które możemy ustawić na komponencie. -
Instrukcja
codegenNativeComponentumożliwia wygenerowanie kodu dla niestandardowego komponentu i definiuje nazwę używaną do dopasowania implementacji natywnych.
Podobnie jak w przypadku modułów natywnych, możesz mieć wiele plików specyfikacji w katalogu specs/. Więcej informacji o typach i ich mapowaniu na typy platformowe znajdziesz w dodatku.
2. Konfiguracja działania Codegen
Specyfikacja jest używana przez narzędzia Codegen React Native do generowania interfejsów platformowych i kodu szkieletowego. Aby to zadziałało, Codegen musi wiedzieć, gdzie znaleźć naszą specyfikację i co z nią zrobić. Zaktualizuj swój package.json dodając:
"start": "react-native start",
"test": "jest"
},
"codegenConfig": {
"name": "AppSpec",
"type": "components",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.webview"
},
"ios": {
"componentProvider": {
"CustomWebView": "RCTWebView"
}
}
},
"dependencies": {
Po skonfigurowaniu Codegen, musimy przygotować nasz kod natywny do integracji z wygenerowanym kodem.
Należy zauważyć, że dla iOS deklaratywnie mapujemy nazwę komponentu JS eksportowaną przez specyfikację (CustomWebView) z klasą w iOS, która będzie natywnie implementować komponent.
2. Budowanie kodu natywnego
Teraz nadszedł czas na napisanie natywnego kodu platformy, aby gdy React zażąda renderowania widoku, platforma mogła utworzyć odpowiedni natywny widok i wyświetlić go na ekranie.
Powinieneś przepracować to zarówno na platformie Android, jak i iOS.
Ten przewodnik pokazuje, jak utworzyć komponent natywny działający tylko z Nową Architekturą. Jeśli potrzebujesz wsparcia zarówno dla Nowej Architektury, jak i Starszej Architektury, zapoznaj się z naszym przewodnikiem zgodności wstecznej.
- Android
- iOS
Ta strona została przetłumaczona przez PageTurner AI (beta). Nie jest oficjalnie zatwierdzona przez projekt. Znalazłeś błąd? Zgłoś problem →
Czas napisać kod dla platformy Android, który umożliwi renderowanie widoku webowego. Kroki, które należy wykonać:
-
Uruchomienie Codegen
-
Napisanie kodu dla
ReactWebView -
Napisanie kodu dla
ReactWebViewManager -
Napisanie kodu dla
ReactWebViewPackage -
Rejestracja
ReactWebViewPackagew aplikacji
1. Uruchom Codegen przez Gradle
Wykonaj to jednorazowo, aby wygenerować szablony kodu, które może wykorzystać twoje IDE.
cd android
./gradlew generateCodegenArtifactsFromSchema
Codegen wygeneruje interfejs ViewManager do zaimplementowania oraz delegata ViewManager dla widoku webowego.
2. Stwórz ReactWebView
ReactWebView to komponent opakowujący natywny widok Androida, który React Native będzie renderował przy użyciu naszego komponentu niestandardowego.
Utwórz plik ReactWebView.java lub ReactWebView.kt w folderze android/src/main/java/com/webview z następującym kodem:
- 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
}
}
ReactWebView rozszerza Androidowy WebView, więc możesz łatwo wykorzystać wszystkie właściwości już zdefiniowane przez platformę.
Klasa definiuje trzy konstruktory Androida, ale ich faktyczną implementację przekazuje do prywatnej funkcji configureComponent. Ta funkcja odpowiada za inicjalizację właściwości specyficznych dla komponentu: w tym przypadku ustawiasz układ WebView i definiujesz WebClient, którego używasz do dostosowania zachowania WebView. W tym kodzie ReactWebView emituje zdarzenie po zakończeniu ładowania strony, poprzez implementację metody onPageFinished w WebClient.
Kod definiuje następnie funkcję pomocniczą do emitowania zdarzeń. Aby wyemitować zdarzenie, musisz:
-
uzyskać referencję do
ReactContext; -
pobrać
surfaceIdwidoku, który prezentujesz; -
uzyskać referencję do
eventDispatcherpowiązanego z widokiem; -
zbudować ładunek zdarzenia przy użyciu obiektu
WritableMap; -
stworzyć obiekt zdarzenia do wysłania do JavaScript;
-
wywołać
eventDispatcher.dispatchEvent, aby wysłać zdarzenie.
Ostatnia część pliku zawiera definicje typów danych potrzebnych do wysłania zdarzenia:
-
OnScriptLoadedEventResultz możliwymi wynikami zdarzeniaOnScriptLoaded; -
właściwe
OnScriptLoadedEvent, które musi rozszerzać klasęEventz React Native.
3. Stwórz WebViewManager
WebViewManager to klasa łącząca środowisko wykonawcze React Native z natywnym widokiem.
Gdy React otrzyma z aplikacji instrukcję renderowania konkretnego komponentu, używa zarejestrowanego menedżera widoków do stworzenia widoku i przekazania wymaganych właściwości.
Oto kod dla 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"
)))
}
ReactWebViewManager rozszerza klasę SimpleViewManager z Reacta i implementuje CustomWebViewManagerInterface wygenerowany przez Codegen.
Przechowuje referencję do CustomWebViewManagerDelegate - kolejnego elementu wygenerowanego przez Codegen.
Następnie nadpisuje funkcję getName, która musi zwracać tę samą nazwę używaną w wywołaniu funkcji codegenNativeComponent w specyfikacji.
Funkcja createViewInstance odpowiada za utworzenie nowej instancji ReactWebView.
Następnie ViewManager musi zdefiniować, jak wszystkie właściwości komponentów Reacta będą aktualizować natywny widok. W tym przykładzie musisz zdecydować, jak obsłużyć właściwość sourceURL, którą React ustawi na WebView.
Na koniec, jeśli komponent może emitować zdarzenie, musisz zmapować nazwę zdarzenia przez nadpisanie getExportedCustomBubblingEventTypeConstants dla zdarzeń bąbelkujących lub getExportedCustomDirectEventTypeConstants dla zdarzeń bezpośrednich.
4. Napisz ReactWebViewPackage
Podobnie jak w przypadku modułów natywnych, komponenty natywne również muszą implementować klasę ReactPackage. Jest to obiekt, którego możesz użyć do zarejestrowania komponentu w środowisku wykonawczym React Native.
Oto kod dla 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,
)
)
}
}
ReactWebViewPackage rozszerza BaseReactPackage i implementuje wszystkie metody wymagane do poprawnego zarejestrowania naszego komponentu.
-
metoda
createViewManagersjest metodą fabryczną, która tworzyViewManagerzarządzający niestandardowymi widokami. -
metoda
getModulezwraca odpowiedni ViewManager w zależności od Widoku, który React Native musi wyrenderować. -
getReactModuleInfoProviderdostarcza wszystkie informacje wymagane podczas rejestracji modułu w środowisku wykonawczym,
5. Zarejestruj ReactWebViewPackage w aplikacji
Na koniec musisz zarejestrować ReactWebViewPackage w aplikacji. Robimy to, modyfikując plik MainApplication przez dodanie ReactWebViewPackage do listy pakietów zwracanych przez funkcję getPackages.
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()
}
}
}
Ta strona została przetłumaczona przez PageTurner AI (beta). Nie jest oficjalnie zatwierdzona przez projekt. Znalazłeś błąd? Zgłoś problem →
Czas napisać kod dla platformy iOS, aby móc renderować widok internetowy. Kroki, które należy wykonać:
-
Uruchom Codegen.
-
Napisz kod dla
RCTWebView -
Zarejestruj
RCTWebVieww aplikacji
1. Uruchomienie Codegen
Możesz ręcznie uruchomić Codegen, jednak prościej jest użyć aplikacji, w której będziesz testować komponent, aby zrobił to za Ciebie.
cd ios
bundle install
bundle exec pod install
Kluczowe jest to, że zobaczysz logi wyjściowe Codegen, których użyjemy w Xcode do zbudowania naszego natywnego komponentu WebView.
Należy zachować ostrożność przy commitowaniu wygenerowanego kodu do repozytorium. Wygenerowany kod jest specyficzny dla każdej wersji React Native. Użyj peerDependencies w npm, aby ograniczyć kompatybilność z wersjami React Native.
2. Pisanie RCTWebView
Przygotuj projekt iOS w Xcode, wykonując te 5 kroków:
- Otwórz workspace Xcode wygenerowany przez CocoaPods:
cd ios
open Demo.xcworkspace
- Kliknij prawym przyciskiem na aplikację i wybierz
New Group, nazwij nową grupęWebView.
- W grupie
WebViewutwórzNew→File from Template.
- Użyj szablonu
Objective-C Filei nazwij goRCTWebView.
-
Powtórz krok 4 i utwórz plik nagłówkowy o nazwie
RCTWebView.h. -
Zmień nazwę
RCTWebView.m→RCTWebView.mm, tworząc plik Objective-C++.
Podfile
...
Demo
├── AppDelegate.swift
...
├── RCTWebView.h
└── RCTWebView.mm
Po utworzeniu plików nagłówkowego i implementacyjnego możesz rozpocząć ich implementację.
Oto kod pliku RCTWebView.h, który deklaruje interfejs komponentu:
#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
Ta klasa definiuje RCTWebView, który rozszerza klasę RCTViewComponentView. Jest to klasa bazowa dla wszystkich komponentów natywnych, dostarczana przez React Native.
Kod pliku implementacyjnego (RCTWebView.mm) wygląda następująco:
#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
Ten kod napisany w Objective-C++ zawiera różne szczegóły:
-
@interfaceimplementuje dwa protokoły:RCTCustomWebViewViewProtocol, wygenerowany przez Codegen;WKNavigationDelegate, dostarczany przez framework WebKit do obsługi zdarzeń nawigacji w widoku internetowym;
-
metodę
init, która tworzy instancjęWKWebView, dodaje ją do podwidoków i ustawianavigationDelegate; -
metodę
updateProps, wywoływaną przez React Native gdy zmieniają się propsy komponentu; -
metodę
layoutSubviews, która opisuje sposób rozmieszczenia widoku; -
metodę
webView:didFinishNavigation:, która pozwala obsłużyć zakończenie ładowania strony wWKWebView; -
metodę
urlIsValid:(std::string)propString, która sprawdza poprawność URL przekazanego jako prop; -
metodę
eventEmitter, która jest narzędziem do pobierania silnie typowanej instancjieventEmitter; -
componentDescriptorProvider, który zwracaComponentDescriptorwygenerowany przez Codegen;
Dodanie frameworka WebKit
Ten krok jest wymagany tylko dlatego, że tworzymy widok sieci Web. Komponenty internetowe w iOS muszą być połączone z frameworkiem WebKit dostarczanym przez Apple. Jeśli twój komponent nie wymaga dostępu do funkcji sieciowych, możesz pominąć ten krok.
Widok sieci Web wymaga dostępu do niektórych funkcji, które Apple udostępnia poprzez jeden z frameworków dostarczanych z Xcode i urządzeniami: WebKit.
Możesz to zobaczyć w kodzie natywnym po dodaniu linii #import <WebKit/WebKit.h> w pliku RCTWebView.mm.
Aby połączyć framework WebKit w swojej aplikacji, wykonaj następujące kroki:
-
W Xcode kliknij swój projekt
-
Wybierz cel aplikacji
-
Wybierz zakładkę General (Ogólne)
-
Przewiń w dół do sekcji "Frameworks, Libraries, and Embedded Contents" (Frameworki, biblioteki i osadzone treści) i naciśnij przycisk
+
-
W pasku wyszukiwania wpisz "WebKit"
-
Wybierz framework WebKit
-
Kliknij Add (Dodaj).

3. Używanie twojego komponentu natywnego
Wreszcie możesz użyć nowego komponentu w swojej aplikacji. Zaktualizuj wygenerowany plik App.tsx do:
import React from 'react';
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('Page Loaded');
}}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
alignContent: 'center',
},
webview: {
width: '100%',
height: '100%',
},
});
export default App;
Ten kod tworzy aplikację wykorzystującą nowy komponent WebView, który stworzyliśmy do ładowania strony react.dev.
Aplikacja pokazuje również alert po załadowaniu strony internetowej.
4. Uruchom swoją aplikację za pomocą komponentu WebView
- Android
- iOS
yarn run android
yarn run ios
| Android | iOS |
|---|---|
![]() | ![]() |

