【Electron】vue+electron与webview间的通信

3,100 阅读3分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

一、前言

在electron应用中,会集成许多第三方应用,比如某些模块是单独的开发的,需要嵌套在electron应用的工作台中,而面对这种情况,我在electron官方文档中找到了<webview>标签,在一个独立的 frame 和进程里显示外部 web 内容electron文档webview。这里有些需要注意的点,

默认情况下,Electron >= 5禁用 webview 标签。 在构造 BrowserWindow 时,需要通过设置 webviewTag webPreferences选项来启用标签。 更多信息请参看 BrowserWindows 的构造器文档

在集成外部应用到electron应用中后,有时会不可避免的,需要实现第三方应用与electron应用本身的通信。比如第三方应用想要从应用内独立出来,由独立窗口方式显示,又或者需要第三方应用内跳转至另一个应用内等,接下来我就给大家讲述下electron应用与webview间的通信。

二、实施方案

要实现两者间的通信,我们需要一下几个Api方法。

  1. <webview>标签的监听事件ipc-message
  2. ipcRenderer模块的方法ipcRenderer.sendToHost(channel, ...args)
  3. <webview>标签的属性preload

好了,关键方法说完了,接下来咱们就开始实现。

首先我们先写一个给webview中第三方应用预置方法的文件。

// webviewPreload.js
const { ipcRenderer } = require('electron')
/**
 * @description 第三方应用向webview发送信息
 * @param {String} channel 事件名称
 * @param {String} params 传递的参数
 */
global.sendMessageToHost = ({channel = 'ping', args}) => {
  ipcRenderer.sendToHost(channel, params)
}

然后我们将这个文件路径存入全局变量中,这里我将webviewPreload.js文件放在了public文件夹中。

这里要注意preload仅支持file:asar:协议

如果不写file协议或者用相对路径引入就会报如下错误Only "file:" protocol is supported in "preload" attribute.,因此一定要写全路径并加上协议。

// background.js

global.shareObject = {
    webviewPreload = `file://${__static}/webviewPreload.js`
}

之后我们在electron应用的webview标签处注入这个js文件,并设置相应的监听

<template>
    <webview id="myWebView" :src="outsideUrl" nodeintegration allowpopups :preload="preloadPath" />
</template>
import { remote } from "electron";
export default {
    data () {
        return {
            outsideUrl: '',
            preloadPath: ''
        }
    },
    created () {
        this.preloadPath = remote.getGlobal('shareObject').webviewPreload
    },
    mounted () {
        this.eventHandler()
    },
    methods: {
        eventHandler () {
          const webview = document.querySelector('#myWebView')
          webview.addEventListener('ipc-message', event => {
            // 这里我们就能接收到嵌入这个webview标签的应用发出的事件和参数了
            console.log('来自远方的呼唤', event.channel, '=>', event.args)
          })
        }
    }
}

接下来是第三方应用调用的

<template>
  <div @click="useWebview()">我是按钮</div>
</template>
<script setup>
const useWebview = () => {
  let params = {
      channel: '协议好的事件名称'args: '参数'
  }
  try {
    // 调用注入方法
    global.sendMessageToHost(params)
  } catch (error) {
    console.log(error)
  }
}
</script>

以上 我们就实现了electron应用与嵌入到webview中的第三方应用通信功能。

三、后记

说来惭愧,在没有看到这个方法之前,我都是用另外一种很奇怪的方式,去实现的与webview通信的方式。

铛~ 铛~ 铛!!!

这个方法就是webview的另一个监听,就是console-message具体查看,以下是官网内容

Event: 'console-message'

返回:

  • level Integer - 日志级别,从0到3。 按顺序匹配 verboseinfowarning 和 error.
  • message string - 实际控制台消息
  • line Integer - 触发控制台消息的源代码行号。
  • sourceId string

Fired when the guest window logs a console message.

The following example code forwards all log messages to the embedder's console without regard for log level or other properties.

这里我是怎么做的呢,就是通过这个监听,跟第三方应用的开发者约定好,相关协议内容,在进行判断他要做什么,如下

<template>
    <webview id="myWebView" :src="outsideUrl" allowpopups />
</template>
export default {
    data () {
        return {
            outsideUrl: ''
        }
    },
    mounted () {
        this.eventHandler()
    },
    methods: {
        eventHandler () {
          const webview = document.querySelector('#myWebView')
          webview.addEventListener('console-message', event => {
            console.log('Guest page logged a message:', e.message)
            // 格式
            // project(项目名) - action(事件) - params(参数)
            // project@{action: *****, params: {key: value}}
            const index = e.message.indexOf('@')
            const projectName = e.message.split('@')[0]
            const content = index !== -1 ? e.message.substr(index + 1) : ''
            if (projectName === 'public') {
              const defaultContent = JSON.parse(content)
              if (defaultContent.action === '约定的协议') {
                // 要做的事情
              }
            }
          })
        }
    }
}

第三方应用那边是这样写的

userWebview () {
    const obj = { 
        action: '协议,比如getList',
        params: '参数' 
    } 
    console.error('public@' + JSON.stringify(obj))
}

这就是我之前的实现方式,虽然怪异,但是实现了,哈哈哈~ 本篇完结撒花! 感谢观看!希望能帮助到你!