辅助 WebView
辅助 WebView(xunlei.runtime.webview)让微应用可以打开一个受控的浏览器页面,通过代码驱动它完成页面数据提取、表单操作等任务,相当于"内置的无头浏览器自动化能力"。
典型场景
- 页面数据提取:页面内容由脚本异步渲染、直接
fetch拿不到最终结果时,在辅助 WebView 中加载页面,等待渲染完成后从 DOM 中提取数据; - 表单操作:自动填表、点击、等待元素出现。

权限
使用辅助 WebView 需要在 manifest 中声明 webview 权限:
json
{
"permissions": ["webview"]
}未声明时:
await xunlei.runtime.webview.isAvailable()返回false(可用于功能探测);open()调用返回PERMISSION_DENIED。
打开页面
ts
const page = await xunlei.runtime.webview.open({
headless: false, // 是否隐藏窗口(true 时不显示给用户)
title: 'Example 页面', // 窗口标题
width: 480, // 窗口尺寸
height: 640,
userAgent: '...', // 自定义 UA(可选)
});| 选项 | 类型 | 说明 |
|---|---|---|
headless | boolean | 无头模式,默认 false;需要用户交互(登录)时保持 false。非本地开发环境中,事件脚本打开的网页窗口会被强制隐藏 |
title | string | 窗口标题 |
width / height | number | 窗口尺寸 |
userAgent | string | 自定义 User-Agent |
WebviewPage API
open() 返回的 page 对象提供页面驱动力:
| 方法 | 说明 |
|---|---|
on(event, handler) | 监听页面事件,返回取消监听的函数,见事件监听 |
goto(url, opts?) | 打开地址,opts.waitUntil 支持 'load' / 'domcontentloaded',opts.timeoutMs 控制超时 |
execute(scriptOrFn, ...args) | 在页面中执行脚本(字符串或函数),返回执行结果 |
addInitScript(script) | 在后续每次页面加载前注入脚本 |
click(selector, opts?) | 点击元素 |
type(selector, text, opts?) | 向输入框输入文本 |
focus(selector) | 聚焦元素 |
waitForSelector(selector, opts?) | 等待元素出现 / 消失(visible / hidden 可分别要求可见或隐藏),返回 boolean |
waitForFunction(fn, ...args) | 等待自定义条件成立,返回函数结果 |
getCookies() / setCookie(c) / deleteCookie(c) / clearCookies() | Cookie 管理 |
url() | 当前页面地址 |
content() | 页面 HTML 内容 |
close() | 关闭页面 |
超时行为:
waitForSelector超时返回false(不抛错);waitForFunction超时返回null(不抛错);- 默认超时 10 秒,轮询间隔 100ms。
事件监听
page.on(event, handler) 监听页面事件,Promise 完成表示监听已就绪,并返回取消监听的函数:
ts
const unsubscribe = await page.on('url-changed', ({ url, sameDocument }) => {
xunlei.logger.info('页面地址变化', url, sameDocument);
});
// 不再需要时取消监听
await unsubscribe();| 事件 | 触发时机 | 回调参数 |
|---|---|---|
url-changed | 实际地址发生变化,包含同一文档内的导航;不代表页面已加载完成 | { url, sameDocument } |
load | 主页面触发 window.load,即文档及阻塞 load 的资源加载结束;刷新页面会触发,仅 hash / History API 引起的同文档导航不触发 | { url } |
load-error | 页面加载失败 | { url, message } |
closed | 页面已关闭:用户关闭窗口,或通过 close() 关闭 | { reason: 'user' | 'api' } |
使用要点:
- 导航和加载事件仅针对主页面,不包含 iframe;
- 监听不回放当前状态:需要监听导航时,在
goto()之前完成注册; - 页面关闭时先通知
closed监听器,再自动释放全部监听,之后调用unsubscribe()也安全; - 回调仅用于通知,不能阻止导航;宿主不等待异步回调完成。
示例:提取动态渲染的页面数据
页面内容由脚本异步渲染时,直接 fetch HTML 拿不到最终结果。在辅助 WebView 中等渲染完成后再从 DOM 提取:
ts
const page = await xunlei.runtime.webview.open({ headless: true });
try {
await page.goto('https://example.com/report/123');
// 等待异步渲染完成
await page.waitForSelector('#result .item', { timeoutMs: 10_000 });
// 渲染完成后从 DOM 中提取结构化数据
const items = await page.execute<{ title: string; url: string }[]>(() =>
Array.from(document.querySelectorAll('#result .item'), (el) => ({
title: el.querySelector('.title')?.textContent?.trim() ?? '',
url: el.querySelector('a')?.getAttribute('href') ?? '',
})),
);
if (!items.length) throw new MessageError('未提取到数据');
return items;
} finally {
await page.close();
}要点:
headless: true不弹窗,整个过程对用户无感;- 先
waitForSelector确认渲染完成,再execute提取,避免读到空内容; - 提取逻辑在目标页面中执行,可自由使用
document查询。
示例:表单操作
自动填表、提交并读取结果:
ts
const page = await xunlei.runtime.webview.open({
title: '提交查询',
width: 480,
height: 640,
});
try {
await page.goto('https://example.com/query');
await page.waitForSelector('#query-form input[name="keyword"]');
await page.type('#query-form input[name="keyword"]', '迅雷');
await page.click('#query-form button[type="submit"]');
// 等待结果渲染
const ok = await page.waitForSelector('#result');
if (!ok) throw new MessageError('查询失败');
return await page.execute<string>(
() => document.querySelector('#result')?.textContent?.trim() ?? '',
);
} finally {
await page.close();
}生命周期与限制
- 辅助 WebView 的生命周期归属于创建它的运行环境:微应用窗口关闭、事件脚本结束、微应用重载 / 停用 / 卸载时,相关页面会被自动关闭,无需(也没有)批量关闭 API——但建议用完主动
close(); - 辅助 WebView 页面不注入
xunlei对象,页面内无法调用平台能力; - 辅助 WebView 有独立的会话环境,与微应用页面、与其它微应用互不共享 Cookie;卸载微应用时,运行时会清理其页面与辅助 WebView 会话中持久化的 Cookie、存储数据和缓存。


