
移动开发前端跨平台原生移动【免费下载链接】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点击查看免费下载导读expo-linking是 Expo SDK 中专门用于“通用地创建与打开深度链接deep link”的核心模块面向 Android、iOS 与 Web 三端统一提供 URL 构造、解析、监听与跳转能力。本文以 packages/expo-linking/README.md 为骨架结合仓库内 TypeScript 源码、Android/Kotlin 与 iOS/Swift 原生实现及测试用例系统讲解安装方式、createURL/parse的构造解析原理、Scheme 解析策略、URL 事件监听与 React Hook 用法、开发工具链深链unwrapDevLaunchURL以及各平台的底层实现差异。读完本文你将能够在 Expo 应用中独立完成深链的创建、解析、接收与跳转全流程并理解其跨平台实现细节。一、expo-linking 是什么expo-linking是 Expo 生态中负责深度链接的模块其定位在 package.json 的 description 中写得很明确Create and open deep links universally通用地创建和打开深度链接。它基于 React Native 内置的LinkingAPI但进行了跨平台增强与 Expo 配置app config集成能从项目 Expo 配置中自动解析出当前应用可用的 URI Scheme在原生端缓存并分发初始 URL 与后续到达的 URL在 Web 端基于window.location与message事件工作提供 React HookuseLinkingURL与函数式 API 两套用法。从依赖关系看它仅依赖expo-constants用于读取 manifest / app config与invariant运行时校验并以expo、react、react-native作为 peer dependency是 expo-modules 体系中一个轻量、纯粹的工具型模块。二、安装与引入2.1 托管managedExpo 项目在托管工作流managed workflow中推荐直接通过 Expo CLI 安装与当前 SDK 版本匹配的版本npx expo install expo-linking该命令会自动解析并安装与当前 SDK 兼容的expo-linking版本避免手工指定版本号带来的兼容性问题。仓库中该模块当前版本为58.0.11见 package.json。2.2 纯 React Nativebare项目在 bare 工作流中需要先确保已经安装并配置好expo包即 expo-modules-core 等基础设施然后再执行同样的命令安装expo-linkingnpx expo install expo-linking安装完成后即可在代码中引入import * as Linking from expo-linking;也可以按需具名引入import { createURL, parse, useLinkingURL } from expo-linking;expo-linking的主入口为 src/Linking.ts它会根据运行环境选择对应实现原生端使用 src/ExpoLinking.ts 加载名为ExpoLinking的原生模块同时封装 React Native 的Linking见 src/RNLinking.tsWeb 端则使用 src/ExpoLinking.web.ts 与 src/RNLinking.web.ts 的浏览器实现React Server 环境使用 src/Linking.server.ts 提供的无副作用 shim对应 package.json 中exports的react-server条件导出。三、创建深度链接createURL3.1 基本用法createURL(path, options)是构造指向当前应用的深度链接 URL 的助手函数实现位于 src/createURL.tscreateURL(path, { scheme: myapp, // 可选URI 协议必须内置于原生应用 queryParams: { foo: bar }, // 可选将转换为查询字符串 isTripleSlashed: false, // 可选是否使用 scheme:///path 三段斜线 });其中CreateURLOptions的三个字段在 src/Linking.types.ts 中有明确定义参数类型说明schemestringURI 协议例如myapp://必须已内置于原生应用queryParamsQueryParams参数对象会经URLSearchParams序列化为查询字符串null/undefined值会被剔除isTripleSlashedboolean是否生成三段斜线形式scheme:///path默认双斜线scheme://path3.2 不同环境的 URL 形态根据运行环境不同createURL会生成不同形态的 URL源码注释与测试共同印证开发构建 / 生产构建scheme://path使用传入的scheme否则使用 app config 中定义的第一个 schemeWeb 开发环境https://localhost:19006/pathWeb 生产环境https://myapp.com/pathExpo Go 开发环境exp://128.0.0.1:8081/--/path。3.3 底层构造逻辑从 src/createURL.ts 的源码可以看到其构造流程通过getDevServerLocation()获取开发服务器地址仅当Constants.expoGoConfig?.developer存在时并据此判断是否使用exp/exps协议——HTTPS 开发服务器必须用exps因为 Expo Go 将exps映射为 HTTPS而exp会让其以 HTTP 请求开发服务器通过getHostUri()依次尝试开发服务器 authority、Constants.expoConfig.hostUri、Constants.linkingUri若hasCustomScheme()且为 Expo 托管环境则清空 hostUri仅用自定义 scheme在 Expo 托管环境下为 path 添加--/前缀如exp://host/--/path合并 hostUri 中原有的查询参数如 release-channel与用户传入的queryParams剔除空值后用URLSearchParams序列化用encodeURI对 scheme/host/path 部分编码最后拼接查询字符串。需要特别注意的是createURL在 Expo Go 中处理已发布publishedupdate 时的行为是未定义的不应依赖。如果需要稳定 URL例如 OAuth 授权回调场景应使用构建产物build 或 development build并提供 scheme。3.4 Web 端实现Web 端 src/createURL.web.ts 的实现更简单基于window.location.origin与URL构造查询参数逐项set最终返回url.toString()并去除末尾斜杠。这也解释了为何 Web 端生成的 URL 是https://host/path形态而非自定义 scheme。四、解析深度链接parse 与 parseInitialURLAsync4.1 parseparse(url)将深链 URL 解析为结构化对象ParsedURL定义见 src/Linking.types.tstype ParsedURL { scheme: string | null; // 例如 myapp协议冒号会被移除 hostname: string | null; path: string | null; // 应用内路径已去除前导斜杠 queryParams: QueryParams | null; // 查询参数键值对 };原生端解析逻辑在 src/createURL.ts优先用new URL(url)解析得到scheme移除末尾冒号、hostname、pathname与searchParams若 URL 非法则回退为path url针对 Expo 托管环境会剥离--/前缀路径对包含的路径按之后的部分截断兼容旧的深链格式。测试用例src/tests/Linking-test.ts覆盖了exp://127.0.0.1:8081/--/test/path?queryparam、custom:///test/path?foobar、https://example.com/test/path等典型输入并验证了查询参数不会被二次解码%252B只解码一层、畸形百分号编码不会抛错%GG原样保留两个关键行为——这两点在 CHANGELOG 中也有对应修复记录58.0.9版本“Stop decoding query parameter values twice inparse()”。4.2 parseInitialURLAsyncconst parsed await Linking.parseInitialURLAsync();parseInitialURLAsync()是Linking.getInitialURL()与parse()的组合封装先通过 React Native 的Linking.getInitialURL()获取启动应用的初始 URL再调用parse()解析。若没有链接打开应用返回的ParsedURL各字段均为null。在 Web 端它解析的是当前窗口 URL见 src/RNLinking.web.ts 的getInitialURL返回window.location.href。4.3 Web 端 parseWeb 端 src/createURL.web.ts 的parse与原生端行为保持一致但对非法 URL 做了兜底浏览器环境下返回scheme: http、hostname: localhost无窗口环境如 Node返回scheme: null。这套逻辑在 src/tests/Linking-test.web.ts 中有对应测试。五、Scheme 解析策略配置驱动的 URI 协议createURL与解析过程高度依赖项目的 URI Scheme 配置相关逻辑集中在 src/Schemes.ts。5.1 Scheme 在哪里配置Scheme 必须在 app configapp.json 或app.config.js中定义支持的字段expo.scheme通用 scheme可为字符串或字符串数组expo.ios.scheme、expo.android.scheme平台专属 scheme优先级高于通用 scheme兜底expo.ios.bundleIdentifier/expo.android.package原生应用标识符也可作为 scheme 使用主要用于 Google Sign-In 等场景。5.2 解析优先级与告警/报错resolveScheme(options)src/Schemes.ts的逻辑按执行环境分派StoreClientExpo Go若传入的 scheme 属于 Expo 客户端白名单iOS 为exp、exps、fb1696089354000816、host.exp.exponent等Android 为exp、exps见EXPO_CLIENT_SCHEMES则返回该 scheme否则静默回退到expBare裸工作流始终视为使用自定义 scheme传入的 scheme 优先返回未传入时依次尝试 manifest scheme、原生应用标识符多个 scheme 时使用第一个并告警一个 scheme 都没有时在生产环境直接抛错Standalone独立构建从 manifest 收集 scheme有自定义 scheme 则使用否则回退到 Expo 默认。同时resolveScheme对缺少 manifest未配置 expo-constants的场景会抛出expo-linking needs access to the expo-constants manifest ...错误对未配置 scheme 的生产应用抛出Cannot make a deep link into a standalone app with no custom scheme defined。这些分支在 src/tests/Schemes-test.native.ts 中均有详尽测试含“bare 下无 scheme 抛错”“bare 下仅 app id 时告警并使用 app id”等场景。实操建议从仓库实现看任何生产应用都应尽早配置scheme字段否则在开发期会收到console.warn告警在生产期则直接抛错。六、打开链接与系统能力openURL / canOpenURL / openSettings / sendIntent6.1 openURL 与 canOpenURLawait Linking.openURL(https://expo.dev); // 打开外部 URL const canOpen await Linking.canOpenURL(myapp://path); // 检测是否能被处理openURL(url)尝试让操作系统用已安装的应用打开给定 URL例如tel:5555555、exp://。URL 会先经过 src/validateURL.ts 的validateURL校验非空字符串否则抛invariant错误然后调用 React Native 的Linking.openURL。Promise 在用户确认或系统自动打开时 resolve 为true在没有应用可处理或用户取消时 reject。canOpenURL(url)检测是否有已安装应用能处理该 URL。Web 端恒返回true浏览器没有检测 API见 src/RNLinking.web.ts。Android 上若无法检查会 rejectiOS 上若未在Info.plist的LSApplicationQueriesSchemes中登记对应 scheme 也会 reject。6.2 openSettings 与 sendIntentawait Linking.openSettings(); // 打开系统设置中的应用自定义设置页 await Linking.sendIntent(action, extras); // Android 专用openSettings()打开操作系统设置中应用的自定义设置页Web 端不可用抛UnavailabilityError原生端优先使用 RNopenSettings否则回退到打开app-settings:协议。sendIntent(action, extras)Android 专用向系统发送带 extras 的 Intentextras为{ key, value }数组value 可为字符串/数字/布尔。源码注释明确建议改用expo-intent-launchersendIntent仅为兼容 React Native Linking API 而保留非 Android 平台会抛UnavailabilityError。七、接收深链监听 URL 事件与 React Hook7.1 addEventListenerimport { useEffect } from react; import * as Linking from expo-linking; useEffect(() { const subscription Linking.addEventListener(url, (event) { console.log(收到深链:, event.url); }); return () subscription.remove(); }, []);addEventListener(type, handler)只接受type urlhandler 接收EventType{ url: string; nativeEvent?: MessageEvent }返回带remove()的EmitterSubscription。原生端委托给 React Native 的Linking.addEventListenerWeb 端则监听window的message事件并以window.location.href作为 URL见 src/RNLinking.web.ts。7.2 useURL 与 useLinkingURLHookconst url Linking.useLinkingURL(); // 推荐返回初始 URL 后续变更 const legacyUrl Linking.useURL(); // 已废弃请改用 useLinkingURLuseURL()src/Linking.ts内部使用getInitialURL()addEventListener(url)源码标记为deprecateduseLinkingURL()src/Linking.ts则基于原生模块ExpoLinking.getLinkingURL与onURLReceived事件每次重新加载都会立刻返回初始 URL行为更稳定是推荐用法。7.3 getInitialURL / getLinkingURL / clearInitialURLconst url await Linking.getInitialURL(); // 启动 URL字符串或 null const url Linking.getLinkingURL(); // 原生缓存的链接 URL Linking.clearInitialURL(); // 清除缓存之后 getLinkingURL 返回 nullgetInitialURL()返回用于启动应用的 URL 字符串无则nullgetLinkingURL()同步返回原生模块缓存的初始 URL见 src/ExpoLinking.ts 的getLinkingURL()声明clearInitialURL()仅 Android/iOS 生效Web 端是 no-op清除缓存后直到收到新的深链前getLinkingURL()都返回null。八、原生实现Android 与 iOS 的深链分发链路8.1 Android 端Android 侧由三个文件协同完成深链接收与分发ExpoLinkingModule.kt声明名为ExpoLinking的模块暴露getLinkingURL、clearInitialURL两个函数与onURLReceived事件通过OnStartObserving/OnStopObserving管理观察者集合onURLReceivedObserversLinkingReactActivityLifecycleListener.kt在onCreate与onNewIntent中从intent?.data取出 Uri写入ExpoLinkingModule.initialURL并通知所有观察者ExpoLinkingPackage.kt将LinkingReactActivityLifecycleListener注册为ReactActivityLifecycleListener。也就是说Android 端深链的完整链路是Activity Intent → LinkingReactActivityLifecycleListener → ExpoLinkingModule缓存 initialURL 广播 onURLReceived→ JS 层useLinkingURL/addEventListener收到事件。首次冷启动由onCreate处理应用运行中由onNewIntent处理。8.2 iOS 端iOS 侧结构类似但基于NotificationCenterExpoLinkingModule.swift模块定义同名ExpoLinkinggetLinkingURL读取ExpoLinkingRegistry.shared.initialURLOnStartObserving注册onURLReceivedNotification观察者ExpoLinkingRegistry.swift单例持有initialURL并监听ExpoLinkingClearInitialURL通知以支持clearInitialURLLinkingAppDelegateSubscriber.swift实现ExpoAppDelegateSubscriber在application(_:open:options:)中写入 URL 并广播通知对Universal LinkNSUserActivity冷启动走continue userActivity分支App Clip 与通用链接冷启动不会触发open:options:因此用首次请求作为 initial URLmacOS 下则走application(_:open:urls:)分支。此外 DEBUG 构建会忽略指纹检查fingerprint check触发 URL避免它污染初始 URL对应 CHANGELOG58.0.4的修复。九、开发工具链深链unwrapDevLaunchURLunwrapDevLaunchURL(url)src/devLaunchURL.ts用于解析Expo Go / development build的启动 URL取出应用真正应处理的 URLunwrapDevLaunchURL(myapp://expo-development-client/?urlhttp%3A%2F%2F10.0.0.5%3A8081%2F--%2Fprofile); // http://10.0.0.5:8081/--/profile unwrapDevLaunchURL(myapp://login?__expo_disable_fab1); // myapp://login其规则如下优先读取保留参数__expo_url任意 host 上均生效兼容旧版expo-development-clienthost 的url参数若是旧的 launcher host 但没有目标参数返回空字符串否则删除所有__expo_*保留参数如__expo_disable_fab、__expo_disable_auto_launch、__expo_disable_onboarding并返回清理后的 URLURL 非法或没有保留参数时原样返回。该函数在 src/tests/Linking-test.ts 中有 7 组测试用例覆盖上述分支是开发环境深链调试中非常实用的工具。十、类型定义速查expo-linking的类型集中在 src/Linking.types.ts并随主入口一并导出export type QueryParams Recordstring, undefined | string | string[]; export type ParsedURL { scheme: string | null; hostname: string | null; path: string | null; queryParams: QueryParams | null; }; export type CreateURLOptions { scheme?: string; queryParams?: QueryParams; isTripleSlashed?: boolean; }; export type EventType { url: string; nativeEvent?: MessageEvent }; export type URLListener (event: EventType) void; export type SendIntentExtras { key: string; value: string | number | boolean };十一、测试与质量保障仓库为expo-linking配置了完整的测试体系jest.config.js主要测试文件包括src/tests/Linking-test.ts覆盖parse17 种 URL 形态的快照、unwrapDevLaunchURL、createURL查询参数转义、开发服务器 authority 优先、exps协议切换、bare 下三段斜线等src/tests/Linking-test.web.ts验证 Web 与原生实现返回一致的 subscription 形态src/tests/Schemes-test.native.ts覆盖collectManifestSchemes与resolveScheme的全部环境分支src/rsc_tests/Linking.test.ts验证 React Server 环境下openURLresolve 为true、getInitialURL返回 shim 空字符串。测试的快照文件Linking-test.ts.snap.ios、.android、.node、.web进一步印证了各平台 URL 形态的差异。十二、典型使用场景小结场景推荐 API生成可分享的深链如邀请链接、内容详情页createURL(path, { scheme, queryParams })解析收到的深链为结构化数据parse(url)/parseInitialURLAsync()冷启动获取启动 URLgetInitialURL()/getLinkingURL()/useLinkingURL()监听应用运行期间新到达的深链useLinkingURL()/addEventListener(url, handler)跳转到外部 URL 或系统能力openURL()、canOpenURL()、openSettings()解析开发构建/Expo Go 的 launcher URLunwrapDevLaunchURL()清除缓存的初始 URLclearInitialURL()仅 Android/iOS结语expo-linking以一份统一 API 覆盖了 Android、iOS、Web 与 React Server 四种运行环境JS 层通过 src/Linking.ts 聚合 React NativeLinking与 Expo 原生模块原生层分别在 AndroidActivity 生命周期监听与 iOSAppDelegate 订阅 NotificationCenter完成 URL 捕获与事件广播配置层则由 src/Schemes.ts 依据 app config 智能解析 URI Scheme。理解本文所述的构造、解析、监听与 scheme 解析机制后你即可在 Expo 应用中稳定地实现深度链接的全链路功能并能在遇到“深链打不开”“scheme 报错”“初始 URL 获取异常”等问题时依据源码路径快速定位根因。赞分享移动开发前端跨平台原生移动【免费下载链接】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点击查看免费下载相关推荐expo-file-system 深度指南在 Expo 与 React Native 中访问本地文件系统expo file system 深度指南在 Expo 与 React Native 中访问本地文件系统 导读 本文围绕 expo file system 移动开发前端跨平台原生移动如何部署 BiliBiliToolProB站自动化签到、投币与账号管理完整指南如何部署 BiliBiliToolProB站自动化签到、投币与账号管理完整指南 BiliBiliToolPro 是一款开源 B站自动化 工具把每日自动签到、后端任务调度工作流自动化KS-Downloader终极指南如何快速下载无水印快手视频KS Downloader终极指南如何快速下载无水印快手视频 KS Downloader是一款功能强大的快手视频下载工具专为需要获取高质量无水印快手视频素材视频网页爬虫后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考