news 2026/9/7 16:36:20

Expo Go 内置的 react-native-webview:vendored 模块的架构、平台实现与 Expo 定制补丁解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Expo Go 内置的 react-native-webview:vendored 模块的架构、平台实现与 Expo 定制补丁解析

Expo Go 内置的 react-native-webview:vendored 模块的架构、平台实现与 Expo 定制补丁解析

【免费下载链接】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 仓库中 Expo Go 应用内嵌的 react-native-webview 模块 为主体,解析这个社区维护的 WebView 组件为什么被 Expo Go 以「vendored module」方式内置、它如何同时覆盖 iOS/Android/macOS/Windows 与新旧两套 React Native 架构,以及 Expo 团队通过 vendoring 配置 与 作用域补丁 在原生层做了什么定制。读完后你能掌握:该模块的目录结构与多平台入口机制、版本升级的操作方式,以及 Expo 定制的 WKProcessPool 作用域隔离原理。

1. 模块定位:为什么 Expo Go 要内置一份 react-native-webview

模块自带的 README 说明了它的来历:React Native WebView是一个社区维护的 WebView 组件,目标取代已从 React Native 核心中移除的内置 WebView;它兼容iOS、Android、Windows 和 macOS,同时支持旧架构(paper)与新架构(fabric),并且明确声明与 Expo 兼容(compatible with expo)。

在 Expo 仓库中,apps/expo-go/modules/下的所有模块遵循一个统一规则——modules 目录说明 指出:Expo Go 中的 vendored 模块意味着「我们将其内置(vendor),并应用位于tools/src/vendoring/config的 Expo-Go 专属补丁」。react-native-webview正是这类模块之一。当前 vendored 版本由 package.json 确认为13.16.1,该版本号与 Expo Go 应用自身的依赖声明(apps/expo-go/package.json"react-native-webview": "13.16.1")保持一致。

模块自身的依赖极简,仅有两个运行时依赖:

"dependencies": { "escape-string-regexp": "^4.0.0", "invariant": "2.2.4" }

peerDependencies要求宿主项目提供任意版本的reactreact-native。开发依赖则固定在一个较新的基线上,例如react-native: 0.73.5react-native-macos: 0.73.17react-native-windows: 0.73.8@callstack/react-native-visionos: 0.73.8——从这些版本组合可以推断,该组件是在 RN 0.73 世代完成新架构(Codegen/Fabric)适配的。

README 还给出了官方使用示例,这也是本模块对外暴露的最基本 API:

import React, { Component } from 'react'; import { StyleSheet, Text, View } from 'react-native'; import { WebView } from 'react-native-webview'; // ... const MyWebComponent = () => { return <WebView source={{ uri: 'https://reactnative.dev/' }} style={{ flex: 1 }} />; }

组件从react-native-webview导入,通过source属性传入uri/HTML,其余行为由一组onXxx回调与配置 props 驱动。README 同时保留了上游的常见问题提示:出现Invariant Violation: Native component for "RNCWebView does not exist"通常是原生 link 环节出错;Android 在:app:mergeDexRelease阶段构建失败则需要在android/app/build.gradle开启 multidex。项目遵循语义化版本(SemVer),破坏性变更只出现在主版本号,许可证为 MIT。

需要注意一个仓库事实:README 中链接的docs/Getting-Started.mddocs/Reference.md等上游文档并未包含在本仓库的 vendored 副本中apps/expo-go/modules/react-native-webview/下没有docs目录)。vendoring 配置中明确排除了测试文件(excludeFiles: ['src/__tests__/**/*']),API 参考需以上游仓库文档为准,本仓库内可查证的是src/lib/中的完整类型定义。

2. 目录结构:一套 TypeScript 源码 + 四套原生实现

vendored 副本的顶层结构清晰地体现了「JS 层统一、原生层分平台」的组织方式:

目录/文件职责
src/TypeScript 源码:跨平台共享逻辑与四个平台入口
apple/iOS 与 macOS 共用的 Objective-C++/Swift 原生实现
android/Kotlin/Java 实现,按事件分类组织 JS 事件类
windows/C++ 实现(ReactWebView / ReactWebView2 双后端)
lib/prepare脚本产出的编译产物(JS + d.ts)
react-native-webview.podspecCocoaPods 集成入口,iOS/macOS 共用apple/源码
index.js / index.d.ts包入口,指向lib/

package.json 中三个入口字段解释了「源码模式 vs 发布模式」的双轨制:

  • main: index.js—— npm 安装场景,加载lib/编译产物;
  • react-native: src/index.ts—— 在 Metro(Expo/RN 开发链路)中,解析器优先取该字段,直接消费 TypeScript 源码,保证 Expo Go 内置副本可以被调试与热更新;
  • main-internal: src/index.ts—— Expo 内部构建链路的显式入口。

package.json 的codegenConfig声明了 Codegen 规范RNCWebViewSpectype: "all"),Android 包名为com.reactnativecommunity.webview,iOS 侧映射组件RNCWebView与模块RNCWebViewModule——这就是新架构下src/RNCWebViewNativeComponent.ts能从 spec 生成原生绑定、并在android/newarchandroid/oldarch两个目录中各放一份 Manager 实现的结构来源。

2.1 多平台入口与 Web 端的「哑组件」

入口文件 只有一行导出:

import WebView from './WebView'; export { WebView }; export default WebView;

src/目录下并排存在五个 WebView 实现:WebView.ios.tsxWebView.android.tsxWebView.macos.tsxWebView.windows.tsx与 WebView.tsx。RN/Metro 的文件扩展名解析约定决定了不同平台会命中对应后缀的文件;最后的WebView.tsx是不被任何后缀命中的兜底实现,它的源码直白地说明了用途:

// This "dummy" WebView is to render something for unsupported platforms, // like for example Expo SDK "web" platform. const WebView: React.FunctionComponent<WebViewProps> = () => ( <View style={styles.flexStart}> <Text style={styles.colorRed}> React Native WebView does not support this platform. </Text> </View> );

即:在 Expo 的 Web 平台上渲染一段红字提示而非崩溃——这保证了含 WebView 的应用可以直接跑在 Expo Web SDK 里。lib/中同名分布的.js/.d.ts文件(WebView.ios.jsWebView.macos.js等)印证了babel --extensions ".ts,.tsx" --out-dir lib src构建脚本的产物布局。

3. Expo 的定制:vendoring 流程与作用域隔离补丁

3.1 升级流程:et uvm命令 + 自动打补丁

modules 目录说明 给出了升级命令:运行et uvm react-native-webview -c "<版本号>",工具会把模块更新到指定版本并重新应用补丁。其自动化逻辑位于 expoGoConfig.ts:

