Electron的配置以及自动更新

诸新霁
2023-12-01

更新依赖

npm install --registry=https://registry.npm.taobao.org
手动明跳转路由
location.hash = '#/chat'
手动获取当前路由
window.location.href
electron常用配置
"build": {
    "productName":"xxxx",//项目名 这也是生成的exe文件的前缀名
    "appId": "com.leon.xxxxx",//包名  
    "copyright":"xxxx",//版权  信息
    "directories": { // 输出文件夹
      "output": "build"
    }, 
    "nsis": {
      "oneClick": false, // 是否一键安装
      "allowElevation": true, // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。
      "allowToChangeInstallationDirectory": true, // 允许修改安装目录
      "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脚本
    },
    "publish": [
      {
        "provider": "generic", // 服务器提供商 也可以是GitHub等等
        "url": "http://xxxxx/" // 服务器地址
      }
    ],
    "files": [
      "dist/electron/**/*"
    ],
    "dmg": {
      "contents": [
        {
          "x": 410,
          "y": 150,
          "type": "link",
          "path": "/Applications"
        },
        {
          "x": 130,
          "y": 150,
          "type": "file"
        }
      ]
    },
    "mac": {
      "icon": "build/icons/icon.icns"
    },
    "win": {
      "icon": "build/icons/aims.ico",
      "target": [
        {
          "target": "nsis",
          "arch": [
            'x64',// 64位
            'ia32' // 32位
          ]
        }
      ]
    },
    "linux": {
      "icon": "build/icons"
    }
  }

安装包打入向导

  • 下载(使用此方法,代码打包时关闭nsis配置)
  • NSIS中文版下载地址:https://pan.baidu.com/s/1mitSQU0
官方说使用asar pack ./index.html app.asar --unpack *.node 得到app.asar可以防止杀毒软件的注意,但是我用过之后并没有效果,都躲不过360的注意,但是不影响流程,如果有人解决了360误报的问题,希望可以告诉我,谢谢 ^_^

vue.config.js配置

  pluginOptions: {
    electronBuilder: {
      nodeIntegration: true,
      builderOptions: {
        asar: false, // 是否把app文件夹压缩成app.asar
        win: {
          icon: './public/app.ico',
          target: [
            {
              target: 'nsis', // 利用nsis制作安装程序
              arch: [
                // 'x64', // 64位
                'ia32' // 32位
              ]
            }
          ]
        },
        nsis: {
          oneClick: false, // 是否一键安装
          // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。
          allowElevation: true,
          perMachine: false,
          allowToChangeInstallationDirectory: true, // 允许修改安装目录
          installerIcon: './public/app.ico', // 安装图标
          uninstallerIcon: './public/app.ico', // 卸载图标
          installerHeaderIcon: './public/app.ico', // 安装时头部图标
          createDesktopShortcut: true, // 创建桌面图标
          createStartMenuShortcut: true, // 创建开始菜单图标
          shortcutName: name // 图标名称
        },
        mac: {
          icon: './public/app.ico'
        },
        publish: [
          {
            provider: 'generic',
            url: 'http://xxxx/' // 版本服务器地址
          }
        ],
        productName: 'YueGongJia', // 包名
        appId: 'com.gongyoushijie.ttgy.electron-vue', // 项目id
        copyright: '最终解释权归长时科技所有', // 版权信息
        files: ['./**/*'],
        directories: {
          output: './dists' // 输出文件路径
        }
      }
    }
  }

electron配置(package.json)

"electron:build": "vue-cli-service electron:build",
"electron:serve": "vue-cli-service electron:serve",
"postinstall": "electron-builder install-app-deps",
"postuninstall": "electron-builder install-app-deps",
"main": "background.js",

electron配置(background.js)

'use strict'

'use strict'

