Lynx Explorer 完整指南:跨平台容器集成、Node-API 实验能力与 ReactLynx 页面开发
2026/9/14 21:32:35 网站建设 项目流程

Lynx Explorer 完整指南:跨平台容器集成、Node-API 实验能力与 ReactLynx 页面开发

【免费下载链接】lynxEmpower the Web community and invite more to build across platforms.项目地址: https://gitcode.com/GitHub_Trending/lynx10/lynx

Lynx Explorer 是 Lynx 官方用于测试和探索的示例应用,同时覆盖 Android、iOS、Harmony、Windows 与 macOS 五类原生平台容器,以及运行在其中的 ReactLynx 页面工程。本文以 explorer/README.md 为骨架,结合仓库内的 Sparkling 容器文档、Node-API 实验能力文档与各平台构建指南,系统梳理 Explorer 的目录结构、构建方法、路由架构与实验能力,帮助你快速基于该仓库搭建自己的 Lynx 集成环境。

读完本文,你将掌握:Lynx Explorer 两大组成部分(原生应用 + ReactLynx Web 应用)的职责边界、五个平台从源码构建的完整命令链路、Lynx Node-API Addons 实验能力的接入模型与页面侧调用方式,以及 iOS/Android 上 Sparkling 容器的路由协调器(RouteCoordinator)设计。

认识 Lynx Explorer:一个仓库、两类工程

Lynx Explorer 是 Lynx 官方的测试与探索应用,也是 Lynx 快速开始指南(Quick Start Guide)中推荐体验 Lynx 的入口。整个explorer目录由两个主要部分组成:

  1. 原生应用(Native applications):提供运行时环境的客户端,分布在android/darwin/(iOS 与 macOS)、harmony/windows/等平台目录下,负责承载 Lynx 运行时的加载与宿主能力(资源加载、原生模块、路由、调试等)。
  2. ReactLynx 网页应用(ReactLynx-based web applications):以 ReactLynx 实现的页面工程,运行在原生应用内部,目前包含两个页面(screen):homepage/showcase/

此外,docs/目录沉淀了三份关键设计文档:Android 与 iOS 的 Sparkling 容器集成说明(android-sparkling-container.md、ios-sparkling-container.md),以及 Lynx Node-API Addons 实验能力说明(lynx-node-api.md)。cpp/目录存放跨平台共享的原生加载器,sparkling-source.json则固定了 Sparkling 上游源码的版本。

从源码构建原生应用

如果你希望从源码构建并运行这些原生应用,请参考各平台目录下的独立指南。下面按平台给出环境要求与关键命令摘要,完整步骤请跳转对应文档。

Android

构建前需要准备 JDK 11、Android 开发环境与 Python(>= 3.9),并至少预留 100GB 磁盘空间。JDK 11 可通过 Homebrew(macOS 的zulu@11)、apt/yum(Linux 的openjdk-11-jdk)或 winget(Windows 的ojdkbuild.openjdk.11.jdk)安装,随后配置JAVA_HOMEPATHANDROID_HOME指向 Android SDK 路径(可用仓库内的 prepare_android_build.py 辅助安装)。

拉取代码后在仓库根目录执行依赖同步与环境初始化:

cd lynx source tools/envsetup.sh tools/hab sync . python3 tools/android_tools/prepare_android_build.py

构建方式有两种:

  • Android Studio:用 Android Studio 打开explorer/android目录,将 Gradle 的 JDK 指向步骤中安装的 JDK 11,触发 Gradle sync 后选择LynxExplorer模块点击 Run。
  • 命令行:在explorer/android目录执行:
./gradlew :LynxExplorer:assembleNoAsanDebug --no-daemon adb install lynx_explorer/build/outputs/apk/noasan/debug/LynxExplorer-noasan-debug.apk

上述命令会生成LynxExplorer-noasan-debug.apk并通过adb安装到设备。完整步骤见 Android Build Guide。

iOS

iOS 构建要求 Xcode(>= 15.0)、CocoaPods(>= 1.11.3)、Ruby(>= 2.7 且 < 3.4)与 Python(>= 3.9),磁盘空间同样建议 100GB 以上。Python 侧需要安装pyyaml以执行自动生成逻辑:

python3 -m venv venv source venv/bin/activate pip3 install pyyaml

