【发布时间】:2019-06-06 21:44:54
【问题描述】:
我使用的是 <webview> 标签而不是 iFrame,但找不到关于 NWJS docs、Electron docs 的更多详细信息,也找不到关于访问其中内容的实际 <webview> docs 的详细信息。
我想从<webview> 中检索document.title,并将其发送回主进程。
【问题讨论】:
我使用的是 <webview> 标签而不是 iFrame,但找不到关于 NWJS docs、Electron docs 的更多详细信息,也找不到关于访问其中内容的实际 <webview> docs 的详细信息。
我想从<webview> 中检索document.title,并将其发送回主进程。
【问题讨论】:
我在主进程和<webview> 内部进行通信的基本解决方案是使用Webview 的ContentWindow.postMessage() 方法。这与window.postMessage() 非常相似。通过使用postMessage()——特别是跟踪event.source——我们在主进程和<webview>之间建立了一个通信桥梁。
const webview = document.getElementById('your-webview-element');
// <webview> Content is loaded
function contentload() {
// The following will be injected in the webview
const webviewInjectScript = `
var data = {
title: document.title,
url: window.location.href
};
function respond(event) {
event.source.postMessage(data, '*');
}
window.addEventListener("message", respond, false);
`;
webview.executeScript({
code: webviewInjectScript
});
}
// <webview> Loading has finished
function loadstop() {
webview.contentWindow.postMessage("Send me your data!", "*"); // Send a request to the webview
}
// Bind events
webview.addEventListener("contentload", contentload);
webview.addEventListener("loadstop", loadstop);
window.addEventListener("message", receiveHandshake, false); // Listen for response
function receiveHandshake(event) {
// Data is accessible as event.data.*
// This is the custom object that was injected during contentload()
// i.e. event.data.title, event.data.url
console.log(event.data)
// Unbind EventListeners
removeListeners();
}
// Remove all event listeners
function removeListeners() {
webview.removeEventListener("contentload", contentload);
webview.removeEventListener("loadstop", loadstop);
window.removeEventListener("message", receiveHandshake);
}
它是如何工作的(至少我找到了一种方法):
<webview> 和窗口(以便稍后监听来自 <webview> 内的消息)<webview> 元素加载 URL 时,会触发 contentload() contentload() 将向 <webview> 注入一个 EventListener 并设置我们想要从 <webview> 内部获取的数据/DOM 元素。<webview> 完成加载,它就会触发loadstop()
loadstop() 将向<webview> 发送消息以建立桥接。需要注意的是,这里我使用webview.contentWindow.postMessage()而不是window.postMessage()。<webview> 以我们在步骤 1 中设置的数据进行响应<webview>的响应(通过EventListener“消息”)时,它会触发receiveHandshake()
receiveHandshake() 内部,您现在可以访问来自<webview> 内部的数据。这可以是页面标题,也可以是您在 webviewInjectScript 中配置的任何内容。removeListeners() 删除我们设置的所有 EventListener,但您可以继续来回发送消息。仅供参考——在 Electron 和 NWJS 的上下文中,<webview> 标签允许您渲染网站(如 iframe),其好处是它在单独的进程中运行。这比一堆 iframe 的性能要好得多。 <webview> 包含一个标准的 HTML 文档,比 iframe 更复杂的是因为它在单独的进程中运行。
还有another thread和其他一些解决方案,比如使用IPC消息,使用preload标签。
还有另一种更适合 Electron 的方法。在下面的代码中,我使用的是 Vue (2) 和 Webpack,但与上述实现的主要区别是:
ipcRenderer 和 ipcMain 发送消息,而不是 postMessage
preload 属性加载注入脚本,而不是使用executeScript()。我已将 :preload 值绑定到 Vue 计算属性 (injectScript),该属性返回外部 injectWebPageScript.js 文件的路径。components/myComponent.vue
<template>
<webview ref="frame" class="frame" :preload="injectScript"/>
</template>
<script>
export default {
computed: {
injectScript() {
const appPath = require("electron").remote.app.getAppPath();
return `file://${require("path").resolve(
__dirname,
"../../mixins/injectWebPageScript.js"
)}`;
}
},
methods: {
mySiteLoaderScript(url) {
const frame = this.$refs.frame;
// Initialize event listeners on the Webview
addListeners();
// Set the URL, start loading
frame.setAttribute("src", url);
// Bind events
function addListeners() {
frame.addEventListener("dom-ready", contentloaded);
frame.addEventListener("ipc-message", receiveHandshake);
}
// Remove all event listeners
function removeListeners() {
frame.removeEventListener("dom-ready", contentloaded);
frame.removeEventListener("ipc-message", receiveHandshake);
}
// Once webview content is loaded, request its data
function contentloaded() {
frame.send("requestData");
}
// Triggered when we receive a response from the Webview
// This is the `ipc-message` event
function receiveHandshake(event) {
// Only listen to replyData messages
if (event.channel !== "replyData") return false;
const data = event.args[0];
const title = data.title;
const favicon = data.favicon;
// Remove listeners once data has been received
removeListeners();
}
}
},
mounted() {
this.mySiteLoaderScript("https://stackoverflow.com");
}
};
</script>
mixins/injectWebPageScript.js
const { ipcRenderer } = require("electron");
// Once the Webview's document has been loaded, notify the ipcRenderer
document.addEventListener("DOMContentLoaded", () => {
ipcRenderer.on("requestData", () => {
ipcRenderer.sendToHost("replyData", {
title: document.title,
url: window.location.href
});
});
});
【讨论】: