Electron及Electron-vue 初识

561 阅读6分钟

项目安装

Electron-vue其实就是一个脚手架模板,类似于大家用的vue-cli

vue init simulatedgreg/electron-vue my-project

如需更简洁的项目,请点击此处

项目目录

进程通讯

Electron其实就两个主要的进程,一个主进程(负责桌面应用原生的操作);一个渲染进程(负责页面展示)。

渲染进程向主进程发送消息

// vue 页面中
// 引入通讯模块
import { ipcRenderer } from 'electron'
let msg = '哈哈哈哈'
// 向主进程发送消息
ipcRenderer.send('my-renderer-message', msg)

注:主进程的中运行的都是node.js,所以console都会出现在控制台

// main/index.js
// 引入通讯模块
import { ipcMain } from 'electron'
// 接收渲染进程的消息
ipcMain.on('my-renderer-message'(e, msg) => {
console.log(e, msg)
// do someing
})

主进程向渲染进程发送消息

// main/index.js
// 实例化窗口
let mainWindow = new BrowserWindow({
    height563,
    useContentSizetrue,
    width1000
  })
// 向渲染进程发送消息
mainWindow.webContents.send('my-main-message')
// vue 页面中
// 引入通讯模块
import { ipcRenderer } from 'electron'
// 接收主进程消息
ipcRenderer.on('my-main-message'() => {
 // do someing
})

生命周期

窗口相关

首先会走ready,然后在里面实例化BrowserWindow。并在后面添加了一些相关的监听

注:app.on和mainWindow.on的区别,所有的mainWindow.on必须在BrowserWindow实例化后

// main/index.js
import { app, BrowserWindowfrom 'electron'
let mainWindow
app.on('ready'()=>{
  console.log('---ready----')
  mainWindow = new BrowserWindow({
    height563,
    useContentSizetrue,
    width1000
  })
  // 此处的winURL就是渲染进程的入口
  let winURL = 'http://localhost:9080'
  mainWindow.loadURL(winURL)
  // 窗口变化时触发
  mainWindow.on('resize'(event) => {
   console.log('---mainWindow-resize----')
  })
  // 窗口最大化时触发
  mainWindow.on('maximize'(event) => {
    console.log('---mainWindow-maximize----')
  })
  // 窗口关闭时触发
  mainWindow.on('close'(event) => {
    console.log('---mainWindow-close----')
  })
  // 窗口已经关闭
  mainWindow.on('closed'() => {
    console.log('---mainWindow-closed----')
    mainWindow = null
  })
})
// 应用如果没退出,只是进入后台了,再次启动时会触发activate
app.on('activate'() => {
  console.log('---activate----')
  if (mainWindow === null) {
    createWindow()
  }
})
app.on('window-all-closed'() => {
 console.log('---window-all-closed----')
})
// 调用 event.preventDefault(),可阻止默认行为
app.on('will-quit'() => {
   console.log('---will-quit----')
})
// 调用 event.preventDefault(),可阻止默认行为
app.on('before-quit'() => {
   console.log('---before-quit----')
})
app.on('quit'() => {
   console.log('---quit----')
})

点击应用的关闭按钮

1.如果所有的监听里面都不写app.quit()或app.exit(),打印顺序如下。且进程不会被关闭

---mainWindow-close----
---window-all-closed----
---mainWindow-closed----

2.如果app.quit()写在close中,打印顺序如下。进程会被关闭

---mainWindow-close----
---before-quit----
---mainWindow-close----
---will-quit----
---quit----
---mainWindow-closed----

3.如果app.quit()写在window-all-closed中,打印顺序如下。进程会被关闭

---mainWindow-close----
---window-all-closed----
---before-quit----
---will-quit----
---quit----
---mainWindow-closed----

4.如果app.quit()写在before-quit;will-quit;quit中。效果和第一条一样 5.如果app.quit()写在closed中。打印顺序如下。进程会被关闭

---mainWindow-close----
---window-all-closed----
---mainWindow-closed----
---before-quit----
---will-quit----
---quit----

6.如果使用app.exit(),会在app.exit() 之后打印会直接跳到quit。关闭进程

---mainWindow-close----
// app.exit() 之后会直接到quit并关闭进程
---quit----

结合上面的生命周期做拦截应用的叉叉,做退出确认

大体思路是在主进程的close中,向渲染层发送消息,并阻止默认操作。渲染层收到消息后弹框确认,向主进程发送消息,主进程执行app.exit()。不用app.quit()的原因,是因为app.quit()会再次出发close,流程上会形成死循环。

// main/index.js
// 引入模块
import { app, BrowserWindow, ipcMain } from 'electron'
let mainWindow
app.on('ready'()=>{
  console.log('---ready----')
  mainWindow = new BrowserWindow({
    height563,
    useContentSizetrue,
    width1000
  })
  // 此处的winURL就是渲染进程的入口
  let winURL = 'http://localhost:9080'
  mainWindow.loadURL(winURL)
  // 窗口关闭时触发
  mainWindow.on('close'(event) => {
    console.log('---mainWindow-close----')
    // 通知渲染进程,用户点击了关闭应用
    mainWindow.webContents.send('master-closed')
    // 拦截默认操作
    event.preventDefault()
  })
})
// 监听从渲染进程发过来的消息,并执行退出
ipcMain.on('master-close-app'() => {
  console.log('master-close-app')
  app.exit()
})
// vue页面中
import { ipcRenderer } from 'electron'
mounted () {
  // 监听主进程发过来的消息,并打开弹框
  ipcRenderer.on('master-closed'() => {
    this.dialogShow = true
  })
},
methods: {
  // 点击确认关闭,并通知主进程操作
  doConfirm () {
    ipcRenderer.send('master-close-app')
  },
}

其他监听或方法,请参考官方文档

官方api

打包相关

如果是是直接使用脚手架生成的项目,都已经安装了electron-builder,直接build出来的是免安装的应用。如果需自定义,配置如下

基础配置

// package.json 文件中
"win": {
  "icon": "build/icons/icon.ico",
  "target": [
    {
      "target": "nsis" // 此处可以为数组,同时打出几个类型的包:["nsis","zip","mis"]
    }
  ]
},
"nsis": {
  "oneClick": false, // 是否一键安装,建议为 false,可以让用户点击下一步、下一步、下一步的形式安装程序,如果为true,当用户双击构建好的程序,自动安装程序并打开,即:一键安装(one-click installer)
  "allowElevation": true, // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。
  "allowToChangeInstallationDirectory": true, // 允许修改安装目录,建议为 true,是否允许用户改变安装目录,默认是不允许
  "installerIcon": "", // 安装图标
  "uninstallerIcon": "", // 卸载图标
  "installerHeaderIcon": "", // 安装时头部图标
  "createDesktopShortcut": true, // 创建桌面图标
  "createStartMenuShortcut": true, // 创建开始菜单图标
  "shortcutName": "xxxx", // 快捷方式名字
  "include": "installer.nsh" // 自定义的安装脚本(如没有自定义脚本,不需写该行)
}

自定义安装目录脚本

//installer.nsh 文件中
!macro preInit
    SetRegView 64
    WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "D:\haha"
    WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "D:\haha"
    SetRegView 32
    WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "D:\haha"
    WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "D:\haha"
!macroend

使用NSIS软件

安装使用说明

其他

运行时窗口左上角图标配置

实例化窗口时添加icon,代码如下

// main/index.js 文件中
if (process.env.NODE_ENV !== 'development') {
  global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
}
mainWindow = new BrowserWindow({
    icon: __static + '/icon.icon', // 开发环境不需要__static
    height563,
    autoHideMenuBartrue,
    useContentSizetrue,
    width1000
  })