【问题标题】:How can I access the contents of a <webview> (Electron/NW.JS)?如何访问 <webview> (Electron/NW.JS) 的内容?
【发布时间】:2019-06-06 21:44:54
【问题描述】:

我使用的是 &lt;webview&gt; 标签而不是 iFrame,但找不到关于 NWJS docsElectron docs 的更多详细信息,也找不到关于访问其中内容的实际 &lt;webview&gt; docs 的详细信息。

我想从&lt;webview&gt; 中检索document.title,并将其发送回主进程。

【问题讨论】:

    标签: webview electron nwjs


    【解决方案1】:

    我在主进程和&lt;webview&gt; 内部进行通信的基本解决方案是使用Webview 的ContentWindow.postMessage() 方法。这与window.postMessage() 非常相似。通过使用postMessage()——特别是跟踪event.source——我们在主进程和&lt;webview&gt;之间建立了一个通信桥梁。

    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);
    }
    

    它是如何工作的(至少我找到了一种方法):

    1. 首先,将主进程中的 EventListener 绑定到 &lt;webview&gt; 和窗口(以便稍后监听来自 &lt;webview&gt; 内的消息)
    2. &lt;webview&gt; 元素加载 URL 时,会触发 contentload()
    3. contentload() 将向 &lt;webview&gt; 注入一个 EventListener 并设置我们想要从 &lt;webview&gt; 内部获取的数据/DOM 元素。
    4. 一旦&lt;webview&gt; 完成加载,它就会触发loadstop()
    5. loadstop() 将向&lt;webview&gt; 发送消息以建立桥接。需要注意的是,这里我使用webview.contentWindow.postMessage()而不是window.postMessage()
    6. &lt;webview&gt; 以我们在步骤 1 中设置的数据进行响应
    7. 当主进程收到来自&lt;webview&gt;的响应(通过EventListener“消息”)时,它会触发receiveHandshake()
    8. receiveHandshake() 内部,您现在可以访问来自&lt;webview&gt; 内部的数据。这可以是页面标题,也可以是您在 webviewInjectScript 中配置的任何内容。
    9. 最后,我调用 removeListeners() 删除我们设置的所有 EventListener,但您可以继续来回发送消息。

    仅供参考——在 Electron 和 NWJS 的上下文中,&lt;webview&gt; 标签允许您渲染网站(如 iframe),其好处是它在单独的进程中运行。这比一堆 iframe 的性能要好得多。 &lt;webview&gt; 包含一个标准的 HTML 文档,比 iframe 更复杂的是因为它在单独的进程中运行。

    还有another thread和其他一些解决方案,比如使用IPC消息,使用preload标签。


    更新:使用前端框架(即 Vue)

    还有另一种更适合 Electron 的方法。在下面的代码中,我使用的是 Vue (2) 和 Webpack,但与上述实现的主要区别是:

    • 使用 Electron ipcRendereripcMain 发送消息,而不是 postMessage
    • 使用Webview 的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
        });
      });
    });
    

    【讨论】:

      猜你喜欢
      • 2016-10-07
      • 2016-02-05
      • 2019-05-28
      • 2018-12-26
      • 1970-01-01
      • 2019-08-08
      • 1970-01-01
      • 2017-08-13
      • 1970-01-01
      相关资源
      最近更新 更多