一天一个开源项目(第247篇):wxa-runner —— 让小程序在普通浏览器里用微信官方基础库跑起来,直接接 Playwright 做无头 E2E

引言

小程序的 E2E 测试,长期卡在"开发者工具只有桌面版"这一步。

这是"一天一个开源项目"系列的第 247 篇 文章。今天带你了解的项目是 wxa-runner(open-wxa/wxa-runner)。

做过微信小程序自动化测试的人大概都踩过这个坑:官方的开发者工具只有 Windows 和 macOS 版本,没法塞进 Linux CI;想用 Playwright 这类现代工具,又没有一个能在浏览器里跑的小程序运行环境。H5 方案倒是能跑,但渲染和组件是框架重新实现的,和真机总有差异。

wxa-runner 的思路很干脆:不重写基础库,而是把微信官方基础库原样搬进浏览器,自己只补上微信客户端那层"原生壳"。

你将学到什么

  • wxa-runner 要解决什么问题,以及它不解决什么
  • 它的核心思路:只实现原生层,复用官方基础库与编译器
  • 分层架构与各个包的职责
  • 浏览器里怎么跑起逻辑层和渲染层,WXML/WXSS 怎么编译
  • 与开发者工具的关系,以及在 Linux CI 上的运行方式
  • 已知限制与适用边界

前置知识

  • 了解微信小程序的基本结构(逻辑层 / 渲染层、WXML / WXSS、setData)
  • 用过 Playwright 或类似的浏览器自动化工具
  • 对 uni-app 或 Taro 有所了解更佳

项目背景

项目简介

一句话:让小程序在普通浏览器中借助微信官方基础库运行,用于本地调试和 E2E 自动化测试。

需要先说清楚它的边界:它不取代微信开发者工具,预览、上传、真机调试仍然需要开发者工具。

作者/团队介绍

  • 组织账号:open-wxa
  • 具体个人作者与贡献者名单:项目页未提供
  • 致谢:微信团队(官方基础库与编译器);WEPT(chemzqm/wept),最早验证了在浏览器里运行小程序的可行性
  • 创建时间:未提供

项目数据

  • ⭐ GitHub Stars: 556
  • 🍴 Forks: 73
  • 📝 Commits: 184
  • 📦 版本: v0.8.0
  • 📄 License: MIT(注意:内置基础库 wxa-baselib 是微信官方代码,版权归腾讯,不适用 MIT)
  • 📚 文档: open-wxa.github.io/wxa-runner
  • 🏷️ Topics: e2e-testing、mini-program、playwright、simulator、taro、uni-app、weapp

主要功能

核心作用

把小程序的编译产物直接放进浏览器运行,渲染、组件、setData、生命周期都由官方基础库执行,而不是像 H5 那样由某个框架重新实现。这样得到的行为更接近真机,同时又能被浏览器工具链驱动。

使用场景

  1. 无头 E2E 测试

    • 基于 Playwright 在浏览器内测试,无需远程控制开发者工具;CI 中可以无头运行。
  2. Linux CI 中的小程序测试

    • 在 GitHub Actions 的 Linux 机器上运行,不需要安装开发者工具。
  3. 本地调试

    • 用 Chrome DevTools 打断点、查看网络请求和 DOM,保存后热更新,页面栈和全局状态保留。
  4. 绕过登录等障碍

    • 在 setup 中预置登录态、mock 原生 API、拦截请求(如 wx.request),无需改业务代码。
  5. uni-app / Taro 项目的测试

    • 支持它们编译出的 mp-weixin 目录。

快速开始

启动命令:

bash 复制代码
npx wxa-runner dev ./dist/dev/mp-weixin   # 原生小程序目录,或 uni-app / Taro 编译产物
# → http://localhost:3000

可以通过地址参数直达某个页面,例如:

text 复制代码
http://localhost:3000/?path=pages/detail/detail&id=1

文档站首页没有给出完整的安装步骤、setup 与 Playwright 的代码示例,具体用法请查阅文档站的"启动前准备(setup)""在 Playwright 中测试""在 CI 中运行""命令与环境变量"等子页面。本文不替它们编造示例代码。

核心特性

  1. 与真机一致的运行方式

    • 直接运行编译产物,渲染、组件、setData、生命周期都由官方基础库执行。
  2. 无头 E2E

    • 基于 Playwright,可断言页面 data 和发出的请求。
  3. Chrome DevTools 调试

    • 断点、网络、DOM 都能看,保存后热更新。
  4. 启动前 setup

    • 预置登录态、mock 原生 API、拦截请求。
  5. 借用开发者工具登录态

    • 在装有开发者工具的机器上,可借用其登录态获取真实 code、手机号,并访问服务市场和插件页面。
  6. 兼容主流跨端产物

    • 原生小程序、uni-app、Taro。

项目优势

文档站有一页专门讲"与 miniprogram-automator 的选择",页面首页没有展开,下面是我基于其设计方式做的定性比较,仅供参考:

维度 wxa-runner 依赖开发者工具的方案(如 miniprogram-automator) H5 / 框架重新实现
运行环境 普通浏览器 需要桌面版开发者工具 浏览器
Linux CI 可无头运行,无需开发者工具 受限于开发者工具只有 Windows / macOS 可以
渲染一致性 官方基础库执行 官方 框架自行实现,可能有差异
驱动方式 Playwright 远程控制开发者工具 取决于方案

为什么值得关注?

  • 它解决的是小程序工程化里一个很实际的缺口:能放进 CI 的、尽量贴近真机的运行环境。
  • "复用官方、只补原生层"的取舍,让它不用追着基础库的每个版本重写渲染。
  • 它对自己的边界很坦诚,限制清单写得很具体。

项目详细剖析

核心思路:只实现"原生层"

小程序运行时可以粗分为两部分:微信官方提供的基础库、编译器、桥协议 ,以及微信客户端提供的原生层(消息桥、页面栈、原生 API、导航栏等原生 UI)。

wxa-runner 的选择是:前者全部复用官方,只自己实现后者。

分层架构

text 复制代码
应用层:cli | playwright fixture
服务层:core/server(开发服务器、代理、监听、热更新分流)
构建层:project / compiler / extractor
运行时:core/runtime(host 宿主 + frame 注入脚本)
        └─ 官方基础库(WAService.js / WAWebview.js)

各包的职责

包 职责
extractor 找到开发者工具目录,解析 .wxvpkg,读取 app.asar,把基础库解包缓存到 ~/.wxa-runner/baselib
compiler 封装 wcc(WXML / WXS)与 wcsc(WXSS),优先用开发者工具自带的,否则用 miniprogram-compiler
core project(解析项目、生成产物、__wxConfig)、server(DevServer、监听、热更新、代理、插件)、runtime(浏览器端)
cli 命令行 wxa-runner
playwright 封装 Playwright fixture、匹配器、Node 侧 mock 重放

仓库里还有 examples(showcase、hello-world、custom-tabbar、subpackages、worker 等)和 tests(unit、integration、e2e、playwright)。技术栈包括 TypeScript、pnpm workspace、Express、ws、chokidar、esbuild、tsup、Vitest 与 Playwright。

浏览器里是怎么把小程序跑起来的

逻辑层与渲染层。 逻辑层和渲染层各是一个同源 iframe:逻辑层隐藏,渲染层每个页面一个,URL 与页面路径一致,因此相对资源路径能正常解析。

先于基础库注入的 frame.js 负责这些事:

  • 预置全局变量
  • 提供 WeixinJSBridge(invoke / publish / subscribe / on)
  • 模拟设备尺寸和竖屏
  • 把鼠标事件转成触摸事件
  • 提前注入内置组件样式

选择 iframe 而不是 Web Worker ,是因为基础库需要 window / document,而且同源 iframe 能直接调用宿主,所以像 getStorageSync 这样的同步 API 也能同步返回。

宿主只维护页面栈并派发 onAppRoute,页面生命周期(onLoad / onShow / onReady)全部由基础库驱动。页面栈上限 10 层,路由操作串行,tabBar 页面实例做缓存。

