Electron WebContents API 知识点总结
目录
- [1. 概述](#1. 概述 "#1-%E6%A6%82%E8%BF%B0")
- [2. 导航事件流程](#2. 导航事件流程 "#2-%E5%AF%BC%E8%88%AA%E4%BA%8B%E4%BB%B6%E6%B5%81%E7%A8%8B")
- [3. 页面加载事件](#3. 页面加载事件 "#3-%E9%A1%B5%E9%9D%A2%E5%8A%A0%E8%BD%BD%E4%BA%8B%E4%BB%B6")
- [4. 窗口管理事件](#4. 窗口管理事件 "#4-%E7%AA%97%E5%8F%A3%E7%AE%A1%E7%90%86%E4%BA%8B%E4%BB%B6")
- [5. 渲染进程事件](#5. 渲染进程事件 "#5-%E6%B8%B2%E6%9F%93%E8%BF%9B%E7%A8%8B%E4%BA%8B%E4%BB%B6")
- [6. 静态方法](#6. 静态方法 "#6-%E9%9D%99%E6%80%81%E6%96%B9%E6%B3%95")
- [7. 实例方法](#7. 实例方法 "#7-%E5%AE%9E%E4%BE%8B%E6%96%B9%E6%B3%95")
- [8. 实际应用示例](#8. 实际应用示例 "#8-%E5%AE%9E%E9%99%85%E5%BA%94%E7%94%A8%E7%A4%BA%E4%BE%8B")
- [9. 安全考虑](#9. 安全考虑 "#9-%E5%AE%89%E5%85%A8%E8%80%83%E8%99%91")
1. 概述
webContents 是什么?
webContents 是负责渲染和控制网页 的模块,是 BrowserWindow 对象的属性。
scss
复制代码
┌─────────────────────────────────────────────────────────┐
│ BrowserWindow │
│ ┌─────────────────────────────────────────────────┐ │
│ │ webContents │ │
│ │ │ │
│ │ • 加载页面 (loadURL, loadFile) │ │
│ │ • 导航控制 (goBack, goForward) │ │
│ │ • 页面操作 (print, savePage) │ │
│ │ • 开发者工具 (openDevTools) │ │
│ │ │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
获取 webContents
javascript
复制代码
const { BrowserWindow } = require('electron')
// 通过 BrowserWindow 获取
const win = new BrowserWindow({ width: 800, height: 600 })
const contents = win.webContents
// 通过 ID 获取
const { webContents } = require('electron')
const contents = webContents.fromId(1)
2. 导航事件流程
2.1 完整导航事件顺序
scss
复制代码
文档导航 (完整页面加载)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. did-start-loading 开始加载
↓
2. will-frame-navigate 任何 frame 准备导航 (可取消)
↓
3. will-navigate 主 frame 准备导航 (可取消)
↓
4. will-redirect 重定向发生时 (可取消)
↓
5. did-redirect-navigation 重定向完成
↓
6. did-frame-navigate 任意 frame 导航完成
↓
7. did-navigate 主 frame 导航完成
↓
8. did-finish-load 加载完成
↓
9. did-stop-loading 停止加载
↓
10. dom-ready DOM 准备就绪
2.2 页面内导航
markdown
复制代码
页面内导航 (不刷新页面)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. did-start-loading
↓
2. did-navigate-in-page 页面内导航完成
↓
3. did-stop-loading
2.3 事件对比
| 事件 |
触发时机 |
可取消 |
适用场景 |
will-navigate |
主 frame 导航前 |
✅ |
拦截主 frame 导航 |
will-frame-navigate |
任意 frame 导航前 |
✅ |
拦截所有 frame 导航 |
will-redirect |
重定向发生前 |
✅ |
阻止重定向 |
did-navigate |
主 frame 导航完成 |
❌ |
记录导航完成 |
did-frame-navigate |
任意 frame 导航完成 |
❌ |
记录 frame 导航 |
did-navigate-in-page |
页面内导航 |
❌ |
hashchange, pushState |
2.4 导航事件示例
javascript
复制代码
const { BrowserWindow } = require('electron')
const win = new BrowserWindow()
const { webContents } = win
// 拦截导航
webContents.on('will-navigate', (event, url) => {
console.log('准备导航到:', url)
// 阻止外部链接
if (!url.startsWith('https://myapp.com')) {
event.preventDefault()
console.log('已阻止导航')
}
})
// 监听导航完成
webContents.on('did-navigate', (event, url, httpResponseCode) => {
console.log('导航完成:', url)
console.log('HTTP 状态码:', httpResponseCode)
})
// 监听页面内导航
webContents.on('did-navigate-in-page', (event, url, isMainFrame) => {
console.log('页面内导航:', url)
})
3. 页面加载事件
3.1 加载状态事件
| 事件 |
说明 |
did-start-loading |
加载开始(spinner 开始旋转) |
did-stop-loading |
加载结束(spinner 停止旋转) |
did-finish-load |
加载完成 |
did-fail-load |
加载失败 |
did-fail-provisional-load |
加载取消或失败 |
did-frame-finish-load |
Frame 加载完成 |
dom-ready |
DOM 准备就绪 |
3.2 示例
javascript
复制代码
webContents.on('did-finish-load', () => {
console.log('页面加载完成')
})
webContents.on('did-fail-load', (event, errorCode, errorDescription) => {
console.log('加载失败:', errorDescription)
})
webContents.on('dom-ready', () => {
console.log('DOM 已就绪,可以操作页面元素')
})
4. 窗口管理事件
4.1 页面创建事件
javascript
复制代码
// 渲染进程调用 window.open() 创建新窗口后触发
webContents.on('did-create-window', (window, details) => {
console.log('新窗口创建:')
console.log(' URL:', details.url)
console.log(' 名称:', details.frameName)
console.log(' 打开方式:', details.disposition)
// disposition: 'default', 'foreground-tab', 'background-tab', 'new-window'
// 可以设置新窗口的属性
window.webContents.on('did-finish-load', () => {
window.setTitle('新窗口标题')
})
})
4.2 窗口移动/调整大小
javascript
复制代码
// 页面调用 window.moveTo() 或 window.resizeTo() 时触发
webContents.on('content-bounds-updated', (event, bounds) => {
console.log('窗口新边界:', bounds)
// 阻止窗口移动
// event.preventDefault()
})
5. 渲染进程事件
5.1 进程状态事件
| 事件 |
说明 |
原因 |
render-process-gone |
渲染进程消失 |
崩溃、被杀、OOM |
unresponsive |
页面无响应 |
JS 执行时间过长 |
responsive |
页面恢复响应 |
JS 执行完成 |
crashed |
渲染进程崩溃(已废弃) |
- |
javascript
复制代码
// 渲染进程崩溃
webContents.on('render-process-gone', (event, details) => {
console.log('渲染进程消失:', details.reason)
// reason: 'crashed', 'killed', 'oom', 'launch-failed', 'integrity-failure'
if (details.reason === 'crashed') {
// 显示错误页面或重启
win.loadURL('error.html')
}
})
// 页面无响应(假死)
webContents.on('unresponsive', () => {
console.log('页面无响应')
})
webContents.on('responsive', () => {
console.log('页面恢复响应')
})
5.2 离开页面事件
javascript
复制代码
// beforeunload 尝试阻止卸载
webContents.on('will-prevent-unload', (event) => {
const choice = dialog.showMessageBoxSync(win, {
type: 'question',
buttons: ['离开', '留下'],
message: '您确定要离开吗?'
})
if (choice === 1) {
event.preventDefault() // 留下
}
})
6. 静态方法
6.1 获取所有/聚焦的 WebContents
javascript
复制代码
const { webContents } = require('electron')
// 获取所有 WebContents
const allContents = webContents.getAllWebContents()
console.log('所有 WebContents 数量:', allContents.length)
// 获取聚焦的 WebContents
const focused = webContents.getFocusedWebContents()
6.2 通过 ID 获取
javascript
复制代码
// 通过 ID 获取
const contents = webContents.fromId(1)
// 通过 WebFrameMain 获取
const contents = webContents.fromFrame(frame)
// 通过 DevTools TargetID 获取
const contents = webContents.fromDevToolsTargetId(targetId)
7. 实例方法
7.1 页面加载
javascript
复制代码
// 加载 URL
await webContents.loadURL('https://example.com')
// 加载本地文件
await webContents.loadFile('./index.html')
// 带选项
await webContents.loadURL('https://example.com', {
httpReferrer: 'https://google.com',
userAgent: 'Mozilla/5.0',
extraHeaders: 'X-Custom-Header: value'
})
7.2 导航控制
javascript
复制代码
// 后退
webContents.goBack()
// 前进
webContents.goForward()
// 跳转到指定位置
webContents.goToOffset(offset)
// 跳转到指定索引
webContents.goToIndex(index)
// 获取导航历史
const history = webContents.getAllShared逸s()
7.3 页面操作
javascript
复制代码
// 打印页面
webContents.print(options, callback)
// 保存页面
webContents.savePage(fullPath, saveType, callback)
// 缩放
webContents.setZoomFactor(1.5) // 150%
webContents.setZoomLevel(1) // 缩放级别
webContents.getZoomFactor() // 获取缩放因子
webContents.getZoomLevel() // 获取缩放级别
// 焦点
webContents.focus()
7.4 开发者工具
javascript
复制代码
// 打开开发者工具
webContents.openDevTools()
// 关闭开发者工具
webContents.closeDevTools()
// 是否打开
webContents.isDevToolsOpened()
// 是否聚焦
webContents.isDevToolsFocused()
7.5 JavaScript 执行
javascript
复制代码
// 在页面中执行 JavaScript
webContents.executeJavaScript(`
document.title = 'New Title'
`).then(result => {
console.log('执行结果:', result)
})
// 在页面加载前插入代码
webContents.on('did-finish-load', () => {
webContents.executeJavaScript('console.log("页面加载完成")')
})
7.6 发送消息到渲染进程
javascript
复制代码
// 主进程 → 渲染进程
webContents.send('channel', ...args)
// 示例
webContents.send('update-data', { name: '张三', age: 25 })
// 渲染进程中使用 ipcRenderer.on('update-data', ...) 接收
8. 实际应用示例
8.1 页面加载监控
javascript
复制代码
class PageLoader {
constructor(webContents) {
this.webContents = webContents
this.setupListeners()
}
setupListeners() {
this.webContents.on('did-start-loading', () => {
console.log('🔄 开始加载...')
this.updateLoadingUI(true)
})
this.webContents.on('did-finish-load', () => {
console.log('✅ 加载完成')
this.updateLoadingUI(false)
})
this.webContents.on('did-fail-load', (event, code, desc) => {
console.log(`❌ 加载失败: ${desc}`)
this.showErrorUI(desc)
})
this.webContents.on('render-process-gone', (event, details) => {
console.log(`💥 进程消失: ${details.reason}`)
if (details.reason === 'crashed') {
this.showCrashUI()
}
})
}
updateLoadingUI(isLoading) { /* ... */ }
showErrorUI(error) { /* ... */ }
showCrashUI() { /* ... */ }
}
8.2 导航拦截
javascript
复制代码
class NavigationGuard {
constructor(webContents, allowedDomains) {
this.webContents = webContents
this.allowedDomains = allowedDomains
this.setupGuard()
}
setupGuard() {
this.webContents.on('will-navigate', (event, url) => {
const parsedUrl = new URL(url)
if (!this.allowedDomains.includes(parsedUrl.hostname)) {
event.preventDefault()
console.log(`⛔ 阻止跳转到: ${url}`)
}
})
this.webContents.on('will-redirect', (event, url) => {
console.log(`🔄 重定向到: ${url}`)
// 可以在这里检查重定向目标
})
}
}
// 使用
const guard = new NavigationGuard(win.webContents, [
'myapp.com',
'api.myapp.com'
])
8.3 自动刷新页面
javascript
复制代码
class HotReload {
constructor(webContents) {
this.webContents = webContents
this.enabled = false
}
enable() {
this.enabled = true
this.webContents.on('did-finish-load', () => {
if (this.enabled) {
this.webContents.reload()
}
})
}
disable() {
this.enabled = false
}
}
8.4 注入脚本
javascript
复制代码
async function injectScripts(webContents) {
// 等待页面加载完成
await webContents.whenReady()
// 注入自定义脚本
await webContents.executeJavaScript(`
// 添加样式
const style = document.createElement('style')
style.textContent = '.debug { border: 1px solid red; }'
document.head.appendChild(style)
// 添加功能
window.showDebug = true
// 返回结果
true
`)
console.log('脚本注入完成')
}
8.5 窗口打开控制
javascript
复制代码
webContents.setWindowOpenHandler(({ url, frameName, features, disposition }) => {
console.log('window.open 被调用:')
console.log(' URL:', url)
console.log(' 名称:', frameName)
console.log(' 方式:', disposition)
// 返回配置来允许或拒绝
if (url.startsWith('https://trusted.com')) {
// 允许在新窗口打开
return {
action: 'allow',
overrideBrowserWindowOptions: {
width: 800,
height: 600,
title: frameName
}
}
} else {
// 在当前窗口打开
return { action: 'deny' }
}
})
9. 安全考虑
9.1 危险操作
javascript
复制代码
// ⚠️ 谨慎使用
webContents.setIgnoreMenuShortcut(true) // 忽略菜单快捷键
webContents.pageZoomIn() // 页面缩放
webContents.enablePlugin() // 启用插件
webContents.allowRunningInsecureContent = true // 允许不安全内容
9.2 推荐的验证方式
javascript
复制代码
// ✅ 推荐:在执行 JavaScript 前验证
webContents.executeJavaScript(code).then(result => {
// 处理结果
}).catch(error => {
console.error('执行失败:', error)
})
// ✅ 推荐:使用 webPreferences 限制
const win = new BrowserWindow({
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
sandbox: true
}
})
// ✅ 推荐:验证 URL
webContents.on('will-navigate', (event, url) => {
try {
const parsed = new URL(url)
if (parsed.protocol !== 'https:') {
event.preventDefault()
}
} catch (e) {
event.preventDefault()
}
})
事件速查表
导航事件
| 事件 |
可取消 |
说明 |
will-navigate |
✅ |
主 frame 导航前 |
will-frame-navigate |
✅ |
任意 frame 导航前 |
will-redirect |
✅ |
重定向前 |
did-start-navigation |
❌ |
导航开始 |
did-redirect-navigation |
❌ |
重定向完成 |
did-frame-navigate |
❌ |
frame 导航完成 |
did-navigate |
❌ |
主 frame 导航完成 |
did-navigate-in-page |
❌ |
页面内导航 |
加载事件
| 事件 |
说明 |
did-start-loading |
开始加载 |
did-stop-loading |
停止加载 |
did-finish-load |
加载完成 |
did-fail-load |
加载失败 |
dom-ready |
DOM 就绪 |
进程事件
| 事件 |
说明 |
render-process-gone |
渲染进程消失 |
unresponsive |
页面无响应 |
responsive |
页面恢复响应 |
crashed |
进程崩溃(废弃) |
窗口事件
| 事件 |
说明 |
did-create-window |
新窗口创建 |
content-bounds-updated |
窗口边界更新 |
will-prevent-unload |
阻止卸载 |
方法速查表
页面操作
javascript
复制代码
webContents.loadURL(url, options) // 加载 URL
webContents.loadFile(path) // 加载文件
webContents.reload() // 刷新
webContents.goBack() // 后退
webContents.goForward() // 前进
webContents.stop() // 停止加载
开发者工具
javascript
复制代码
webContents.openDevTools() // 打开
webContents.closeDevTools() // 关闭
webContents.isDevToolsOpened() // 是否打开
执行代码
javascript
复制代码
webContents.executeJavaScript(code) // 执行 JS
webContents.print(options) // 打印
webContents.savePage(path, type) // 保存页面
消息通信
javascript
复制代码
webContents.send(channel, ...args) // 发送消息
webContents.postMessage(channel, data) // 发送消息(CDP)
文档基于 Electron v28+ WebContents API 编写