import { app, protocol, BrowserWindow, globalShortcut, Menu, ipcMain } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
const isDevelopment = process.env.NODE_ENV !== 'production'
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
  {
    scheme: 'app',
    privileges: {
      secure: true,
      standard: true
    }
  }
])
let win = null
// let loadingWindow = null
// function createLoadingWindow() { // 加载页面窗口
//   loadingWindow = new BrowserWindow({
//     height: 200,
//     useContentSize: true,
//     width: 200,
//     show: true,
//     transparent: true,
//     backgroundColor: '#000',
//     maximizable: false, // 禁止双击放大
//     frame: false // 去掉顶部操作栏
//   })
//   // eslint-disable-next-line no-undef
//   const loadingURL = `${__static}/loading.html`
//   loadingWindow.loadURL(loadingURL)
//   Menu.setApplicationMenu(null)
//   loadingWindow.on('closed', () => {
//     loadingWindow = null
//   })
// }
async function createWindow() {
  // 创建浏览器窗口
  win = new BrowserWindow({
    width: 800,
    height: 600,
    show: false,
    backgroundColor: '#000',
    opacity: 0,
    webPreferences: {
      webSecurity: false,
      // Use pluginOptions.nodeIntegration, leave this alone
      // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
      nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION
    },
    // eslint-disable-next-line no-undef
    icon: `${__static}/app.ico`
  })
  if (process.env.WEBPACK_DEV_SERVER_URL) {
    // 如果处于开发模式,请加载开发服务器的网址
    await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
    if (!process.env.IS_TEST) win.webContents.openDevTools()
  } else {
    createProtocol('app')
    // 不在开发中时加载index.html
    win.loadURL('app://./index.html')
  }
  if (!isDevelopment) {
    createMenu()
  }
  win.maximize()
  win.on('ready-to-show', () => {
    win.setOpacity(1.0)
  })
  // mainWindow要关闭时的方法↓
  win.on('close', e => {
    e.preventDefault() // 先阻止一下默认行为,不然直接关了,提示框只会闪一下
    win.webContents.send('appClose')
    ipcMain.on('appClose', () => {
      win = null
      app.exit()
    })
  })
  win.on('closed', () => {
    win = null
  })
}
// 设置菜单栏
function createMenu() {
  // darwin表示macOS,针对macOS的设置
  if (process.platform === 'darwin') {
    const template = [
      {
        label: 'App Demo',
        submenu: [
          {
            role: 'about'
          },
          {
            role: 'quit'
          }
        ]
      }
    ]
    const menu = Menu.buildFromTemplate(template)
    Menu.setApplicationMenu(menu)
  } else {
    // windows及linux系统
    Menu.setApplicationMenu(null)
  }
}
// close httpcache
app.commandLine.appendSwitch('--disable-http-cache')
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
// eslint-disable-next-line space-before-function-paren
app.on('ready', async () => {
  // createLoadingWindow()
  createWindow()
  ipcMain.on('close-loading-window', (e, res) => {
    if (res.isClose) {
      //  if (loadingWindow) loadingWindow.close()
      win.show()
    }
  })
  if (isDevelopment && !process.env.IS_TEST) {
    // eslint-disable-next-line space-before-function-paren
    globalShortcut.register('F10', function() {
      win.webContents.openDevTools()
    })
  }
})
app.on('activate', () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (BrowserWindow.getAllWindows().length === 0) createWindow()
})

// 关闭所有窗口后退出。
app.on('window-all-closed', () => {
  // 在macOS上,应用程序及其菜单栏很常见
  // 保持活动状态,直到用户使用Cmd + Q明确退出为止
  if (process.platform !== 'darwin') {
    app.quit()
  }
})
// Exit cleanly on request from parent process in development mode.
if (isDevelopment && !process.env.IS_TEST) {
  if (process.platform === 'win32') {
    process.on('message', data => {
      if (data === 'graceful-exit') {
        app.quit()
      }
    })
  } else {
    process.on('SIGTERM', () => {
      app.quit()
    })
  }
}

// 自动更新
import { autoUpdater } from 'electron-updater'
ipcMain.on('update', () => {
  if (isDevelopment) {
    return
  }
  updateHandle()
})
// 检测更新,在你想要检查更新的时候执行,renderer事件触发后的操作自行编写
function updateHandle() {
  const message = {
    error: '检查更新出错',
    checking: '检查更新中……',
    updateAva: '更新可用',
    updateNotAva: '现在已是最新版本'
  }
  // 如下应用程序的路径请自行替换成自己应用程序的路径
  const updateFeedUrl = 'http://xxxx/'
  // if (process.platform === 'darwin') {
  //   updateFeedUrl = 'http://xxxx//'
  // }
  autoUpdater.setFeedURL(updateFeedUrl)
  // eslint-disable-next-line space-before-function-paren
  autoUpdater.on('error', function() {
    sendUpdateMessage(message.error)
  })
  // eslint-disable-next-line space-before-function-paren
  autoUpdater.on('checking-for-update', function() {
    sendUpdateMessage(message.checking)
  })
  // eslint-disable-next-line space-before-function-paren
  autoUpdater.on('update-available', function() {})
  // 没有可用更新的时候触发。
  // eslint-disable-next-line space-before-function-paren
  autoUpdater.on('update-not-available', function() {
    sendUpdateMessage(message.updateNotAva)
  })
  // 更新下载进度事件
  // eslint-disable-next-line space-before-function-paren
  autoUpdater.on('download-progress', function(progressObj) {
    win.webContents.send('downloadProgress', progressObj)
    win.setProgressBar(progressObj.percent / 100)
  })
  // eslint-disable-next-line space-before-function-paren
  autoUpdater.on('update-downloaded', function() {
    sendUpdateMessage('UpdataNow')
    ipcMain.on('isUpdataNow', () => {
      // 退出并安装更新包
      autoUpdater.quitAndInstall()
    })
  })
  // })

  // 执行自动更新检查
  autoUpdater.checkForUpdates()
}
// 通过main进程发送事件给renderer进程,提示更新信息
function sendUpdateMessage(text) {
  win.webContents.send('updateMessage', text)
}

