Electron 的 Windows 通知问题


技术栈

一个很好的跨三端的组合(略有点过于新):
Vue(Cli 3) => Cordova 9 => cordova-electron 1.1.0 (using electron-builder)
注: cordova-electron 1.0.2 对于 icon 处理有问题,请您一定使用 1.1.0。

其实本文对原生 electron 以及 electron-vue 也有意义。不过,如果我没有用 cordova-electron,大概率也就不会遇到这种棘手的多解均不可用的问题,也就不用写这篇文章了。当然,作为一个文档少少的新玩意,文章是 cordova-electron 很需要的。


1. Electron 原生方案

默认的 WebAPI 语法极为简单:

var notification = new Notification(title, {
	body: message,
	icon: require("@/assets/notifyicon.png"),
});

然而,由于一些官方文档也写明了的原因,在 Windows 下这并不可用,你不会收到错误,但不显示通知。需要配置 AppID 才可以,我没有配置成功。(添加了多处,但没有起效果,有谁成功的可以在底下留言一下谢谢)当然也不排除是 cordova-electron 的 bug。

2. Node-Notifier 这个第三方方案

发生了一个问题 net.connect is not a function, 我尝试了两天一无所获,提的 issue 无人理睬(毕竟是个人开发者的项目)。

我的 issue 的地址

3. 迫不得已,手动模拟窗口

其实还有一条路可走: elctron 的 dialog ,但不喜欢,所以手动用窗口模拟。这里的坑点仍然很多,详见下文。

cordova-electron 的 Main 很局促 (/platforms/electron/platform_www/cdv-electron-main.js),更新 platform 还会覆盖, 所以用了一种尽量用 Renderer 的模式渲染窗口(工作量差不多)。

const { remote } = window.electron //这里原因是非 electron 环境不能直接用 electron, 用法见下文
const { BrowserWindow } = remote //放在 export default 外面貌似不行,欢迎交流
const path = require('path')
localStorage.setItem('nftitle', title);//把信息传递到另一个页面的其中一个方法
localStorage.setItem('nfmessage', message);//注意因为是两个窗口,vuex貌似不可以用
let notifyWin = new BrowserWindow({
    frame: false,
    width: 400,
    height: 300,
    webPreferences: {
        nodeIntegration: true,
        webSecurity: false,
    },
});
notifyWin.loadURL(window.location.href + 'notifypage');//这里有来头,用法见下文
notifyWin.on('closed', () => {
    notifyWin = null
});
notifyWin.show();
notifyWin.focus();//让用户收到通知,也可以用 alwaysOnTop 来处理

上文代码一些注释内容的解释:

  • window.electron

    为了能在 vue 这样的非 electron 环境使用 electron,这里需要通过迫不得已涉及了一下 Main Process, 修改 main.js (/platforms/electron/platform_www/cdv-electron-main.js,原生 electron 无此问题, electron-vue 同法),将 browserWindowOpts 改为如下:

      const browserWindowOpts = Object.assign({}, cdvElectronSettings.browserWindow, { icon: appIcon }, {
          webPreferences: {
              nodeIntegration: true,
              preload: `${__dirname}/useelectron.js` 
              //preload 可以这样写因为 cordova-electron 在 build 前 useelectron 和 main 同目录,electron-vue的情况下照着改一改
              //详见:/platforms/electron/www
          }
      });
    

    然后,在 vue-cli3 的 public 目录下,或者 electron-vue/vue-cli2 的对应目录下放入一个 useelectron.js,来创造 window.electron对象。

    内容只需要一行字(但是寻找方法花费了我颇一会儿):

      global.electron = require('electron');
    
  • loadURL

    相当于重新打开一个浏览器页面,内容是同一个项目别的 router 页面。window.location.href + 'notifypage'相当于在原来的页面 URL 后面加上 router 的 path。

    实现方法:

    1.router 部分 export 的 routes 中,加入:

      {
          path: '/notifypage',
          name: 'notifypage',
          component: './views/notifypage', //用vue-cli2的自行对应
      }
    
    1. views下新建notifypage.vue作为新打开的页面 route 到的 view
      <template>
          <div id="container">
              <h5 id="titlebar"><b>Switchable</b></h5>
              <hr>
              <div id="div">
                  <h2 id="title"></h2>
                  <h5 id="message"></h5>
              </div>
              <a id="close" href="javascript:window.close()">OK</a>
          </div>
      </template>
    	
      <script>
      export default {
        name: 'notifypage',
        data() {
          return {
              title: '',
              message: '',
          };
        },
        mounted: function() {
            this.title = localStorage.getItem('nftitle');
            this.message = localStorage.getItem('nfmessage');
        },
        methods: {
    	
        },
      }
      </script>
      <style scoped>
      #container {
          font-family: system, -apple-system, '.SFNSText-Regular', 'SF UI Text', 'Lucida Grande', 'Segoe UI', Ubuntu, Cantarell, sans-serif;
          color: #fff;
          background-color: #8aba87;
          text-align: center;
          width: 100%;
          height: 100%;
          position: absolute;
      }
    	
      #titlebar {
          position: absolute;
          top: 10%;
          left: 50%;
          transform: translate(-50%, -10%);
      }
    	
      #div {
          padding: 0;
          margin: 0;
          position: absolute;
          top: 60%;
          left: 50%;
          transform: translate(-50%, -60%);
      }
    	
      h2,h5 {
          padding: 0;
          margin: 0; 
      }
    	
      #close {
          color: white;
          opacity: 0.7;
          position: absolute;
          bottom: 20px;
          left: 50%;
          transform: translateX(-50%);
          font-size: 12px;
          text-decoration: none;
      }
      </style>
    

这样就可以完成一个窗口的手动模拟了,也可以当作 Windows 下 electron/electron-vue 通知的一个非常可行的解决方案。

4. 题外话:其他端的推荐通知解决方案

Mac & Linux: Electron 原生

iOS & Android: cordova-plugin-local-notificatio

Web:window.Notification


当然,不得不说 cordova-electron 毕竟未成气候,坑还是挺多的,文档还少,我想如果用 electron-vue 问题绝对是不会有这么多的。也许下次用 Quasar 跨端吧。