Native Platform: Native Components (Paper)
Diese Anleitung behandelt die Bereitstellung von nativen Windows-UIs für React Native durch die Implementierung einer Native Component für die Windows-Plattform. Für einen übergeordneten Überblick über die native Entwicklung unter Windows siehe Native Platform: Overview, bevor Sie diese Anleitung lesen.
Hinweis: Weitere Schritte zur Implementierung neuer Native Components für die Plattformen Android und iOS finden Sie in der Anleitung zu Native Components auf reactnative.dev.
Architektur-Hinweis: Diese Anleitung zeigt, wie eine Paper Native Component erstellt wird, um die alte Architektur von React Native zu unterstützen. Sie funktioniert nicht mit Apps der neuen Architektur. Um React Native für Windows-Apps zu unterstützen, die auf die neue Architektur abzielen, siehe Native Platform: Native Components (Fabric). Weitere Informationen zu React Native-Architekturen in React Native für Windows finden Sie unter New vs. Old Architecture.
Überblick
Um die Windows-Unterstützung für eine Native Component zu implementieren, müssen Sie
- Die API-Oberfläche für Ihre Native Component in TypeScript-Spec-Dateien definieren
- Den Native Library Codegen von React Native for Windows verwenden, um die TypeScript-Spec-Dateien zu verarbeiten und die C++-Header für den Windows-Code zu erstellen
- Den Windows C++-Code zum Implementieren des View Manager schreiben
- Die Native Component in Ihrem JavaScript verwenden
Schritt-für-Schritt-Anleitung
0. Einrichtung
Sie benötigen ein React Native-Bibliotheksprojekt, das mit Windows-Unterstützung initialisiert wurde.
Hinweis: Der Rest dieser Anleitung geht davon aus, dass Sie die Anleitung Native Plattform: Erste Schritte befolgt haben, um ein neues Bibliotheksprojekt namens
testlibeinzurichten.
1. Definieren Sie die API-Oberfläche in TypeScript
Die Standardvorlage für eine neue Bibliothek enthält kein Beispiel für eine Native Component, daher müssen wir eine erstellen. Für diese Anleitung werden wir eine Komponente implementieren, die nativen UI-Code verwendet, um ihre Kindkomponente innerhalb der Grenzen eines Kreises zu rendern und die Ecken zu maskieren. Diese CircleMask könnte verwendet werden, um beispielsweise ein quadratisches Benutzerkonto- oder Profilbild als Kreis darzustellen.
Zuerst müssen wir die Schnittstelle der Komponente in einer neuen TypeScript-Spec-Datei src\CircleMaskNativeComponent.ts erstellen
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
import type { ViewProps } from 'react-native';
export interface CircleMaskProps extends ViewProps {}
export default codegenNativeComponent<CircleMaskProps>('CircleMask');
Diese Spec-Datei deklariert, dass React Native erwartet, dass jede Plattform eine Native Component namens CircleMask implementiert, die die Eigenschaften von CircleMaskProps unterstützt.
Hinweis: Jede Native Component Spec-Datei muss im Format
<componentName> + NativeComponent.tsbenannt sein, um korrekt als Spec-Datei identifiziert zu werden und nicht nur als reguläre TypeScript-Datei in Ihrer Bibliothek.
2. Verwenden Sie den Native Library Codegen von React Native für Windows
Bei der Erstellung einer Fabric Native Component wird der Native Library Codegen von React Native for Windows verwendet, um eine Reihe von C++-Code zu generieren, der Ihnen bei der Implementierung der Native Component hilft. Leider hilft dieser generierte Code nicht bei Paper Native Component-Implementierungen. Weitere Details finden Sie unter Native Platform: Native Components (Fabric).
3. Implementieren Sie den Windows-C++-Code
Jetzt ist es an der Zeit, CircleMaskViewManager im Windows-Code zu implementieren. React Native for Windows Paper View Manager werden in C++ implementiert und rendern die UI mithilfe der Windows::UI::Xaml APIs, auch bekannt als UWP XAML.
3.1 Implementierung des Paper View Manager
Um unseren neuen Paper View Manager zu erstellen, benötigen wir zwei neue Dateien (für unser Beispiel CircleMask.h und CircleMask.cpp im Ordner windows\testlib)
#pragma once
#include "pch.h"
#ifndef RNW_NEW_ARCH
#include <winrt/Windows.UI.Xaml.Controls.h>
#include <winrt/Windows.UI.Xaml.Data.h>
#endif
namespace winrt::testlib::implementation {
void RegisterCircleMaskNativeComponent(
winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept;
#ifndef RNW_NEW_ARCH
struct CircleMaskViewManager : winrt::implements<
CircleMaskViewManager,
winrt::Microsoft::ReactNative::IViewManager,
winrt::Microsoft::ReactNative::IViewManagerWithChildren> {
public:
CircleMaskViewManager() {}
// IViewManager
winrt::hstring Name() noexcept;
winrt::Windows::UI::Xaml::FrameworkElement CreateView() noexcept;
// IViewManagerWithChildren
void AddView(
winrt::Windows::UI::Xaml::FrameworkElement const &parent,
winrt::Windows::UI::Xaml::UIElement const &child,
int64_t /*index*/) noexcept;
void RemoveAllChildren(winrt::Windows::UI::Xaml::FrameworkElement const &parent) noexcept;
void RemoveChildAt(winrt::Windows::UI::Xaml::FrameworkElement const &parent, int64_t /*index*/) noexcept;
void ReplaceChild(
winrt::Windows::UI::Xaml::FrameworkElement const &parent,
winrt::Windows::UI::Xaml::UIElement const & /*oldChild*/,
winrt::Windows::UI::Xaml::UIElement const &newChild) noexcept;
};
struct HeightToCornerRadiusConverter
: winrt::implements<HeightToCornerRadiusConverter, winrt::Windows::UI::Xaml::Data::IValueConverter> {
public:
HeightToCornerRadiusConverter() {}
winrt::Windows::Foundation::IInspectable Convert(
winrt::Windows::Foundation::IInspectable const &value,
winrt::Windows::UI::Xaml::Interop::TypeName const & /*targetType*/,
winrt::Windows::Foundation::IInspectable const & /*parameter*/,
winrt::hstring const & /*language*/) noexcept;
winrt::Windows::Foundation::IInspectable ConvertBack(
winrt::Windows::Foundation::IInspectable const &value,
winrt::Windows::UI::Xaml::Interop::TypeName const & /*targetType*/,
winrt::Windows::Foundation::IInspectable const & /*parameter*/,
winrt::hstring const & /*language*/) noexcept;
static winrt::Windows::UI::Xaml::Data::IValueConverter Instance() noexcept;
// IValueConverter
};
#endif // #ifndef RNW_NEW_ARCH
} // namespace winrt::testlib::implementation
#include "pch.h"
#include "CircleMask.h"
namespace winrt::testlib::implementation {
void RegisterCircleMaskNativeComponent(
winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept {
#ifndef RNW_NEW_ARCH
packageBuilder.AddViewManager(L"CircleMaskViewManager", []() { return winrt::make<CircleMaskViewManager>(); });
#endif
}
#ifndef RNW_NEW_ARCH
// IViewManager
winrt::hstring CircleMaskViewManager::Name() noexcept {
return L"CircleMask";
}
winrt::Windows::UI::Xaml::FrameworkElement CircleMaskViewManager::CreateView() noexcept {
auto const &view = winrt::Windows::UI::Xaml::Controls::Border();
auto const &binding = winrt::Windows::UI::Xaml::Data::Binding();
binding.Source(view);
binding.Path(winrt::Windows::UI::Xaml::PropertyPath(L"Height"));
binding.Converter(HeightToCornerRadiusConverter::Instance());
view.SetBinding(winrt::Windows::UI::Xaml::Controls::Border::CornerRadiusProperty(), binding);
return view;
}
// IViewManagerWithChildren
void CircleMaskViewManager::AddView(
winrt::Windows::UI::Xaml::FrameworkElement const &parent,
winrt::Windows::UI::Xaml::UIElement const &child,
int64_t /*index*/) noexcept {
if (auto const &border = parent.try_as<winrt::Windows::UI::Xaml::Controls::Border>()) {
border.Child(child);
}
}
void CircleMaskViewManager::RemoveAllChildren(winrt::Windows::UI::Xaml::FrameworkElement const &parent) noexcept {
if (auto const &border = parent.try_as<winrt::Windows::UI::Xaml::Controls::Border>()) {
border.Child(nullptr);
}
}
void CircleMaskViewManager::RemoveChildAt(
winrt::Windows::UI::Xaml::FrameworkElement const &parent,
int64_t /*index*/) noexcept {
if (auto const &border = parent.try_as<winrt::Windows::UI::Xaml::Controls::Border>()) {
border.Child(nullptr);
}
}
void CircleMaskViewManager::ReplaceChild(
winrt::Windows::UI::Xaml::FrameworkElement const &parent,
winrt::Windows::UI::Xaml::UIElement const & /*oldChild*/,
winrt::Windows::UI::Xaml::UIElement const &newChild) noexcept {
if (auto const &border = parent.try_as<winrt::Windows::UI::Xaml::Controls::Border>()) {
border.Child(newChild);
}
}
winrt::Windows::Foundation::IInspectable HeightToCornerRadiusConverter::Convert(
winrt::Windows::Foundation::IInspectable const &value,
winrt::Windows::UI::Xaml::Interop::TypeName const & /*targetType*/,
winrt::Windows::Foundation::IInspectable const & /*parameter*/,
winrt::hstring const & /*language*/) noexcept {
double d = winrt::unbox_value<double>(value);
if (isnan(d)) {
d = 0.0;
}
return winrt::box_value(winrt::Windows::UI::Xaml::CornerRadiusHelper::FromUniformRadius(d));
}
winrt::Windows::Foundation::IInspectable HeightToCornerRadiusConverter::ConvertBack(
winrt::Windows::Foundation::IInspectable const &value,
winrt::Windows::UI::Xaml::Interop::TypeName const & /*targetType*/,
winrt::Windows::Foundation::IInspectable const & /*parameter*/,
winrt::hstring const & /*language*/) noexcept {
return value;
}
winrt::Windows::UI::Xaml::Data::IValueConverter HeightToCornerRadiusConverter::Instance() noexcept {
static auto const &instance = winrt::make<HeightToCornerRadiusConverter>();
return instance;
};
#endif // #ifndef RNW_NEW_ARCH
} // namespace winrt::testlib::implementation
Wie Sie sehen können, definiert die Datei CircleMask.h drei Dinge:
- Eine Funktion
RegisterCircleMaskNativeComponentzur Registrierung desCircleMaskPaper View Manager bei React Native - Eine Struktur
CircleMaskViewManager, die denCircleMaskPaper View Manager enthält - Eine Struktur
HeightToCornerRadiusConvertermit einigen Hilfsfunktionen
Dann haben wir in CircleMask.cpp die Implementierungsdetails für unsere neue Native Component. Beachten Sie die Verwendung von #ifndef RNW_NEW_ARCH, um sicherzustellen, dass der Paper View Manager-Code nur dann enthalten ist, wenn die Bibliothek von Apps der alten Architektur verwendet wird.
Hinweis: Ein vollständigeres Beispiel, wie eine
CircleMask-Komponente sowohl für Fabric als auch für Paper gleichzeitig implementiert wird, finden Sie in der Implementierung im Projekt Native Module Sample.
3.2 Hinzufügen der Dateien der Native Component zum nativen Projekt
Da wir einige neue native Dateien erstellt haben (oben CircleMask.h und CircleMask.cpp), müssen wir sicherstellen, dass sie im nativen Windows-Projekt (in unserem Beispiel windows\testlib\testlib.vcxproj und windows\testlib\testlib.vcxproj.filters) enthalten sind, damit sie in den nativen Build einbezogen werden.
<ItemGroup>
<ClInclude Include="testlib.h" />
+ <ClInclude Include="CircleMask.h" />
<ClInclude Include="ReactPackageProvider.h">
<DependentUpon>ReactPackageProvider.idl</DependentUpon>
</ClInclude>
<ClInclude Include="resource.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="targetver.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="testlib.cpp" />
+ <ClCompile Include="CircleMask.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="ReactPackageProvider.cpp">
<DependentUpon>ReactPackageProvider.idl</DependentUpon>
</ClCompile>
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="targetver.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="testlib.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="CircleMask.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="testlib.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="CircleMask.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
3.3 Registrierung des Paper View Manager beim React Package Provider
Jede React Native for Windows-Bibliothek enthält einen IReactPackageProvider, der alle nativen Module (und/oder Komponenten) der Bibliothek enthält, damit React Native sie zur Laufzeit verwenden kann. Die letzte native Arbeit, die wir leisten müssen, ist die Aktualisierung von ReactPackageProvider::CreatePackage in windows\testlib\ReactPackageProvider.cpp.
#include "pch.h"
#include "ReactPackageProvider.h"
#if __has_include("ReactPackageProvider.g.cpp")
#include "ReactPackageProvider.g.cpp"
#endif
#include "testlib.h"
#include "CircleMask.h"
using namespace winrt::Microsoft::ReactNative;
namespace winrt::testlib::implementation {
void ReactPackageProvider::CreatePackage(IReactPackageBuilder const &packageBuilder) noexcept {
AddAttributedModules(packageBuilder, true);
RegisterCircleMaskNativeComponent(packageBuilder);
}
} // namespace winrt::testlib::implementation
Der entscheidende Punkt hier ist das Hinzufügen des Includes #include "CircleMask.h" und der Aufruf der Funktion RegisterCircleMaskNativeComponent, die wir zuvor erstellt haben. Dies stellt sicher, dass die neue Native Component in das Paket der Bibliothek aufgenommen wird.
4. Verwenden der Native Component in Ihrem JavaScript
Wenn wir nun zur TypeScript-Spec-Datei CircleMaskNativeComponent.ts zurückkehren, sehen wir, dass sie die Schnittstelle CircleMaskProps sowie die Native Component exportiert. Der nächste Schritt ist die Verwendung dieser exportierten Elemente in unserem JavaScript-Code.
Da der Zweck der Bibliothek darin besteht, die native Funktionalität für Code außerhalb der Bibliothek (auch bekannt als unser React Native für Windows-App-Code) bereitzustellen, ist die Standardeinstellung, die Funktionalität im Index der Bibliothek zu exportieren, in diesem Fall in src\index.tsx.
import Testlib from './NativeTestlib';
export function multiply(a: number, b: number): number {
return Testlib.multiply(a, b);
}
export {default as CircleMask} from './CircleMaskNativeComponent';
export * from './CircleMaskNativeComponent';
Wir sehen dann, dass das JavaScript-Modul testlib einfach unsere neue Native Component als CircleMask exportiert und alles andere unverändert aus dem Modul CircleMaskNativeComponent.
Hinweis: Bibliotheken sind nicht verpflichtet, ihre Native Components direkt an ihre Konsumenten weiterzugeben. Dieses Beispiel zeigt nur den einfachsten Fall, die
CircleMaskNative Component unverändert zu exportieren. Bibliotheken können und tun oft ihre Native Components in JavaScript-Komponenten einbetten und bieten daher eine völlig andere API-Oberfläche für ihre Kunden.
Nächste Schritte
Nachdem Sie Ihre native Bibliothek implementiert haben, ist der letzte Schritt die Nutzung in Ihrer React Native für Windows-App. Fahren Sie mit Native Plattform: Native Bibliotheken verwenden fort.