在仓库根目录完成依赖同步(tools/hab sync .source tools/envsetup.sh)后,安装 iOS 工程依赖:

cd explorer/darwin/ios/lynx_explorer ./bundle_install.sh

该脚本会生成LynxExplorer.xcworkspace,用 Xcode 打开并选择LynxExplorer执行构建即可。若要在真机运行,需要在 Signing & Capabilities 中选择 Personal Team,并把 Bundle Identifier 从com.lynx.LynxExplorer改为唯一标识。详见 iOS Build Guide。

Harmony

Harmony 构建要求 DevEco Studio 5.0.13.200 或更高版本,以及 Git、Python(>= 3.9)。配置环境变量HARMONY_HOME指向 DevEco SDK 目录,并确保hdc(位于$HARMONY_HOME/default/openharmony/toolchains/hdc)与ohpm(位于 DevEco Studio 的tools/ohpm/bin)在PATH中。

安装 Harmony 依赖后通过脚本构建:

pushd platform/harmony && ohpm install && popd pushd explorer/harmony && ohpm install && popd python3 explorer/harmony/script/build.py --debug --dev --build_lynx_core --build_bundle --build_hap hdc install explorer/harmony/lynx_explorer/build/default/outputs/default/lynx_explorer-default-unsigned.hap

其中--build_lynx_core--build_bundle--build_hap分别控制构建 Lynx 核心库、页面 bundle 与最终 HAP;去掉--debug构建 release 版本。也可以直接用 DevEco Studio 打开explorer/harmony目录运行,若后续构建想跳过 Lynx 核心库与 bundle,可在 BuildMode 中选择skipBundle。详见 Harmony Build Guide。

Windows

Windows 构建要求 Visual Studio(推荐 2022)、Git、Python(>= 3.9)与 Node.js(>= 18),并设置三个环境变量:

DEPOT_TOOLS_WIN_TOOLCHAIN=0 GYP_MSVS_OVERRIDE_PATH="C:\Program Files (x86)\Microsoft Visual Studio\2022\Community" WINDOWSSDKDIR="C:\Program Files (x86)\Windows Kits\10"

Windows 侧使用 GN/Ninja 构建,默认 JS 引擎为 QuickJS,可通过jsengine_type切换为 V8:

cd lynx .\tools\envsetup.ps1 .\tools\hab.ps1 sync . --target clay $PSNativeCommandArgumentPassing = 'Legacy' .\buildtools\gn\gn.exe gen out\Default --args='desktop_enable_embedder_layer = true enable_clay_standalone = true disable_visibility_hidden = true use_ndk_static_cxx = false enable_linker_map = false enable_clay = true is_headless = true skia_enable_flutter_defines = true skia_use_dng_sdk = false skia_use_sfntly = false skia_enable_pdf = false skia_enable_svg = true enable_svg = true skia_enable_skottie = true skia_use_x11 = false skia_use_wuffs = true skia_use_expat = true skia_use_fontconfig = false clay_enable_skshaper = true skia_use_icu = true allow_deprecated_api_calls = true stripped_symbols = true is_official_build = true enable_lto = false is_clang = true enable_lepusng_worklet = true enable_napi_binding = true enable_inspector = true enable_libcpp_abi_namespace_cr = true jsengine_type = "quickjs"' --ide=vs .\buildtools\ninja\ninja.exe -C out\Default explorer

构建完成后lynx_explorer.exe位于out\Default\lynx_explorer目录;加--ide=vs参数会生成all.sln,可用 Visual Studio 将lynx_explorer设为启动项目进行调试。需要生成 SDK 包时可执行ninja -C out\Default platform\windows:package_sdk,产物为lynx_sdk_windows_${target_cpu}.zip。详见 Windows Build Guide。

macOS

macOS 构建要求 Xcode(>= 15.0)、Git、Python(>= 3.9)与 Node.js(>= 18)。在仓库根完成source tools/envsetup.shtools/hab sync . --target clay后:

buildtools/gn/gn gen out/Default --args='desktop_enable_embedder_layer = true enable_clay_standalone = true disable_visibility_hidden = true use_ndk_static_cxx = false enable_linker_map = false enable_clay = true is_headless = true skia_enable_flutter_defines = true skia_use_dng_sdk = false skia_use_sfntly = false skia_enable_pdf = false skia_enable_svg = true enable_svg = true skia_enable_skottie = true skia_use_x11 = false skia_use_wuffs = true skia_use_expat = true skia_use_fontconfig = false clay_enable_skshaper = true skia_use_icu = true skia_gl_standard = "" skia_use_metal = true shell_enable_metal = true allow_deprecated_api_calls = true stripped_symbols = true is_official_build = true use_clang_static_analyzer = false enable_lto = false enable_lepusng_worklet = true enable_napi_binding = true enable_inspector = true jsengine_type="quickjs" use_flutter_cxx = false is_debug = false use_primjs_napi=true use_weak_suffix_napi=true' --ide=xcode buildtools/ninja/ninja -C out/Default explorer

完成后LynxExplorer.app位于out/Default目录;--ide=xcode会生成all.xcodeproj便于在 Xcode 中调试。SDK 包命令为ninja -C out/Default platform/darwin/macos:package_sdk。详见 macOS Build Guide。

实验能力:向 Lynx 页面暴露 Node-API Addons

Lynx 提供了一项实验性的运行时集成能力,允许宿主应用(host app)把 Node-API addon 暴露给 Lynx 页面。开源 Explorer 应用内置了一个名为LynxNodeAPIModule的示例模块和一个共享的原生加载器,用来演示宿主侧的一种集成模式。完整设计见 Lynx Node-API Addons。

能力模型:Lynx 侧基础与宿主自定义边界

Lynx 本身并不规定唯一的 addon 加载器实现。在开源 Explorer 示例中,宿主侧集成由LynxNodeAPIModule与共享原生加载器(位于 explorer/cpp/LynxNodeAPI.cc)共同完成。

属于 Lynx 侧集成基础(集成方需要具备)的能力包括:

  • 宿主可以获取一个运行时相关的napi_env
  • 宿主可以向 Lynx 页面暴露一个或多个模块;
  • 宿主可以选择把 Node-API addon 加载器接入该模块边界。

属于宿主自定义(集成时由你决定)的部分包括:

  • 页面可见的模块名,如LynxNodeAPI
  • 页面可见的方法名,如requireNodeAddon()
  • addon 二进制的集成方式,例如 Android/Harmony/Windows 的动态加载,或 Apple 平台的静态注册 + 生成的addon_use.h引用;
  • 用于 addon 初始化的导出符号;
  • addon 导出内容发布到 JS 的位置,如__lynx_node_addon_exports__

示例实现的加载流程如下:

  1. Lynx 页面按名称请求某个 addon;
  2. 宿主侧示例加载器通过平台集成策略解析该 addon;
  3. Android/Harmony/Windows 使用动态库加载;iOS/macOS 将 addon 链接进宿主应用,并一次性包含生成的addon_use.h头文件,使NAPI_USE保留 addon 的静态注册入口;
  4. addon 通过标准 Node-API 注册入口完成初始化;
  5. 示例加载器把 addon 的导出内容发布到 JS 全局对象__lynx_node_addon_exports__上。

需要强调的是,文档明确提示动态加载策略仅用于演示/实验用途。生产集成中应避免依赖默认库搜索路径(尤其是 Windows),并限制可加载的内容:对addonName做 allowlist 校验和/或要求固定的基目录;解析到该目录下的绝对规范路径;加载失败时返回可操作的诊断信息。Windows 上更推荐使用受限搜索行为的LoadLibraryExW,而不是默认的 DLL 搜索顺序。

集成前提

接入该能力需要以Lynx 3.9.x SDK为基线,各平台的运行时依赖如下:

平台运行时依赖
Android集成 PrimJS 3.9.x 运行时,并打包配套的libnapi_adapter.so
Harmony集成 PrimJS 3.9.x 运行时,并打包配套的libnapi_adapter.so
iOS集成 PrimJS 3.9.x 运行时,并添加最新兼容的LynxWeakNodeAPI

iOS 还有两项额外要求:

  • 必须在应用启动时(在 Explorer 环境使用之前)一次性安装 PrimJS 到LynxWeakNodeAPI的桥接;
  • 通过 CocoaPods 同时集成PrimJSLynxWeakNodeAPI的宿主,需要启用generate_multiple_pod_projects,使两者同名但不同的 Node-API 头文件隔离在各自 target 的 header maps 中。