编译 WXML / WXSS。

  • wcc 以子进程 方式运行。原因是原生模块 wcc.node 在同一进程里反复调用会随机 std::bad_alloc 崩溃。
  • 所有 wxml 和 wxs 一次编译成一个 $gwx:渲染层用完整版,逻辑层用裁剪版,缺少裁剪版会报节点创建期望不匹配。
  • wcsc 输出的结果中,comm 是公共运行时,其余每个页面或组件对应一个 setCssToHead,rpx 换算由 wcsc 生成的运行时完成。
  • 增量构建:只改 JS 时不重编模板和样式,三类编译并行,并发请求共享同一次构建。
  • 逻辑层每个 JS 文件一个 define 脚本,行号与源文件一致;ES module 经 esbuild 转成 CommonJS 并带 source map,所以断点调试对得上源码。

模拟 wx API 与原生组件。

  • 原生 API 由注册表管理,涵盖 storage、system、network、routes、ui、media、audio、map、picker、webView、fs、subpackages、misc 等模块。能同步得到结果的调用同步回调;未实现的 API 默认返回 ok 并提示一次。
  • 渲染层上报的平台是 devtools,所以大多数原生组件直接用基础库为开发者工具准备的网页实现,canvas、video、map、camera、audio、input 等可用。picker 由宿主弹出选择器,web-view 用 iframe 嵌入。
  • 广告、云开发、支付这类伴生 SDK 没有加载机制,只在 wx 层放占位桩,让业务走失败分支。
  • Worker 使用浏览器的 Web Worker,由宿主实现 WeixinWorker 的两端。

热更新。 只改 wxss,或者页面 / 组件自己的 wxml / js 时热更新;其他改动整页刷新并恢复页面栈。Page() 页面把新方法换到活实例上,Component() 页面和组件交给基础库重新挂载。

与测试相关的启动顺序

宿主会挂载 window.__wxa_test_api__,启动顺序为:测试 API → setup → __wxa_before_launch__ → 启动小程序。setup 在逻辑层创建之前执行,并且只传递以 WXA_ 开头的环境变量。这正是"不改业务代码就能预置登录态、mock 原生 API"的底层机制。

与开发者工具的关系

wxa-runner 复用开发者工具的基础库(.wxvpkg)、编译器(wcc / wcsc)和行政区划数据,抛弃桌面壳。

没有开发者工具时(Linux、CI):

  • 基础库使用内置的 @open-wxa/wxa-baselib(3.7.0)
  • 编译器使用 miniprogram-compiler,仅支持 x86-64,内核较旧,模板运行时有少量差异
  • 地区选择器不可用

同一版本的基础库优先用开发者工具的,可通过 WXA_DEVTOOLS_PATH、WXA_VENDOR_DIR、WXA_WCC / WXA_WCSC 覆盖路径。

基础库版本方面:默认使用不高于 3.7.0 的最新兼容版本;3.7.1 起,业务代码在原生 WeixinJSContext 创建的隔离子上下文中运行,需要用 --baselib 或 WXA_BASELIB 显式指定版本;3.8 起框架拆到 WAServiceMainContext.js / WASubContext.js。

几个关键设计取舍

取舍 原因
不直接用开发者工具 只有 Windows / macOS 版,难以放进 CI 和无头浏览器,也无法被 Playwright 驱动
用 iframe 而非 Web Worker 基础库需要 window / document,同源 iframe 还能支持同步 API
不重写基础库 工作量大且难以保证一致;最初自己模拟的版本只能支持很有限的能力
内置基础库放独立仓库 版权归腾讯,不适用 MIT,作为可选依赖安装,失败不影响主体
编译器用子进程 避开原生模块的随机崩溃
Worker 同步接口直接失败 开发者工具靠 SharedArrayBuffer 阻塞等待,那需要 COOP / COEP 跨源隔离,会让跨域资源加载失败

已知限制