'react-native-webview': { source: 'react-native-webview', sourceType: 'npm', excludeFiles: ['src/__tests__/**/*'], async postCopyFilesHookAsync(sourceDirectory, targetDirectory) { // patch for scoped webview const patchFile = path.join( EXPOTOOLS_DIR, 'src/vendoring/config/react-native-webview-scoping.patch' ); // ... await applyPatchAsync({ patchContent, cwd: targetDirectory, stripPrefixNum: 0 }); }, },

流程是:从 npm 拉取指定版本 → 剔除测试文件 → 拷贝进apps/expo-go/modules/→ 以patch -p0方式应用 scoping 补丁。补丁应用失败时工具会打印等价的手动命令(patch -p0 -d <dir> < patch文件>)并抛错,保证 CI 中不会静默丢失定制。

3.2 补丁原理:按 scopeKey 隔离 WKProcessPool

react-native-webview-scoping.patch 是理解 Expo 定制的核心,它改动了apple/下四个文件,目的是让不同 Expo 项目(scope)的 WKWebView 使用相互隔离的进程池与存储上下文,避免多项目共享 Expo Go 容器时发生 cookie/存储串扰:

  1. RNCWKProcessPoolManager.h/.m:上游只有单例方法sharedProcessPool,补丁为其增加了字典_pools与方法

    - (WKProcessPool *)sharedProcessPoolForScopeKey:(NSString *)scopeKey;

    逻辑为:scopeKey为空时回退到原共享池;否则按 key 懒创建并复用独立WKProcessPool

  2. RNCWebViewImpl.h/.m:给视图实现增加@property (nonatomic, strong) NSString *scopeKey,并在创建WKWebViewConfiguration时改为sharedProcessPoolForScopeKey:self.scopeKey(仅在useSharedProcessPool为真时生效)。

  3. RNCWebViewManager.mm:Manager 新增接收scopeKey(以及easProjectIdkernelServiceDelegate等参数)的初始化方法,并在view工厂方法中把 scopeKey 注入每个RNCWebViewImpl实例:

    RNCWebViewImpl *webview = [[RNCWebViewImpl alloc] init]; webview.scopeKey = _scopeKey;

从源码结构看,scopeKey 最终来源于 Expo Go 内核按项目划分的 stable legacy id / scope 体系;同一项目的多个 WebView 共享一个进程池,不同项目之间则完全隔离。这也解释了为什么补丁只覆盖 Apple 平台——Android 侧的隔离由 Expo Go 自身的 WebView CookieManager 上下文机制处理,而本补丁仅针对 WKWebView 的进程池共享行为。

4. 原生事件系统与关键 Props:从类型定义到原生回调

README 指向的 API 参考虽不在 vendored 副本中,但 WebViewTypes.ts 的完整类型定义就是权威参考。核心 Props 包括:

Prop说明(取自类型定义)
source: WebViewSource加载来源,uri/html等(约 L314、L1177)
onMessage接收 Web 端通过 postMessage 桥接的消息(L307、L1254)
onShouldStartLoadWithRequest拦截并控制新请求是否发起,可用于路由外链(L308、L1323)
injectedJavaScript/injectedJavaScriptObject页面加载时向 JS 上下文注入脚本或序列化对象(L293、L1349)
injectedJavaScriptBeforeContentLoaded*ForMainFrameOnly系列在 content 加载前注入;ForMainFrameOnly默认true,Android 上为强制行为(L643-L654、L1280-L1289)
onLoadSubResourceError子资源发生 SSL 错误时回调(L1165-L1170)
allowsBackForwardNavigationGesturesfileSystemAccessfile://URL 相关,L602、L916)等平台特定行为开关

iOS 侧由 apple/RNCWebViewImpl.m、RNCWebViewManager.mm(其中RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary)声明了 source 的桥接类型)与RNCWebViewDecisionManager(决策链)等文件承载;Android 侧把每一种事件都建模为独立的事件类,见 android 事件目录:TopMessageEventTopLoadingStartEventTopLoadingFinishEventTopLoadingErrorEventTopHttpErrorEventTopNewWindowEventTopRenderProcessGoneEventTopShouldStartLoadWithRequestEventSubResourceErrorEvent等——与 JS 侧onXxx回调一一对应,再由RNCWebViewClient/RNCWebChromeClient触发、RNCWebViewManagerImpl.kt组装 View。Windows 侧则由 windows/ReactNativeWebView 下的ReactWebView(传统后端)与ReactWebView2(Edge WebView2 后端)双实现提供。

5. 小结

  • 该模块是上游社区项目v13.16.1 的 vendored 副本,README 声明的平台矩阵(iOS/Android/Windows/macOS)与新旧架构双支持,均可由src/apple/android/(newarch + oldarch)、windows/的实际代码结构印证。
  • Expo 的价值增量集中在 vendoring 工具链:et uvm react-native-webview -c <ver>可完成「拉取 npm 版本 → 排除测试 → 打 scoping 补丁」的完整升级闭环。
  • scoping 补丁 通过给RNCWKProcessPoolManager引入sharedProcessPoolForScopeKey:按项目隔离 WKProcessPool,是 Expo Go 多项目并发场景下 Web 内容隔离的关键定制。
  • 若需完整的属性文档与 Getting Started 步骤,vendored 副本未携带上游docs/,请以本仓库 WebViewTypes.ts 中的类型与注释作为实现级参考。

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 16:32:44

基于微信小程序的付费自习室系统设计与实现(毕设源码+文档)

温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;本人主页置顶文章(点我)开头有 CSDN 平台…

作者头像 李华
网站建设 2026/9/7 16:29:02

虚拟电厂多时间尺度调度:模型、代码与滚动优化实践

1. 虚拟电厂多时间尺度调度到底在解决什么问题 先别急着打开Matlab敲代码&#xff0c;这个方向的核心问题得先想明白。虚拟电厂&#xff08;Virtual Power Plant&#xff0c;VPP&#xff09;这个概念在新型电力系统里已经不算新词了&#xff0c;它本质上就是把分散的分布式电源…

作者头像 李华
网站建设 2026/9/7 16:25:08

单片机计算机毕设之基于 STM32 或 51 单片机的定时自动晾衣控制系统设计与实现 基于 STM32 或 51 单片机的环境监测型智能门窗控制器设计(025606)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机&#xff0c;Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华