当前开源 Explorer 示例中这些前提的实现位置分别是:Android PrimJS 运行时依赖在 platform/android/lynx_android/build.gradle;Harmony PrimJS 依赖在 explorer/harmony/oh-package.json5;iOS 的 PrimJS 与LynxWeakNodeAPI依赖在 explorer/darwin/ios/lynx_explorer/Podfile;iOS 运行时桥接安装见 explorer/darwin/ios/lynx_explorer/LynxExplorer/AppDelegate.mm。

示例模块与页面侧用法

LynxNodeAPIModule是集成示例代码,而非能力定义本身。它演示了宿主应用如何:接受页面加载 addon 的请求、把请求从平台 UI/运行时层桥接到原生代码、绑定运行时相关的napi_env、调用共享 addon 加载器、把得到的导出暴露回页面 JS 环境。

示例模块只暴露一个宿主方法:

requireNodeAddon(addonName)

addonName需要匹配库的 basename,并且去掉平台可能使用的lib前缀、文件扩展名或 framework bundle 路径组件。各平台对照示例:

  • Android 共享库libsample.so->requireNodeAddon("sample")
  • Harmony 共享库libsample.so->requireNodeAddon("sample")
  • iOS 静态链接且注册名为sample的 addon ->requireNodeAddon("sample")
  • macOS 静态链接且注册名为sample的 addon ->requireNodeAddon("sample")
  • Windows addon 二进制sample.nodesample.dll->requireNodeAddon("sample")

在当前 Explorer 示例中,Node-API 集成仅在页面 URL query 包含enable_napi_addon=1(或enable_napi_addon=true)时启用,例如:

file://lynx?local://homepage.lynx.bundle?enable_napi_addon=1

各平台示例文件索引

共享原生加载器位于 explorer/cpp/LynxNodeAPI.h 与 explorer/cpp/LynxNodeAPI.cc。各平台注册模块、绑定运行时环境与桥接的示例文件:

  • Android:注册模块与绑定运行时环境的入口见 explorer/android/lynx_explorer/src/main/java/com/lynx/explorer/modules/LynxNodeAPIModule.java、LynxModuleAdapter.java 与 LynxViewShellActivity.java。
  • iOS:注册模块、安装运行时桥接并绑定napi_env的文件见 AppDelegate.mm、LynxNodeAPIModule.h、LynxNodeAPIModule.mm、LynxNodeAPILifecycleListener.mm 与 LynxViewShellViewController.m。
  • Harmony:注册模块、管理 sendable token 并桥接原生代码的文件见 LynxNodeAPIModule.ets、Lynx.ets、lynx_node_api_napi.cpp 与 CMakeLists.txt。
  • macOS:在 embedder builder 中注册模块、通过运行时生命周期观察者绑定napi_env并复用共享加载器,见 ViewController.mm、LynxNodeAPIModule.h、LynxNodeAPIModule.mm 与 ExampleLynxRuntimeLifecycleObserver.mm。
  • Windows:同样在 embedder builder 注册模块,通过运行时生命周期观察者绑定napi_env,见 lynx_window.cc、lynx_node_api_module.h、lynx_node_api_module.cc 与 example_lynx_runtime_lifecycle_observer.cc。

平台集成与 addon 构建

示例加载器要求 addon 按平台的打包模型集成:Apple 平台偏好静态集成,Android/Harmony/Windows 继续使用动态 addon 二进制。各平台的精确集成步骤见:

  • Android 打包与 ABI 说明:explorer/android/lynx-napi-addon.md
  • iOS 静态库podspecxcframework集成:explorer/darwin/ios/lynx-napi-addon.md
  • Harmonyhar集成:explorer/harmony/lynx-napi-addon.md
  • macOS 静态库集成:explorer/darwin/macos/lynx-napi-addon.md
  • Windows 应用本地打包:explorer/windows/lynx-napi-addon.md

如果你要编写面向 Lynx 的 Node-API addon 本身,应使用最新的@lynx-js/weak-node-api并遵循其文档中的头文件、注册宏、导出符号与构建配置;Explorer 的文档只覆盖如何把构建好的 addon 集成进各宿主应用,并不是 addon 二进制创作的权威指南。该能力与示例集成目前仍属实验性质,库命名、打包规则与宿主集成细节未来可能继续演进。

在 iOS/Android Explorer 中集成 Sparkling 容器

Sparkling 是 Lynx 生态中提供独立容器能力的运行时方案。Explorer 将其作为可选的容器扩展集成:Lynx 依然是通用运行时,Sparkling 作为可选 flavor/模式扩展存在。

iOS:显式的全量 Sparkling 启动路径

第一阶段的目标是给 iOS Explorer 增加显式的全量 Sparkling 启动路径,同时不改变普通 bundle 的默认容器。一个RouteCoordinator把每次 URL 入口解析为不可变的LaunchDescriptor,再呈现选定的容器。这是一次容器集成,而不是第二次 Lynx 集成——LynxLynxBaseLynxServiceAPILynxServiceLynxDevtoolBaseDevtoolXElement等组件在两种构建模式下都继续来自当前 Lynx checkout;生成的LynxLibraryRegistrypod 也是源码自有,来自 Explorer workspace 的generated/lynx-library

面向用户的路由契约

Explorer 保持通用首页,把 Sparkling Go 作为可选扩展。普通打开动作与扩展路由有刻意不同的语义:

  • Open保持 raw 与 Legacy bundle URL 落在 Legacy Explorer 容器上;
  • Sparkling Go只在宿主宣告具备 Sparkling 容器能力时显示为一个紧凑的扩展行,其内嵌根 bundle 请求完整 Sparkling 容器,并打开后续 Sparkling 页面。

无论请求来自首页、扫码器、应用代理还是页内 router,协调器都应用如下规则:

输入Open显式 Sparkling 路由
Raw HTTP/HTTPS bundleLegacySparkling
file://lynx?local://bundle_pathbundleLegacySparkling
lynx://open?url=encoded_url(包裹 raw 或 Legacy bundle)解包后 Legacy语义映射后 Sparkling
hybrid://lynxview_page?bundle=bundle_pathSparklingSparkling
hybrid://lynxview_page?url=encoded_urlSparklingSparkling
包裹规范 Sparkling scheme 的 Legacy wrapper解包后 Sparkling解包后 Sparkling
Recorder URLRecorder/Legacyrecorder_unsupported_in_sparkling
畸形或不支持的 URL类型化路由错误类型化路由错误

规范 Sparkling scheme 永远拥有自己的请求,普通Open无法把它强行转回 Legacy;反过来,第一阶段也绝不会把 raw bundle 提升为 Sparkling,除非调用方显式进入Sparkling Go或从 QR 扫码器选择 Sparkling。官方 Sparkling 解析器负责校验规范 scheme,wrapper 解包有界且通常只要求一个url目标;解析器还保留了 Explorer 历史遗留的未转义url=https://...&...尾部形式,此时剩余 query 归属于该唯一目标,原始编码 URL 会被保留。

LaunchDescriptor:URL 语法与容器创建之间的 SDK 中立边界

LaunchDescriptor记录的内容包括:原始输入(以及适用时的原始规范 scheme);本地 bundle、远端 bundle 或 Recorder 资源;初始数据、common props、page props、page name 与无损 query 项;viewport、背景/透明、导航与呈现选项;缓存与 Node-API/调试选项;请求与解析后的容器类型以及路由来源。

Legacy 启动器是唯一把类型化模型转回既有 string-keyed Explorer 参数的地方;Sparkling 启动器则构建SPKContext并调用SPKRouter.create(withURL:context:frame:),绝不会用普通LynxView顶替。

Legacy 参数兼容映射

已知类型化参数采用 query 名称最后一次有值的出现;布尔参数保留 Legacy shell 的NSString.boolValue兼容语义;未识别值会保留在无损 query/extra 视图中,而不是让一个本来合法的 Legacy URL 失败。核心映射如下(完整行为见 ios-sparkling-container.md):

Legacy 参数解析类型/默认值Descriptor 字段无效值行为
animatedFoundation 兼容布尔,默认truepresentation.animated无值项不清除先前值;未识别拼写按NSString.boolValue处理并无损保留
hidden_navFoundation 兼容布尔,默认falsenavigation.navigationHidden未识别值按NSString.boolValue处理并无损保留
fullscreenFoundation 兼容布尔,默认falsenavigation.fullScreen、导航隐藏、透明外观未识别值按NSString.boolValue处理并无损保留
titleStringnavigation.title无值项保留但不提升为类型化字段,不擦除先前有值项
title_colorStringnavigation.titleColor以字符串保留;不支持的色值由渲染所有者忽略
bar_colorStringnavigation.barColor以字符串保留;不支持的色值由渲染所有者忽略
back_button_styleStringnavigation.backButtonStyle为向前兼容保留;活动容器决定支持哪些样式
width+height物理像素正整数(Int32 语义),两者必填viewport不完整/非正/超范围的一对会保持类型化 viewport 未设置,同时保留原始值
orientationportraitlandscapenavigation.orientation强制进入 Sparkling 的非规范路由返回sparkling_option_unsupported;原始值始终保留
enable_napi_addon窄 truthy 集(1/true/yes),默认falsedebugOptions.enableNAPIAddon其他值关闭 addon 并无损保留
initial_page透传字符串pageNameextrasqueryItemsinitialPageprop无额外类型化校验;无值项不提升为 prop
未知 query 键透传有序queryItemsextras与驼峰化 page props 取末值无类型化拒绝;重复编码项保持有序,字典视图末值胜出

解析器还识别container_bg_colortrans_status_bar。无法安全表示的字段会显式失败,而不是静默改变容器语义。规范参数hide_nav_barnav_bar_color优先于 Legacy 别名且与 query 顺序无关;hide_status_bar与 fullscreen 分开建模,规范页面可以只隐藏状态栏而不改变呈现模式。

全局 props 与能力标识

普通 Sparkling 页面 props 的优先级为:Explorer common props < Sparkling stable/container props < launch/page props。common 层不会遮蔽 SDK 的稳定 device/viewport/safe-area/URL/SPK_version/lynxSdkVersion/sparklingVersion/containerInitTime字段。以下身份/能力名在每个不可信边界被保留:containerIDcontainerTypeexplorerSupportsExplicitRouteOwnershipexplorerSupportsSparklingContainersparklingAvailablesparklingNavigationspkContainerIDspkPipe

完整 Sparkling 页面会收到 SDK 拥有的容器身份、MethodPipe、router、生命周期、稳定 props、Explorer 资源/图片提供者、XElement 注册、原生模块与本地 Lynx DevTool 配置。sparklingNavigation=true表示当前页面具备该能力,而不仅仅是应用二进制链接了 Sparkling;Legacy 页面不注册spkPipe也不宣告 Sparkling 导航。explorerSupportsExplicitRouteOwnership描述的是已安装的 iOS 协调器,在 Legacy-only 构建中依然为 true;独立的explorerSupportsSparklingContainer构建能力才控制首页是否提供Sparkling Go扩展。

失败契约与审计路由入口

一旦 descriptor 解析为 Sparkling:容器创建/呈现失败会返回给调用方;Sparkling 生命周期错误视图展示异步加载失败;router.open上报协调器的实际接受或类型化错误;router.close接受缺失/空目标作为当前容器、仅在 ID 匹配时接受非空 ID、拒绝未知 ID 且不会关闭其他页面;请求绝不会回退重试到 Legacy;Explorer 也绝不会创建普通LynxView作为 Sparkling 回退。最近历史只在路由被接受后更新;原生模块回调以稳定 code 和 message 恰好完成一次;router 服务条目在读取 UIKit 状态前把完整操作编组到主线程(包括 MethodPipe 请求当前线程执行时)。

所有当前 iOS 入口最终都终止于同一个协调器,包括lynx_initial_url环境值、UIApplicationLaunchOptionsURLKey冷启动自定义 URL、application:openURL:options:热路径、universal link、首页手动 Open/Sparkling Go、首页最近行、QR 扫码器、DebugBridge 本地/远端路由(分别采用replaceTopresetAndPush策略)、ExplorerModule.openSchema/openRoute/navigateBack以及 SparklingRouterService的 open/close。当前 iOS Explorer 没有 Scene manifest、SceneDelegatestartFromUrl入口;未来若新增此类入口也必须走同一个协调器,而不是再加一个 URL 解析器。Explorer 有意不声明通用的hybridURL scheme,第三方统一通过应用专属的lynx://open?url=...传输(或 universal link)包裹规范 Sparkling URL。

模拟器冒烟验收与依赖模式

scripts/run_sparkling_smoke.sh会安装一次选定应用,以lynx_initial_url启动,并通过真实 LaunchServices 路径用simctl openurl两次热投递 URL(一次畸形规范路由、一次有效规范路由),期间不重新启动或终止应用。脚本检查作用域进程日志中的初始容器、畸形路由的类型化失败与有效路由的预期结果,并要求本地 Lynx 版本非空、应用在两次热投递期间存活、拒绝新的重复类诊断、等待后确认失败路由没有回退到其他容器。+sparkling模式期望两次 Sparkling 成功且无 Legacy 成功;无+sparkling模式期望一次 Legacy 成功与显式的sparkling_unavailable失败。

Sparkling 是显式构建模式,默认不带+sparkling

cd explorer/darwin/ios/lynx_explorer ./bundle_install.sh --sparkling-mode disable_sparkling ./bundle_install.sh --sparkling-mode enable_sparkling

+sparkling模式会在忽略的 generated 目录中按固定 commit(上游 sparkling 仓库 PR #113 的头部修订)物化官方 sparkling 源码,并只从源码构建SparklingSparklingMacroSparklingMethodSparkling-Router这四个 pod(Sparkling-DebugToolSparkling-MediaSparkling-Storage不在第一阶段依赖图中);同时打包该固定 checkout 构建出的官方packages/playground/dist/*.lynx.bundle产物,入口 bundle 位于Resource/extensions/sparkling-go/main.lynx.bundle。本地构建+sparkling需要 Node 22 与 pnpm 10.26.0:

python3 ../../../scripts/sync_sparkling_source.py \ --manifest ../../../sparkling-source.json \ --source-root ../../../generated/sparkling-source pnpm --dir ../../../generated/sparkling-source install --frozen-lockfile pnpm --dir ../../../generated/sparkling-source --filter sparkling-playground build bash bundle_install.sh --sparkling-mode enable_sparkling

bundle_install.sh在 CocoaPods 安装后运行所有权校验器:检查 8 个源码自有 pod(LynxLibraryRegistry必须解析到generated/lynx-library,其余 7 个必须解析到当前 checkout 根),拒绝不匹配或脏的 pin、包管理器/CocoaPods 缓存目标、非本地源码自有 Lynx pod、被禁止的 Sparkling pod 或第二个 Lynx 所有者。

CI 构建矩阵

CI 与发布只构建会发布或被下游任务消费的产物:publish-release.yml发布四个 iOS Explorer 应用(arm64/x86_64 x 无+sparkling/+sparkling,全部为 Debug 模拟器),ci.ymlios-explorer-build构建同样的四个。之所以是四个而不是+sparkling吞并原始构建:路由以#if canImport(Sparkling)门控,无+sparkling构建是唯一类型检查#else分支的构建,也是仍在发布的原始.app;而ios-e2e-test只下载 arm64 Debug+sparkling构建,由于LegacyContainerLauncher无条件编译,单个+sparkling构建在运行时同样覆盖了 Legacy 路由路径。构建基线与上游 API 依赖(SPKHybridSchemeParam.buildLynxPageSchemeSPKContext.navigationBarBackHandlerSPKContext.interactivePopGestureDelegateSPKContext.failedViewBuilder)以及各移除条件详见 ios-sparkling-container.md。

Android:Sparkling 作为可选 flavor 扩展

Android Explorer 同样保持 Lynx 为通用运行时,并把 Sparkling 作为可选 flavor 扩展:withoutSparklingflavor 既不包含 Sparkling 运行时也不包含 Sparkling Go bundles;withSparklingflavor 打包基于explorer/sparkling-source.json中共享固定源构建的官方 playground bundles。

Android 侧的路由所有权与 iOS 设计同源:每个入口都调用RouteCoordinator,将输入一次性解析为不可变的LaunchDescriptor。Raw HTTP/HTTPS、assets://file://lynx?local://路由默认走 Lynx,除非调用方显式请求 Sparkling;规范的hybrid://lynxview_page路由永远属于 Sparkling(包括被lynx://open?url=...包裹时)。所有权解析为 Sparkling 后,启动错误作为类型化失败返回,绝不会回退重试 Lynx;Recorder 路由保持 Lynx-only;最近历史只在启动器接受路由后更新。Android 使用 Activity task stack 处理工具栏与系统返回:Sparkling 的router.open以显式 Sparkling 所有权重新进入协调器,router.close只在可选容器 ID 匹配时 finish 所属 Activity;router.open还会把options.extra转发给被启动的SparklingContext(键值规范化为字符串,并覆盖对应 URL query 参数),运行时自有属性保留给宿主。

能力与外观方面:Universal Home 会收到explorerSupportsExplicitRouteOwnership=true与由 flavor 推导的explorerSupportsSparklingContainer;普通 Lynx 页面是sparklingAvailable=falsesparklingNavigation=false,且没有 Sparkling 容器身份或 MethodPipe;Sparkling 页面从 SDK 获得这些值,只暴露 Explorer 安装的宿主模块。两种运行时读取同一份 Explorer 存储中的 Auto/Light/Dark 偏好,force_theme_style仍是页面级覆盖、不改应用偏好;两个启动器共用 Android 加载/错误界面(Sparkling 通过SparklingUIProvider获得)。注意已发布的SparklingUIProviderAPI 提供加载、错误与工具栏视图,但不暴露重试回调或容器生命周期钩子,因此 Explorer 呈现的是诚实的终端 Sparkling 错误视图,重试需要重新打开路由。

验证方式:在explorer/android下运行两个 flavor 的单元测试任务与verifySparklingAndroidRuntimeSmokeTestArtifacts(后者构建 Debug/Release 产物、检查依赖所有权、验证启用/禁用 APK 内容并编译两个插桩变体),设备插桩随后可运行各变体的SparklingRuntimeSmokeTest

开发内嵌的 ReactLynx 页面工程

如果你已经拥有构建好的 Lynx Explorer 应用(或任何其他集成 Lynx 的环境),可以聚焦开发运行在其中的 Lynx 页面。目前 Explorer 有两个页面:

  • homepage/:用 ReactLynx 实现的 Lynx Explorer 首页,是应用的入口页面。工程清单与脚本见 explorer/package.json。
  • showcase/:用 ReactLynx 实现的展示页,通过集成官方 Lynx 示例来演示各种 Lynx 特性与能力。

这两个页面工程由根级explorer/package.json统一驱动,使用 pnpm workspace 组织:

pnpm run build:homepage # pnpm --filter homepage build pnpm run build:showcase # pnpm --filter showcase build pnpm run build # 依次构建 homepage 与 showcase

页面构建产物(bundle)随后被打包进各平台原生应用,由原生容器加载运行。在原生侧可以看到这些页面工程被引用:例如 Harmony 构建脚本explorer/harmony/script/build.py--build_bundle选项、iOS+sparkling模式打包Resource/extensions/sparkling-go/main.lynx.bundle等,均体现了"原生容器 + ReactLynx 页面"的分工模式。

小结

Lynx Explorer 是一个结构清晰的参考实现,值得关注的设计要点包括:

  • 双工程结构:原生容器(Android/iOS/Harmony/Windows/macOS)与 ReactLynx 页面工程(homepage/showcase)解耦,页面 bundle 独立构建后嵌入原生应用;
  • 统一路由模型:iOS 与 Android 都通过RouteCoordinator+ 不可变LaunchDescriptor收敛所有入口,把 URL 语法与容器创建解耦,并显式区分 Lynx 与 Sparkling 的路由所有权;
  • 实验能力样板LynxNodeAPIModule+ 共享加载器(explorer/cpp/LynxNodeAPI.cc)演示了向 Lynx 页面暴露 Node-API addon 的宿主集成模式,并给出生产环境的安全注意事项;
  • 可验证的工程质量:iOS 的冒烟脚本、Android 的 flavor 验证任务、CI 的依赖所有权校验,共同保证路由契约、无回退语义与源码自有依赖不被破坏。

若要以该仓库为起点构建自己的 Lynx 宿主,建议按以下顺序展开:先阅读 explorer/README.md 掌握整体结构,再按目标平台进入对应的构建指南编译原生应用,随后参考 Node-API 与 Sparkling 文档评估扩展能力,最后基于 homepage/showcase 工程开发自己的 ReactLynx 页面。

【免费下载链接】lynxEmpower the Web community and invite more to build across platforms.项目地址: https://gitcode.com/GitHub_Trending/lynx10/lynx

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询