10月20, 2018

Electron简单笔记

记录一下electron的简单使用笔记。

GUI

  • 图形用户界面(Graphical User Interface,简称 GUI,又称图形用户接口)是指采用图形方式显示的计算机操作用户界面
  • 与 CLI 相比,图形界面对于普通用户在视觉和操作上更加容易接受

vue-cli来说,它的3.x版本新增了一个命令ui

alt

alt

基于Node.js的GUI框架

  • NW.JS(Node-Webkit)
  • Electron

使用HTML、CSS、JavaScript来构建 UI、处理与用户的交互,同时不约而同的使用了开源浏览器 Chromium。

使用 Node.js 来访问 浏览器 之外的内容,比如系统、文件、网络等等……

基本使用

npm install electron
npx electron .

当然可以把下面这一行放到package.jsonscripts里面去:

{
   "scripts": {
    "dev": "electron ."
   },
}

electron的主入口是package.json里面的main所对应的文件:

{
    "main": "index.js",
}

主进程与渲染进程

  • 在 Electron 中,被 Electron 直接运行的脚本(package.json 中指定的 main 脚本)被称为主进程
  • 在 Electron 中用来展示界面的 web 页面都运行在一个独立的,属于它自己的渲染进程中
  • 我们可以通过主进程来创建 web 页面,但一个 web 页面被销毁的时候,对应的渲染进程也会被终止
  • 主进程管理所有的 web 页面和它们对应的渲染进程 一个应用程序有且仅有一个主进程

在 Electron 中,Electron 同时为 主进程 与 渲染进程暴露了 Node.js 的所有接口,也就是说,我们可以在 Electron 的主进程 与 渲染进程 中使用 Node.js 的 API。

同时,在 Electron 中,也提供了大量的 API 去帮助我们开发桌面应用程序,我们可以通过 require('electron') 来引入它们,需要注意的是,API 是区分进程类型的,也就是有的 API 只能在特定(主进程或渲染进程中进行使用)

常用API

app 对象

该对象提供了一系列的事件用来控制整个应用程序的生命周期,从打开到关闭,如: ready、window-all-closed、quit…… 同时也提供了一些方法来管理应用程序的状态与行为,如: quit()、relaunch()、hide()、show()……

const { app } = require('electron');

app.on('ready', () => {
    console.log('ok');
    setTimeout(() => {
        app.exit();
    }, 2000)
});

BrowserWindow 类

创建和控制浏览器窗口

new BrowserWindow( [options] )

options:窗口选项

BrowserWindow 对象

每一个 BrowserWindow 对象的实例都是一个独立的渲染进程,同时该对象也提供了各种用于操控的 API,包括:事件、属性、方法。

实例事件:close、closed、focus、blur、show、hide……

实例属性:

  • webContents:窗口包含的内容对象
  • id:窗口的唯一 ID

实例方法:close()、show()、hide()、maximize()、unmaximize()、setSize()、getSize()、setPosition()、getPosition()、setTitle()、getTitle()等。比较常用的是:

  • loadFile() : 加载页面(这就是我们要显示的内容了),页面地址使用相对路径,相对路径相对于应用程序根目录
  • loadURL() : 使用 URL 协议加载文件,可以是 http 协议,也可以是 file 协议
const { app, BrowserWindow } = require('electron');

app.on('ready', () => {

    let bw1 = new BrowserWindow({
        width: 800,
        height: 600,
        title: '加载中...',
    });


    // 打开调试面板
    bw1.webContents.openDevTools();

    // 加载指定的页面到窗口中,支持绝对路径,但是推荐使用相对
    // 路径,而且路径在解析的时候会被处理,相对路径默认指向
    // 应用程序的根目录
    bw1.loadFile('./test/index.html');

    // 支持加载远程文件,支持http协议,也支持file协议
    // bw1.loadURL('https://www.baidu.com');

});

alt

窗口

  • 一般窗口
    • 无边框窗口
      • BrowserWindow 的 options
        • frame : false
    • 透明窗口
      • BrowserWindow 的 options
        • transparent : true
  • 父子窗口
    • BrowserWindow 的 options
      • parent: 父窗口对象
    • 子窗口永远显示在父窗口的前面
  • 模态窗口
    • BrowserWindow 的 options
      • parent: 父窗口对象
      • modal : true
    • 子窗口会禁用父窗口

菜单

创建原生应用菜单和上下文菜单。

electron的默认菜单是长这样的:

alt

VSCode是用electron来实现的,它的菜单是从这样的:

alt

显然它是重新实现了一套自己的菜单。

const {app, BrowserWindow, Menu, MenuItem} = require('electron');

