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

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

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

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

二、实施方案

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

  • <webview> 标签的监听事件 ipc-message
  • ipcRenderer模块的方法 ipcRenderer.sendToHost(channel, ...args)
  • <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))
    

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

  • 私信