这一部分写得越具体,越说明项目对自己的边界很诚实:

  • 基础库超过 3.7.0 属于未验证范围
  • 未覆盖或能力受限的组件:live-player(不能播放)、live-pusher、voip-room、open-data(渲染为空)、ad(走失败分支)、xr-frame
  • web-view:设置了 X-Frame-Options 或 CSP frame-ancestors 的网站无法嵌入;网页内的 wx.miniProgram 接口不可用;binderror 不会触发
  • map:需要联网,部分方法(toScreenLocation、地面覆盖物、室内图等)无效
  • 文件系统:saveFile 指定 filePath 会失败,saveFileSync 未实现;临时文件存在开发服务器上,重启后失效
  • 网络代理只用于本机开发环境,基础库自己的请求直接按失败返回
  • 在无开发者工具的环境里,编译器仅支持 x86-64,且与真机编译器存在少量差异

如何评价它的价值

  • 它测的是"逻辑和交互",不是"真机像素"。 浏览器里的渲染、原生组件与真机总有差异,适合验证业务流程、页面 data、请求,而不是做视觉回归。
  • 覆盖面取决于你的小程序用了哪些 API。 重度依赖直播、AR、广告、云开发的页面,需要自己评估。
  • 版本跟随是长期成本。 基础库超出 3.7.0 属于未验证范围,随着微信基础库演进,项目需要持续跟进。
  • 项目页没有给出路线图、配置详解和正式的同类对比,深度使用要依赖文档站和 PROJECT_DESIGN.md。

项目地址与资源

官方资源

相关资源

  • WEPT:最早验证了浏览器运行小程序的可行性
  • 仓库中的 PROJECT_DESIGN.md:架构与设计取舍的详细说明
  • 文档站"与 miniprogram-automator 的选择"一页

总结与展望

核心要点回顾

  1. 思路清晰:基础库、编译器、桥协议复用微信官方,只自己实现原生层,比重写基础库稳得多。
  2. 面向 CI:用 Playwright 驱动,Linux 上无需开发者工具即可无头运行。
  3. 贴近真机 :渲染、setData、生命周期都由官方基础库执行。
  4. 边界坦诚:限制清单具体,也明确说明不取代开发者工具。

适用人群

  • 想把小程序 E2E 测试放进 Linux CI 的团队
  • 使用 uni-app / Taro 开发小程序、需要自动化回归的开发者
  • 对"如何在浏览器中复用官方运行时"这类工程问题感兴趣的读者

学习建议

先在 examples/hello-world 上跑一遍 npx wxa-runner dev,用 Chrome DevTools 熟悉调试体验;再读 PROJECT_DESIGN.md 理解 host / frame / 基础库的分工;最后按文档站的 setup 与 Playwright 页面,给自己项目写第一个带登录态 mock 的 E2E 用例,并在 CI 里无头跑通。


探索 PrimeSkills ------ 精选 AI agent 和技能工具,每一个都经过真实工作流验证。没有炒作,只有真正好用的工具。

访问我的个人主页,获取更多见解和有趣的产品。

相关推荐
supabc1231 小时前
欧盟电子发票(XRechnung/Peppol/ZUGFeRD)踩坑记:用 Python 不装 Java 跑官方校验规则
xml·python·开源·电子发票·1024程序员节
andrsted2 小时前
练题簿在线免费刷题:让整理过的资料,变成能反复练习的题库
学习·微信小程序·小程序·学习方法·刷题·1024程序员节
行者全栈架构师4 小时前
【鸿蒙心迹】让老照片变清晰:HarmonyOS 7.0 图像超分相册工程化落地与 5 个踩坑解法
架构·前端框架·开源
miofly5 小时前
Kling 4.0 上线 API 调用,支持 30 秒原生音频视频生成
开源·github
DisonTangor6 小时前
FLUX 3 Image Klein 中文全解:5.6B 开源文生图与图像编辑模型实战指南
人工智能·ai作画·开源·aigc·1024程序员节
miofly6 小时前
山东大学研究:代码智能体的复杂 harness 主要提升 token 消耗而非性能
开源·github
2601_965619697 小时前
没时间给开源项目做宣传?AI自动化生成整套物料
人工智能·开源·自动化
DisonTangor7 小时前
MOSS-Image-Base 实战:从文生图到 JSON 布局精准控图
人工智能·ai作画·开源·aigc·1024程序员节
舰长1157 小时前
将百万数据插入到 Redis,有哪些实现方案?
数据结构·redis·开源