app.on('ready', () => {

    let bw1 = new BrowserWindow();

    // 创建菜单对象
    let m1 = new Menu();

    // 创建菜单项
    let mi1 = new MenuItem({
        type: 'submenu',
        label: 'SSSSS',
        submenu: [
            {
                type: 'normal',
                label: 'A1'
            },
            {
                type: 'normal',
                label: 'A2'
            }
        ]
    });

    let mi2 = new MenuItem({
        type: 'submenu',
        label: 'BBB',
        submenu: [
            {
                type: 'normal',
                label: 'B1'
            },
            {
                type: 'normal',
                label: 'B2'
            }
        ]
    });

    let mi3 = new MenuItem({
        type: 'normal',
        label: 'CCC'
    });

    // 把菜单项添加到指定的菜单对象中
    m1.append( mi1 );
    m1.append( mi2 );
    m1.append( mi3 );


    Menu.setApplicationMenu(m1)

})

这里其实有两个问题:

  • 第一层显示的label还是Electron
  • 最后一个CCC没有显示出来(可能没有二级菜单,它不显示吧)

alt

注意事项

当菜单有子菜单的时候,父菜单的 type 应设置为 submenu

添加菜单项目到指定菜单

  • 菜单实例.append(菜单项)
  • 菜单实例.insert(位置, 菜单项)

把菜单添加到应用程序顶层

Menu.setApplicationMenu(menu对象)

作为右键(上下文)菜单

  • 菜单实例.popup(options)
    • options:
      • window:指定窗口
      • x/y : 位置
      • callback : 关闭后的回调
  • 菜单实例.closePopup()
    • 关闭上下文菜单

这里通常会有渲染进程(即页面)和主进程间的通信。

使用 Electron 自定义菜单

快速构建菜单项

Menu.buildFromTemplate(template)

template:是一个数组,用于快速构建 MenuItem

数据共享

渲染进程间的通信

  • Storage API
  • IndexedDB

主进程与渲染进程的通信

global的方式

// 主进程
let username = 'zpu';

global.username = username;

渲染进程需要通过 Electron 提供的API:remote

const { remote } = require('electron');      
remote.getGlobal('username');

IPC

渲染进程发送
ipcRenderer
      .send(channel[, arg1][, arg2][, ...])

主进程监听      
ipcMain
  .on(channel, listener)
    listener(e, ...data) : 监听
      e.sender.send() : 回复

主进程主动发送消息到渲染进程
    win.webContents.send()
const {app, BrowserWindow, ipcMain} = require('electron');

let datas = {
    username: 'zpu',
    gender: '男'
}

app.on('ready', () => {

    const win = new BrowserWindow();
    win.webContents.openDevTools();

    win.loadFile('./layout/index.html');

    // 监听渲染进程 ipcRenderer 发送的消息

    ipcMain.on('getData', function(e, key) {
        // console.log(data);

        // e.sender => 通过这个对象返回消息给渲染进程
        e.sender.send('sendData', datas[key]);
   });

    // 主进程主动发送消息到渲染进程
    setTimeout(() => {
        win.webContents.send('hello', 'hello........', 10, 20, 30);
    }, 2000);

});
// 渲染进程,即html页面
const {ipcRenderer} = require('electron');

const buttons = document.querySelectorAll('button');

buttons[0].onclick = function() {
    ipcRenderer.send('getData', 'username');
}

ipcRenderer.on('sendData', function(e, data) {
    console.log(data); // zpu
});

ipcRenderer.on('hello', function(e, ...data) {
    console.log(data); // ['hello........', 10, 20, 30]
});

一些开发时的点

自定义右上角按钮

closeApp() {
    // app对象只能通过主线程调用
    remote.app.exit();
},
// 最小化应用窗口
miniApp() {
    // 通过remote下的一个方法来获取当前窗口对象(BrowserWindow)
    remote.getCurrentWindow().minimize();
},

拖拽

无框窗口 - 拖拽
    -webkit-app-region: drag
取消元素拖拽
    -webkit-app-region: no-drag

vue模板

可以使用electron-vue

打包

electron-builder:
https://github.com/electron-userland/electron-builder
https://www.electron.build/
npm i electron-builder

配置

相关配置

// package.json
{
    build: {
        "productName": "test",
        "directories": {
               "output": "./dist"
        },
        mac: {},
        dmg: {},
        win{},
        nsis{} // => Nullsoft Scriptable Installation System(Nullsoft 脚本安装系统)
        ... 
   }
}

拿mac来说,可以简单地配置成这样:

alt

然后还需要在package.json写入一条脚本:

 "build": "electron-builder -m"
  • -w : windows
  • -m : mac
  • -l : linux
npm run build

在打包成功后,就能在dist目录下看到dmg的文件。我们可以双击运行它,然后可以看到第一个菜单名会变成productName

注意点

打包的时候 electron 必须在 devDependencies 中,因为这样不会被重复打包到软件安装包中

windows的配置

"win": {
  "icon": "./source/logo.ico",
  "target": [
    "nsis",
    "zip"
  ]
},
"nsis": {
  "oneClick": false,
  "allowToChangeInstallationDirectory": true,
  "installerIcon": "./source/logo.ico",
  "installerHeader": "./source/header.bmp",
  "license": "./source/license.txt"
},

结语

以上说的内容基本还是比较简单的,但差不多核心就是这些,剩下的就是看API,抄API,实现具体的功能了。

本文链接:www.my-fe.pub/post/electron-note.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。