文件
平台概覽
NSH 可在手機、桌面和網頁上運行:iOS、Android、macOS、Windows、Linux、GPUI 和網頁。每個平台都有自己的後端,使用該平台原生的 UI 繪製,因此應用程式在 iPhone 上是 UIKit 視圖,在 Mac 上是 AppKit 視圖,在 Linux 上是 GTK 4 widget,在 GPUI 應用程式中是 GPUI 元素,在瀏覽器中則是 HTML,並沿用該平台的文字、輸入、無障礙功能和深色模式。後端以上的一切都是共用的:響應式圖(reactive graph)、渲染器、Taffy 佈局、組件和原生模組在各處都是同一份 Rust 程式碼,所以同一個應用程式可以在所有平台上運行。以 Rust 編寫的應用程式就是這些程式碼加上你的 crate,沒有 JavaScript VM;TSX 應用程式則在其上以 QuickJS 運行自己的模組(使用 Rust 建構)。
在每個平台上,應用程式都有兩種相同的使用方式:打包成該平台可安裝的應用程式(nsh package),或嵌入你自己的原生應用程式,無論宿主連結的是 Rust root,還是在運行時載入 TSX。開發時使用 nsh run(修改 Rust 會重新建構並重啟應用程式,修改 TSX 則是熱更新)。
#概覽
| 平台 | 後端;文字 | 運行環境 | 開發 | 打包 |
|---|---|---|---|---|
| iOS | UIKit;CoreText | iOS 15.0+,真機和模擬器 | nsh run --target ios |
.app(模擬器)、已簽署的 .ipa |
| Android | Android views;StaticLayout |
Android 9(API 28)+、arm64-v8a、armeabi-v7a 和 x86_64 |
nsh run --target android |
已簽署的 .apk |
| macOS | AppKit 和 Core Animation;CoreText | macOS 14+ | nsh run --target macos |
ad hoc 簽署的 .app |
| Windows | 以 Direct2D 繪製的 Win32 視窗;DirectWrite | Windows 10 1809+、Windows 11 | nsh run(Windows 上的預設值) |
一個目錄和一個 .zip |
| Linux | GTK 4 widget;Pango | GTK 4.14+ | nsh run(Linux 上的預設值) |
一個目錄和一個 .tar.gz |
| GPUI | GPUI;GPUI 的文字系統 | macOS、Windows、Linux(Vulkan) | 在你的 GPUI 應用程式中;TSX 也可使用 nsh run --target gpui |
包含在嵌入它的 GPUI 應用程式內 |
| Web | HTML 和 CSS;瀏覽器的字型 | 現代瀏覽器,桌面和手機 | nsh run --target web |
靜態網站 |
- GPUI 使用 GPUI(姊妹專案 GPUI Kit 的框架)在 macOS、Windows 和 Linux 上同樣繪製應用程式:它是嵌入 GPUI 和 GPUI Kit 應用程式時使用的後端,這些應用程式會把
AppView放進自己的元素樹中。獨立的桌面應用程式則使用各作業系統自己的後端(AppKit、Win32、GTK 4)來運行和打包。 - Web 在瀏覽器頁面中以 HTML 渲染應用程式:
nsh package --target web會發佈適用於任何網頁伺服器或 CDN 的靜態網站,其中 Rust 應用程式的引擎和 crate 會編譯成 WebAssembly,而你自己的頁面可以用createPlayer或 iframe 嵌入應用程式。本網站上的即時示範和 playground 也是以此運行。 - Mac Catalyst 在 Mac 上以 iPhone 大小(390×844)的視窗運行 iOS 後端,供開發之用。使用
--target catalyst選取它,即可查看應用程式在手機上的外觀。
#開發和打包
如果你沒有提供 --target,nsh run 會選用該機器自己的平台:macOS 上是 AppKit,Linux 上是 GTK 視窗,Windows 上是 Win32 視窗。手機則要從電腦連接:iOS 需要裝有 Xcode 的 Mac,Android 則可從 macOS、Linux 或 Windows 使用 Android SDK(安裝)。
Rust 應用程式(其 nsh.json 指定 "root" 組件)在其目錄中運行和打包:nsh 會為目標平台建構應用程式自己的 runner,包含你的 crate,不含 QuickJS。TSX 應用程式則需指定其入口:
# A Rust app, from its directory
nsh run --target macos # or ios, android, windows, linux, web, catalyst
nsh package --target macos # or ios, ios-simulator, android, windows, linux, web
# A TSX app
nsh run App.tsx --target macos # or ios, android, windows, linux, gpui, web, catalyst
nsh package App.tsx --target macos
每個桌面作業系統都使用自己的後端來打包應用程式,並且是為其運行的機器而建構:Mac 應用程式要在 Mac 上打包,Windows 應用程式要在 Windows 上打包,Linux 應用程式要在 Linux 上打包。打包涵蓋每個目標的輸出、簽署和圖示。
#嵌入原生應用程式
| 平台 | 宿主視圖 | 運行內容 | 發佈形式 | 宿主語言 |
|---|---|---|---|---|
| macOS | AppView(一個 NSView) |
Rust root、TSX bundle | nsh-platform-macos crate |
Rust |
| Windows | AppView(子視窗) |
Rust root、TSX bundle | nsh-platform-windows crate |
Rust |
| Linux | AppView(一個 GTK widget) |
Rust root、TSX bundle | nsh-platform-linux crate |
Rust |
| GPUI(macOS、Windows、Linux) | AppView(一個 GPUI 視圖) |
Rust root、TSX bundle、已簽署的套件 | nsh-platform-gpui crate |
Rust |
| iOS | AppView(一個 UIView) |
Rust root、TSX bundle | nsh-platform-ios crate |
Rust |
| iOS | NshAppView(一個 UIView) |
TSX bundle、已簽署的套件 | NativeShell.xcframework |
Swift |
| Android | NshView(一個 FrameLayout) |
TSX bundle、已簽署的套件 | nsh.aar |
Java 或 Kotlin |
| Web | createPlayer(一個頁面元素)或 iframe |
TSX bundle;Rust 應用程式則為其發佈的網站 | 來自 nsh package --target web 的運行時檔案 |
JavaScript 或 HTML |
Rust 宿主會把 Rust root(Root::of::<App>())掛載為 AppSource::Rust 或 App::Rust,並透過 set_ui_sets 為其引擎提供應用程式的 UI 集合,讓它載入的 TSX 可以匯入應用程式自己的組件和模組。每個視圖是一個引擎,而一個應用程式可以顯示多個視圖。宿主會在每個平台上註冊自己的原生視圖供應用程式使用(原生組件)。
#為所有平台編寫
應用程式大部分內容都不需要平台檢查:元素、樣式、佈局和模組在各處的行為都相同。當平台之間有差異時,NSH 為這些差異提供了單一位置(平台專屬程式碼):
- 環境會告知應用程式在哪裏運行:在 Rust 中是
os()、idiom()和is_desktop()(nsh_core::prelude),在 TSX 中是來自nsh的platform.os、platform.idiom、platform.desktop和platform.select。作業系統為 iOS、Android、macOS、Windows、Linux 或網頁。 - 平台專屬的 props 在 TSX 中放在
ios={{…}}、android={{…}}、web={{…}}或desktop={{…}}內(desktop:macOS、Windows、Linux 和桌面瀏覽器)。 - 輸入遵循平台。 手機使用觸控和軟鍵盤;桌面使用滑鼠、觸控板和鍵盤。
應用程式的組件是以 NSH 的 primitive 繪製的,因此既可在桌面上運行,也可在手機上運行,並且兩者外觀一致。桌面佈局由這些 primitive(view、text、scroll、virtual_list 等;在 TSX 中是 View、Text、Scroll、List 等)加上桌面檢查構成,圖庫(gallery)的桌面佈局就是這樣做的:
use nsh_core::prelude::*;
#[component]
pub fn Shell() -> impl IntoElement {
// macOS, Windows, Linux and desktop browsers get a sidebar; phones get tabs.
view()
.flex_1()
.child(if is_desktop() { sidebar_layout() } else { tab_layout() })
}
fn sidebar_layout() -> AnyElement {
view().flex_1().flex_row().into_element()
}
fn tab_layout() -> AnyElement {
view().flex_1().flex_col().into_element()
}import { View } from "nsh";
import { platform } from "nsh";
export function Shell() {
// macOS, Windows, Linux and desktop browsers get a sidebar; phones get tabs.
return <View class="flex-1">{platform.desktop ? <SidebarLayout /> : <TabLayout />}</View>;
}
function SidebarLayout() {
return <View class="flex-1 flex-row" />;
}
function TabLayout() {
return <View class="flex-1 flex-col" />;
}每個後端都列出了它以自己方式處理的內容:macOS、Windows、Linux、GPUI。
#另請參閱
- 入門:第一個應用程式,在各平台上運行。
- 使用 Rust 建構:以 Rust 編寫的應用程式,以及 Rust 搭配 TSX。
- 安裝:各平台所需的工具。
- 架構:哪些部分是共用的,以及各後端負責甚麼。