
Expo Linking API 演进全解析从 1.x 到 57.x 的深度链接能力变迁与实现原理【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo导读本文以packages/expo-linking/CHANGELOG.md为骨架结合expo-linking包的 TypeScript/Android(Kotlin)/iOS(Swift) 源码系统梳理 Expo 深度链接Deep LinkingAPI 从 2020 年到 2026 年的完整演进脉络。你将掌握createURL/parse/getLinkingURL/useLinkingURL等核心 API 的设计动机、破坏性变更背后的原因以及从 manifest 时代走向 bundle-origin 时代、从自定义解析走向标准URL解析的底层原理。一、版本脉络一份 CHANGELOG 背后的工程节奏expo-linking的 CHANGELOG.md 记录了从 1.0.22020-05到 57.0.42026-07的全部版本信息其版本节奏呈现出两个明显阶段。1.1 版本号跳跃SDK 捆绑时代与独立发版时代2020–2025 早期1.x → 8.x版本号与 Expo SDK 解耦跟随模块自身的迭代节奏如 3.0.02021-12、4.0.02023-02、6.0.02023-08、7.0.02024-10、8.0.02025-08。2025 年末至今55.x → 57.x进入 SDK 版本同步时代55.0.02026-01、56.0.02026-05、57.0.02026-06版本号直接与 Expo SDK 对齐。这一转变意味着expo-linking已完全纳入 Expo SDK 的统一版本管理开发者通过npx expo install expo-linking安装时会由 CLI 自动解析与当前 SDK 匹配的版本见 README.md 的安装说明。1.2 大量无用户可见变更版本的含义CHANGELOG 中超过一半的版本标注为This version does not introduce any user-facing changes.如 56.0.1 至 56.0.14、57.0.1 至 57.0.4。这并非发布空包而是说明这些版本仅包含内部重构、依赖升级或与 SDK 其他模块的同步适配。对于库的使用者而言这类版本通常可以安全升级无需修改业务代码——这本身就是一种重要的版本管理信号。二、核心 API 的演进从useURL到useLinkingURLCHANGELOG 中最清晰的演进主线是获取初始深度链接 URL这一 API 的三代更迭它直接反映了 expo-linking 从包装 React Native Linking到自建原生模块的架构转型。2.1 第一代useUrl已移除3.0.02021-12移除了已废弃的useUrl方法PR #15226当时的命名大小写不符合 React Hooks 的useXxx惯例。2.2 第二代useURL已废弃2.2.12021-03以useURL替代useUrl。在 7.1.62025-07中官方正式标记useURL为deprecated推荐迁移到useLinkingURLPR #37005。从 Linking.ts 源码可以看到useURL的实现仍基于 React Native 的Linking事件流export function useURL(): string | null { const [url, setLink] useStatestring | null(null); function onChange(event: { url: string }) { setLink(event.url); } useEffect(() { getInitialURL().then((url) setLink(url)); const subscription addEventListener(url, onChange); return () subscription.remove(); }, []); return url; }2.3 第三代useLinkingURL与原生getLinkingURL7.0.02024-10新增原生getLinkingURL函数PR #294057.1.6 正式推出useLinkingURLhook。这一代 API 的关键区别在于数据源从 JS 桥接层下沉到了原生模块// Linking.ts export function useLinkingURL(): string | null { const [url, setLink] useStatestring | null(ExpoLinking.getLinkingURL); function onChange(event: { url: string }) { setLink(event.url); } useEffect(() { const subscription ExpoLinking.addListener(onURLReceived, onChange as any); return () subscription.remove(); }, []); return url ?? null; }useLinkingURL的初始值直接同步取自ExpoLinking.getLinkingURL()而不是异步的getInitialURL()。这意味着首次渲染时即可拿到初始 URL不再需要等待 Promise避免了useURL在首帧返回null导致的闪烁或竞态问题。从原生侧看ExpoLinkingModule.kt 以静态initialURL缓存启动 URL并通过onURLReceived事件向 JS 推送后续 URL 变化iOS 侧 ExpoLinkingModule.swift 则通过ExpoLinkingRegistry.shared.initialURL与NotificationCenter实现同等能力。Android 与 iOS 的事件订阅分别使用OnStartObserving/OnStopObserving管理生命周期这与 JS 侧addListener/remove的配对调用严格对应。2.4 新增clearInitialURL56.0.132026-05新增Linking.clearInitialURL()PR #46265用于重置缓存的初始深度链接 URL。调用后getLinkingURL()在收到新链接前将一直返回null。其实现非常简洁export function clearInitialURL(): void { ExpoLinking.clearInitialURL?.(); }原生侧对应 Kotlin 的initialURL null与 Swift 的ExpoLinkingRegistry.shared.initialURL nil。在 Web 端该函数为 no-op见 ExpoLinking.web.ts因为浏览器 URL 本身无法被清除。这一 API 的典型场景是应用处理完一次深链后主动清空避免用户下次冷启动时重复处理同一条已消费的链接。三、事件监听 API 的规范化3.1addEventListener返回订阅对象3.1.02022-04起addEventListener不再返回void而是返回EmitterSubscriptionPR #17014使调用方可以通过subscription.remove()显式取消监听。同一版本还收紧了类型签名addEventListener与removeEventListener的type参数从宽泛的string收紧为字面量类型url从类型层面杜绝了传错事件名的可能。3.2removeEventListener的移除与崩溃修复3.3.12023-02修复了调用Linking.removeEventListener时的崩溃并增加警告该方法已从 React Native 中移除。4.0.02023-02正式删除已废弃的Linking.removeEventListenerPR #20832。当前代码库已完全采用订阅式清理例如useURL的 effect 清理函数为() subscription.remove()useLinkingURL同理。四、createURL与parse构造与解析的修复史createURL与parse是 expo-linking 使用频率最高的两个工具函数其演进历史集中体现了深度链接 URL 处理的种种边界问题。4.1createURL的职责与参数从 createURL.ts 源码可见createURL(path, options)支持三个可选参数参数类型默认值说明schemestring自动解析URI 协议如myapp://不传时从 app config 解析queryParamsRecordstring, undefined \| string \| string[]{}转为查询字符串的对象isTripleSlashedbooleanfalse是否生成scheme:///path三重斜杠形式其输出形态随运行环境变化同见源码注释开发/生产构建scheme://pathWeb 开发https://localhost:19006/pathWeb 生产https://myapp.com/pathExpo Go 开发exp://128.0.0.1:8081/--/path值得注意的细节当传入queryParams时源码会先剥离其中的null/undefined值再序列化For legacy purposes, well strip out the nullish values避免生成?keyundefined这类脏查询串同时hostUri中已携带的查询参数如release-channel会被合并进最终的查询串。4.2 双重编码修复7.1.57.1.52025-05修复了createURL双重复编码 URI 参数的问题PR #36704。源码中的修复策略是分层编码先用URLSearchParams.stringify编码查询参数再对 URL 其余部分调用encodeURIconst encodedURI encodeURI(${resolvedScheme}:${isTripleSlashed ? / : }/${hostUri}${path}); return ${encodedURI}${queryString};encodeURI不会重复编码%等已编码字符从而避免了历史上?、、被二次编码导致的参数解析失败。4.3 Web URL 中符号的解析修复6.1.06.1.02023-09修复了解析 pathname 中含的 Web URL的问题PR #24300。在 parse 实现 中路径解析逻辑会区分两种前缀若当前为 Expo 托管环境且无自定义 scheme尝试剥离hostUriStripped计算出的expoPrefix形如--/前缀否则若路径中存在则取之后的子串作为真实路径。这个分支正是为了兼容 Expo Go 场景下exp://host/--/path与 Web 场景https://host/path两种完全不同的 URL 形态。4.4 从 manifesthostUri到 bundle-originUnpublishedCHANGELOG 未发布版本区记录了最新一轮内部重构PR #48275、#48278Bug fix改为从bundle URL 的 authority构造开发深度链接而非 manifest 的hostUriInternal开发服务器地址统一从expo/internal/bundle-origin读取不再自行实现访问器。对应源码正是 createURL.ts 中的getDevServerLocationfunction getDevServerLocation(): { authority: string; isSecure: boolean } | null { if (!Constants.expoGoConfig?.developer) { return null; } const bundleOrigin getBundleOrigin(); if (!bundleOrigin) { return null; } const { host, protocol } new URL(bundleOrigin); return { authority: host, isSecure: protocol https: }; }这一变化的意义在于hostUri来自 manifest 元数据可能滞后或不准确而 bundle URL 是 JS 包实际加载的来源天然精确。同时当开发服务器为 HTTPS 时源码会将expscheme 自动升级为exps因为 Expo Go 将exps映射为 HTTPS保证深链与服务器协议一致。五、Scheme 解析深链的灵魂createURL不传scheme时解析逻辑由 Schemes.ts 的resolveScheme承担其优先级为用户显式传入的scheme开发模式下会校验其是否出现在 app config 的 scheme 列表中否则警告未传入时按Constants.expoConfig即expo.scheme→ 平台级expo.ios.scheme/expo.android.scheme→ 原生应用标识ios.bundleIdentifier/android.package的次序收集候选 scheme见collectManifestSchemes在 Expo GoStoreClient执行环境中仅接受白名单EXPO_CLIENT_SCHEMESiOS 为exp、exps及若干第三方回调 schemeAndroid 为exp、exps否则回退为exp若配置中没有任何 scheme开发模式告警、生产模式直接抛错Cannot make a deep link into a standalone app with no custom scheme defined。CHANGELOG 中与 scheme 相关的重要节点1.0.22020-05改进bare workflow 还是 managed workflow的判定机制PR #10993这是hasCustomScheme中按ExecutionEnvironmentBare / Standalone / StoreClient分流的雏形1.0.52020-10修复 bare workflow 下Constants.manifest未定义导致的崩溃4.1.02023-05对Constants.manifest的使用发出警告PR #22247推动迁移到expoConfig2.2.22021-04为 scheme 解析增加跳过警告的内部能力PR #12464即resolveScheme中的isSilent选项2.1.12021-01新增 bare workflow 支持且createURL在 bare 模式下生成双斜杠 URLPR #11560、#11702。六、平台支持与运行环境扩展6.1 从移动端到桌面端与电视端版本平台变更7.0.02024-10iOS 部署目标提升至 15.1PR #308407.0.12024-10podspec 加入 tvOS 支持PR #322557.1.02025-04新增macOS支持PR #35064同步迁移到 expo-modules gradle pluginPR #34176与统一平台语法的expo-module.config.jsonPR #3444556.0.02026-05最低 iOS/tvOS 版本提升至16.4、macOS 至13.4PR #43296当前仓库的expo-module.config.json、ExpoLinking.podspec 与spm.config.json即为这一系列平台扩展的最终产物。6.2 App Clips 支持7.0.57.0.52025-01为 iOSApp Clips提供基础支持PR #34327。App Clips 是 iOS 14 引入的轻量应用片段其深链处理与完整应用不同这一支持让expo-linking可以在 App Clip 场景下正确注册和响应 URL 事件。6.3 React Server 环境 shims7.0.07.0.0 为react-server环境添加 shimsPR #31622。对应实现可见 Linking.server.ts在服务器渲染环境中addEventListener返回空订阅、openURL直接返回true、getInitialURL返回空串所有原生相关 API 抛出UnavailabilityError从而保证在 RSCReact Server Components下模块可安全导入而不崩溃。配套的__rsc_tests__/Linking.test.ts即用于验证该场景。6.4 冷启动 universal link 修复7.1.77.1.72025-07修复了iOS 通过 universal link 冷启动失败的问题PR #37647。这属于 iOS 侧LinkingAppDelegateSubscriber与ExpoLinkingRegistry在应用启动时序上的注册问题——深链 URL 在 AppDelegate 回调到达时若尚未注册监听URL 便会丢失该修复确保了冷启动场景下初始 URL 能被正确缓存并通过getLinkingURL取回。七、内部架构演进走向标准URL与现代模块体系7.1 原生侧全面采用标准 URL6.2.06.2.02023-11将原生侧的解析迁移到标准URL支持PR #24941。在此之后JS 侧parse的实现直接依赖 WHATWGURL构造器const parsed new URL(url); parsed.searchParams.forEach((value, key) { queryParams[key] decodeURIComponent(value); }); path parsed.pathname || null; hostname parsed.hostname || null; scheme parsed.protocol || null;解析失败非法 URL时回退为将整个字符串视为 path。相比历史上基于正则的手写解析器标准URL带来了更可靠的主机名/协议/查询参数拆分也为URLSearchParams的正确编码行为奠定了基础。7.2 模块工程化expo-modules 体系CHANGELOG 中的多项 Others 条目反映了工程体系升级3.2.22022-07修复isExpoHosted对新 manifest 格式的适配PR #17402以及 Web 端addEventListener不返回订阅的问题PR #179257.0.02024-10Babel 配置统一迁移到expo-module-scriptsPR #31915并补齐react/react-native的 peerDependenciesPR #304737.1.02025-04Android 侧启用 expo-modules gradle pluginUnpublished#48278开发服务器地址读取统一收敛到expo/internal/bundle-origin消除重复实现。结合 ExpoLinking.ts 可知当前原生模块通过requireNativeModule(ExpoLinking)获取模块接口声明为getLinkingURL(): string | null与clearInitialURL(): void事件为onURLReceived(url: string): void——这正是 7.0.0 引入原生getLinkingURL、56.0.13 引入clearInitialURL后沉淀下来的稳定契约。7.3 其他被移除的旧物6.1.02023-09删除已废弃的makeUrl函数PR #24300并顺带缩小 Web 端 bundle 体积5.0.02023-06移除 ExpoKit 时代的detach.schemescheme 支持PR #22848标志旧版分离工作流的终结3.0.02021-12更新qs依赖并将sendIntent的extras参数抽取为独立的SendIntentExtras类型PR #152262.3.02021-06修复 AuthSession Google Provider 在 Expo Go 无 scheme 场景下的报错PR #12846并为 manifest2 新增字段PR #128173.2.22022-10将 EAS Updatesu.expo.devURL 识别为 Expo 托管 URL使createURL能为expo-auth-session生成合法的默认 URLPR #19258。八、如何在当前仓库中查看与验证API 实现Linking.ts全部公开函数与 hooks、createURL.tscreateURL/parse、Schemes.tsscheme 解析、Linking.types.tsParsedURL、CreateURLOptions、QueryParams等类型定义原生模块Android 侧 ExpoLinkingModule.kt 与LinkingReactActivityLifecycleListener.ktiOS 侧 ExpoLinkingModule.swift 与 ExpoLinkingRegistry.swift测试src/__tests__/Linking-test.ts及 Android/iOS/node/web 四端快照src/__tests__/__snapshots__/、src/__tests__/Schemes-test.native.ts、src/__tests__/Linking-test.web.ts覆盖了跨平台行为与回归场景。安装方式见 README.md在 Expo 托管项目中执行npx expo install expo-linking即可获得与当前 SDK 匹配的版本。九、给升级者的实战清单结合 CHANGELOG 的破坏性变更升级expo-linking时建议逐项核对弃用 API 替换useURL→useLinkingURLmakeUrl已删除removeEventListener已删除统一使用addEventListener(...).remove()或 hooks 自动清理scheme 配置生产构建必须在 app configapp.json或app.config.js中声明expo.scheme或平台级 scheme否则createURL在生产环境会抛错多 scheme 时显式传入scheme参数可消除警告并保证确定性最低系统版本56.0.0 起要求 iOS/tvOS 16.4、macOS 13.4低于此版本的设备无法使用新版本初始 URL 处理需要一次性消费深链的应用可在处理完成后调用clearInitialURL()防止冷启动重复消费Expo Go 限制在 Expo Go 中 scheme 解析受限仅exp/exps等白名单createURL在 Expo Go 发布更新场景下的行为不确定需要稳定回调用 URL 时应使用构建产物并显式传 scheme。以上每一条都对应 CHANGELOG 中真实的版本条目与当前源码中的具体实现可作为团队升级评审的核对依据。【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考