Electron 快速入门

1,126 阅读5分钟

是什么

Electron 使用 JavaScript,HTML 和 CSS,来构建跨平台的桌面应用程序。

讲什么

  1. 安装
  2. 创建一个应用程序
  3. 主进程
  4. 渲染进程
  5. 主进程 send 渲染进程
  6. 渲染进程 send 主进程
  7. 渲染进程 send 渲染进程
  8. 原生菜单操作
  9. 文件读写
  10. 打包部署

安装

npm install electron --S

创建一个应用程序

demo/
├── package.json
├── main.js
└── index.html
  1. main.js
const {app, BrowserWindow} = require('electron')
const iconPath = path.join(__dirname, './src/img/icon.png')   //应用运行时的标题栏图标
let win
app.on('ready', function createWindow () {
    // 可以创建多个渲染进程
    win = new BrowserWindow({
        width: 800,
        height: 600,
        frame: false, // 创建无边框窗口
        resizable: false,   //不允许用户改变窗口大小
        icon: iconPath,     //应用运行时的标题栏图标
        fullscreen: true, // 默认全屏显示
        webPreferences:{    
          backgroundThrottling: false,   //设置应用在后台正常运行
          nodeIntegration:true,     //设置能在页面使用nodejs的API
          contextIsolation: false,
          preload: path.join(__dirname, './preload.js') // 预加载
        }
    })
    win.removeMenu() // 去掉菜单栏
    win.show()
    // 渲染进程中的web页面可以加载本地文件
    win.loadFile('index.html')
    // 在页面被关闭后清除该变量,防止内存泄漏
    win.on('closed', function () {
        win = null
    })


})

// 页面全部关闭后关闭主进程,不同平台可能有不同的处理方式
app.on('window-all-closed', () => {
    app.quit()
})
  1. index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>demo</title>
</head>
<body>
    <div>一个electron项目</div>
    <script>
    
    </script>
</body>
</html>

主进程

任何 Electron 应用程序的入口都是 main 文件。 这个文件控制了主进程,它运行在一个完整的Node.js环境中,负责控制您应用的生命周期,显示原生界面,执行特殊操作并管理渲染器进程

  1. 运行package.json中的main脚本的进程是主进程。
  2. 一个electron应用有且只有一个主进程。
  3. 主进程可以进行GUI相关的原生API操作。

渲染进程

每个web页面运行的环境就是渲染进程

  1. 使用BrowserWindow类开启一个渲染进程并将这个实例运行在该进程中
  2. 当一个BrowserWindow实例被销毁后,相应的渲染进程也会被终止。
  3. 渲染进程中不能调用原生资源,但是渲染进程中同样包含Node.js环境,所以可以引入Node.js 模块, 通过 ** webPreferences: { nodeIntegration: true, } **

主进程 send 渲染进程

  1. 主进程 main.js
// 主进程向渲染进程发送消息
win.webContents.on('onload', () => {
    win.webContents.send('msg', '消息来自主进程')
})
  1. 渲染进程 index.html
const {ipcRenderer} = require('electron')
ipcRenderer.on('msg', (event, message) => {
    console.log(message) // 消息来自主进程
})

渲染进程 send 主进程

  1. 主进程 main.js
const {ipcMain} = require('electron')
ipcMain.on('indexMsg',(event,msg) => {
    console.log(msg) //消息来自渲染进程
})
  1. 渲染进程 index.html
const {ipcRenderer} = require('electron')
ipcRenderer.send('indexMsg','消息来自渲染进程')

渲染进程 send 渲染进程

  1. 使用全局共享属性
//主进程
global.config = {
  user: ''
}

//渲染进程一
const {remote} = require('electron')
remote.getGlobal('config').user = 'wxh'

//渲染进程二
const {remote} = require('electron')
console.log(remote.getGlobal('config').user) //wxh
  1. 利用主进程做消息中转站
//渲染进程1
ipcRenderer.send('connect', '来自渲染进程1的消息')

//主进程
ipcMain.on('connect', (event, message) => {
  win.webContents.send('sendTo', message);
}

//渲染进程2
ipcRenderer.on('sendTo', (event, message) => {
    console.log(message)  //来自渲染进程1的消息
  }
)

原生菜单操作

菜单的参数说明:

当设置了 frame: false, 的时候,无边框窗口是不可拖拽的。应用程序需要在 CSS 中指定 -webkit-app-region: drag 来告诉 Electron 哪些区域是可拖拽的。

// 整个窗口都可拖拽,
html,body {
  height: 100%;
  width: 100%;
}

body{
  -webkit-app-region: drag;
}

关闭窗口

win.on('closed', () => { win = null })

系统托盘

程序启动时,将应用程序加入系统托盘。在Electron中,借助Tray模块实现。

const { app, BrowserWindow, Tray, Menu } = electron
const iconPath = path.join(__dirname, './src/img/icon.png')
let win, tray
app.on('ready', () => {
  // ...  
  tray = new Tray(iconPath)      //实例化一个tray对象
  
  tray.setToolTip('hello exe')       //鼠标移到托盘中应用程序的图标上时,显示的文本
  
  tray.on('click', () => {       //点击图标的响应事件,这里是切换主窗口的显示和隐藏
    if(win.isVisible()){
      win.hide()
    }else{
      win.show()
    }
  })
  
  tray.on('right-click', () => {    //右键点击图标时,出现的菜单,通过Menu.buildFromTemplate定制,这里只包含退出程序的选项。
    const menuConfig = Menu.buildFromTemplate([
      {
        label: 'Quit',
        click: () => app.quit()
      }
    ])
    tray.popUpContextMenu(menuConfig)
  })
})

窗口位置设置

const { app, BrowserWindow, Tray, Menu } = electron

function createRemindWindow (task) {
  //创建提醒窗口
  remindWindow = new BrowserWindow({
    //...options
  })
  
  //获取屏幕尺寸
  const size = screen.getPrimaryDisplay().workAreaSize
  
  //获取托盘位置的y坐标(windows在右下角,Mac在右上角)
  const { y } = tray.getBounds()
  
  //获取窗口的宽高
  const { height, width } = remindWindow.getBounds()
  
  //计算窗口的y坐标
  const yPosition = process.platform === 'darwin' ? y : y - height
  
  //setBounds设置窗口的位置
  remindWindow.setBounds({
    x: size.width - width,     //x坐标为屏幕宽度 - 窗口宽度
    y: yPosition,
    height,
    width 
  })
  
  //当有多个应用时,提醒窗口始终处于最上层
  remindWindow.setAlwaysOnTop(true)
  
  remindWindow.loadURL(`file://${__dirname}/src/index.html`)
}

文件读写

Electron读写本地文件其实和nodeJs差不多,只是中间加了一层API

  1. 主进程 main.js
import { app, BrowserWindow, Menu,ipcMain } from 'electron';
ipcMain.on('asynchronous-file', function(event, arg) {
   // arg是从渲染进程返回来的数据
  console.log(arg);
  // fs.writeFile(path.join(__dirname, "../renderer/data/student.json"),JSON.stringify(arg), "utf8",(err)=>{});
  fs.readFile(path.join(__dirname,"../renderer/data/student.json"),"utf8",(err,data)=>{
  	if(err){
      event.sender.send('asynchronous-reply', "读取失败");
    }else{
      event.sender.send('asynchronous-reply', data);
    } 
  })
});
  1. 渲染进程 index.js
// 渲染进程
const ipcRenderer = require("electron").ipcRenderer;
ipcRenderer.on("asynchronous-reply", function(event, arg) {
    // 这里的arg是从主线程请求的数据
  	console.log("render+" + arg);
});

// 这里的会传递回给主进程,这里的第一个参数需要对应着主进程里on注册事件的名字一致
ipcRenderer.send("asynchronous-file", "传递回去值");

打包部署

  1. electron-packager

我们可以利用 electron-packager把我们现有的electron应用打包为exe可执行文件。

npm install electron-packager --save-dev

配置 package.json

// package.json
"scripts": {
  "win32": "electron-packager ./ appDemo --platform=win32 --arch=ia32  --out=./app --app-version=1.0.0 --overwrite --icon=./favicon.ico",
  "win64": "electron-packager ./ appDemo --platform=win32 --arch=x64  --out=./app --app-version=1.0.0 --overwrite --icon=./favicon.ico"
}
  1. electron-builder

electron-builder不仅可以打包为exe可执行文件,还可以打包为可安装程序。

yarn add electron-builder --dev

配置 package.json

"scripts": {
  "pack": "electron-builder --dir",
  "dist": "electron-builder"
},
 "build": {
   "productName": "appDemo", // app中文名称
   "appId": "appDemoId",// app标识
   "directories": { // 打包后输出的文件夹
     "buildResources": "resources",
     "output": "dist/"
   }
   "files": [ // 打包后依然保留的源文件
     "dist/electron",
     "node_modules/",
     "package.json"
   ],
   "mac": { // mac打包配置
     "target": "dmg",
     "icon": "icon.ico"
   },
   "win": { // windows打包配置
     "target": "nsis",
     "icon": "icon.ico"
   },
   "dmg": { // dmg文件打包配置
     "artifactName": "appDemo.dmg",
     "contents": [
       {
         "type": "link",
         "path": "/Applications",
         "x": 410,
         "y": 150
       },
       {
         "type": "file",
         "x": 130,
         "y": 150
       }
     ]
   },
   "nsis": { // nsis文件打包配置
     "oneClick": false,
     "allowToChangeInstallationDirectory": true, // 允许修改安装目录
     "allowElevation": true, // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。
     "installerIcon": "./build/icons/aaa.ico",// 安装图标
     "uninstallerIcon": "./build/icons/bbb.ico",//卸载图标
     "installerHeaderIcon": "./build/icons/aaa.ico", // 安装时头部图标
     "createDesktopShortcut": true, // 创建桌面图标
     "createStartMenuShortcut": true,// 创建开始菜单图标
     "shortcutName": "xxxx", // 图标名称
     "include": "build/script/installer.nsh", //包含的自定义nsis脚本这个对于构建需求严格得安装过程相当有用。
   },
 }

参考

  1. www.electronjs.org/docs/tutori…
  2. electron-react-boilerplate:electron + react: github.com/electron-re…
  3. electron-vue: electron + vue : github.com/SimulatedGR…