自动更新的app.vue

<template>
  <div id="app">
    <router-view />
    <div v-if="upDateStatus" class="upDate">
      <el-progress :text-inside="true" :percentage="percent" :stroke-linecap="'butt'" :stroke-width="20" :format="format" show-text color="#00d8ff" />
    </div>

  </div>

</template>

<script>
import { ipcRenderer } from 'electron'
export default {
  name: 'App',
  data() {
    return {
      percent: 0,
      upDateStatus: false
    }
  },
  beforeMount() {
    ipcRenderer.send('close-loading-window', {
      isClose: true
    })
  },
  mounted() {
    const _this = this
    /** 发起更新*/
    ipcRenderer.send('update')
    /** 监听下载进度*/
    ipcRenderer.on('downloadProgress', (e, data) => {
      if (!_this.upDate) {
        _this.upDateStatus = true
      }
      _this.percent = Number(data.percent).toFixed(2)
      console.log(data.percent)
    })
    /** 安装包下载完时提示*/
    ipcRenderer.on('updateMessage', (e, data) => {
      if (data === 'UpdataNow') {
        _this.upDateStatus = false
        this.$confirm('发现新版本,是否现在更新?', '提示', {
          confirmButtonText: '更新',
          cancelButtonText: '取消',
          cancelButtonClass: 'cancelButtonClass',
          confirmButtonClass: 'confirmButtonClass'
        }).then(() => {
          ipcRenderer.send('isUpdataNow')
        })
      }
    })
    /** 下载时关闭程序提示*/
    ipcRenderer.on('appClose', () => {
      if (_this.upDateStatus) {
        this.$confirm(`应用更新中(${_this.percent}%),退出后将要重新更新?`, '提示', {
          confirmButtonText: '退出',
          cancelButtonText: '取消',
          cancelButtonClass: 'cancelButtonClass',
          confirmButtonClass: 'confirmButtonClass'
        }).then(() => {
          ipcRenderer.send('appClose')
        })
      } else {
        ipcRenderer.send('appClose')
      }
    })
  },
  methods: {
    format(percentage) {
      return percentage === 100 ? '下载完成,请重启' : `下载进度${percentage}%,请勿关闭`
    }
  }
}
// 阻止拖拽文件的默认事件
document.addEventListener(
  'dragover',
  // eslint-disable-next-line space-before-function-paren
  function (e) {
    // console.log(e)
    e.preventDefault()
  },
  false
)

document.addEventListener(
  'drop',
  // eslint-disable-next-line space-before-function-paren
  function (e) {
    // console.log(e)
    e.preventDefault()
  },
  false
)
// rem 自适应
// eslint-disable-next-line space-before-function-paren
;(function () {
  const html = document.documentElement
  // 设置默认主题
  const body = document.querySelector('body')
  body.setAttribute('data-theme', 'default')
  function setFont() {
    const cliWidth = html.clientWidth
    html.style.fontSize = 16 * (cliWidth / 1920) + 'px'
  }
  setFont()
  // eslint-disable-next-line space-before-function-paren
  window.onresize = function () {
    setFont()
  }
})()
</script>
<style lang="scss">
@import './styles/index.scss'; // 全局自定义的css样式
</style>
<style lang="scss" scoped>
.upDate {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  opacity: 0.5;
}
::v-deep .el-progress-bar__outer,
::v-deep .el-progress-bar__inner {
  border-radius: 0 !important;
}
::v-deep .el-progress-bar__innerText {
  color: #000 !important;
  font-weight: bold;
}
</style>

 类似资料: