vue项目打包成exe桌面程序

总结

主要执行以下命令

全局安装electron:npm install electron -g

安装electron-builder依赖:vue add electron-builder

运行桌面程序:npm run electron:serve

打包应用程序:npm run electron:build

更新应用程序:npm i electron-updater --save

electron-builder 教程

一、本人比较笨,拷贝原始项目后,安转依赖并启动

执行命令:npm install

执行命令:npm run serve

二、全局安装electron:

执行命令:npm install electron -g

三、安装electron-builder依赖:

执行命令:vue add electron-builder

electron-builder安装成功后项目目录如下:

electron-builder安装成功后项目目录

会发现目录里多了一个background.js 的文件,并且package.json 里多了依赖和执行命令其中这个“main”: “background.js” 是electron的入口文件

四、运行桌面程序

执行命令:npm run electron:serve

执行以上命令后项目启动速度较慢,可以注释掉background.js文件中await installExtension(VUEJS_DEVTOOLS) 这一句代码

注释掉background.js文件中内容

项目运行后显示界面如下:

运行后的桌面程序


五、打包

执行命令:npm run electron:build

执行打包命令后报错:"directories" in the root is deprecated, please specify in the "build",如下图显示:

打包报错

解决办法,删掉“directories”这部分内容:

删除红线圈住内容

打包成功后exe程序所在位置:

根目录/dist_electron/win-unpacked/vue-antd-pro.exe ,如下图所示:

打包成功后exe程序所在位置

六、在vue.config.js中module.exports中添加pluginOptions配置:更换exe应用程序中的名称和图标

打包教程地址:https://blog.csdn.net/Assassin_EZI0/article/details/107144377

pluginOptions配置

执行打包命令:npm run electron:build重新打包后

七、更新应用程序 参考网址

客户安装应用以后,如果有新功能更新,那客户那边如何更新?解决方案,安装:electron-updater

执行命令:npm i electron-updater --save

介绍一下原理:使用electron-updater, 打包后会生成一个latest.yml 的文件,里面包含了版本号等描述信息,

并且electron-updater 提供了一系列监听事件,允许应用向服务器检测当前版本是否可以更新,

如果可以更新,在对应的监听事件里做了相关处理(下载最新版本,下载完成后退出并重新安装)

 在目录下新增一个目录_main, 里面有两个文件,events.js(定义了事件名称) 和 updater.js (处理接收)和 helper.js(是封装的持久化数据相关操作方法)。

helper.js

import { join } from 'path'

import fs from 'fs'

import { app } from 'electron'

const dataPath = join(app.getPath('userData'), 'data.json')

export function getLocalData(key) {

  if (!fs.existsSync(dataPath)) {

    fs.writeFileSync(dataPath, JSON.stringify({}), { encoding: 'utf-8' })

  }

  let data = fs.readFileSync(dataPath, { encoding: 'utf-8' })

  let json = JSON.parse(data)

  return key ? json[key] : json

}

export function setLocalData(key, value) {

  let args = [...arguments]

  let data = fs.readFileSync(dataPath, { encoding: 'utf-8' })

  let json = JSON.parse(data)

  if (args.length === 0 || args[0] === null) {

    json = {}

  } else if (args.length === 1 && typeof key === 'object' && key) {

    json = {

      ...json,

      ...args[0],

    }

  } else {

    json[key] = value

  }

  fs.writeFileSync(dataPath, JSON.stringify(json), { encoding: 'utf-8' })

}

export async function sleep(ms) {

  return new Promise((resolve) => {

    const timer = setTimeout(() => {

      resolve()

      clearTimeout(timer)

    }, ms)

  })

}

events.js:

// ipc通信事件, main 和 render都会用

export default {

downLoadUpdate: 'downLoadUpdate', // 手动下载更新

checkUpdate: 'checkUpdate', // 请求检查更新

startCheckUpdate: 'startCheckUpdate', // 开始检查更新

checkUpdateError: 'checkUpdateError', // 检查更新出错

checkingUpdate: 'checkingUpdate', // 正在检查更新

updateAvailable: 'updateAvailable', // 有新版本更新

updateNotAvailable: 'updateNotAvailable', // 没有新版本更新

updateDownloading: 'updateDownloading', // 正在下载中

updateDownloaded: 'updateDownloaded' // 下载完成

}

updater.js:

import { autoUpdater } from 'electron-updater'

// import { ipcMain } from 'electron'

// import logger from 'electron-log'

import Events from './events'

import { getLocalData, setLocalData, sleep } from './helper' // 是封装的持久化数据相关操作方法。

import { app, dialog } from 'electron'

// // 主进程接收渲染进程(页面)派发过来的 检测更新事件

// ipcMain.on(Events.checkUpdate, () => {

//  // 向服务端查询现在是否有可用的更新。在调用这个方法之前,必须要先调用 setFeedURL

//  autoUpdater.checkForUpdates()

// })

// // 主进程接收渲染进程(页面)派发过来的 下载更新事件

// ipcMain.on(Events.downLoadUpdate, () => {

//  autoUpdater.downloadUpdate()

// })

export async function listenUpdater(mainWindow, feedUrl) {

  autoUpdater.setFeedURL(feedUrl)

  await sleep(5000)

  //每次启动自动更新检查 更新版本 --可以根据自己方式更新、定时或者什么

  autoUpdater.checkForUpdates()

  autoUpdater.autoDownload = false

  // 当更新发生错误的时候触发

  autoUpdater.on('error', function(error) {

    mainWindow.webContents.send(Events.checkUpdateError, JSON.stringify(error))

  })

  // 当开始检查更新的时候触发

  autoUpdater.on('checking-for-update', function() {

    mainWindow.webContents.send(Events.checkingUpdate)

    // const { version } = info

    // askUpdate(version)

  })

  // 当发现一个可用更新的时候触发,更新包下载会自动开始

  autoUpdater.on('update-available', function(info) {

    mainWindow.webContents.send(Events.updateAvailable, info)

    // logger.info('检查到有更新,开始下载新版本')

    // logger.info(info)

    const { version } = info

    askUpdate(version)

  })

  // 当没有可用更新的时候触发

  autoUpdater.on('update-not-available', function(info) {

    mainWindow.webContents.send(Events.updateNotAvailable, info)

  })

  // 更新下载进度事件

  autoUpdater.on('download-progress', function(progressObj) {

    mainWindow.webContents.send(Events.updateDownloading, progressObj)

  })

  // 下载完成

  autoUpdater.on('update-downloaded', function() {

    // mainWindow.webContents.send(Events.updateDownloaded)

    // autoUpdater.quitAndInstall()

    // logger.info('下载完毕!提示安装更新')

    // logger.info(res)

    //dialog 想要使用,必须在BrowserWindow创建之后

    dialog

      .showMessageBox({

        title: '升级提示!',

        message: '已为您下载最新应用,点击确定马上替换为最新版本!',

      })

      .then(() => {

        // logger.info('退出应用,安装开始!')

        //重启应用并在下载后安装更新。 它只应在发出 update-downloaded 后方可被调用。

        mainWindow.webContents.send(Events.updateDownloaded)

        autoUpdater.quitAndInstall()

      })

  })

}

async function askUpdate(version) {

  // logger.info(`最新版本 ${version}`)

  let { updater } = getLocalData()

  let { auto, version: ver, skip } = updater || {}

  // logger.info(

  //  JSON.stringify({

  //    ...updater,

  //    ver: ver,

  //  })

  // )

  if (skip && version === ver) return

  if (auto) {

    // 不再询问 直接下载更新

    autoUpdater.downloadUpdate()

  } else {

    const { response, checkboxChecked } = await dialog.showMessageBox({

      type: 'info',

      buttons: ['关闭', '跳过这个版本', '安装更新'],

      title: '软件更新提醒',

      message: `最新版本是 ${version},您现在的版本是 ${app.getVersion()},现在要下载更新吗?`,

      defaultId: 2,

      cancelId: -1,

      checkboxLabel: '以后自动下载并安装更新',

      checkboxChecked: false,

      textWidth: 300,

    })

    if ([1, 2].includes(response)) {

      let updaterData = {

        version: version,

        skip: response === 1,

        auto: checkboxChecked,

      }

      setLocalData({

        updater: {

          ...updaterData,

        },

      })

      if (response === 2) autoUpdater.downloadUpdate()

      // logger.info(['更新操作', JSON.stringify(updaterData)])

    } else {

      // logger.info(['更新操作', '关闭更新提醒'])

    }

  }

}

在background.js 里执行监听

ackground.js 里执行监听


每次更新时只需把exe文件和 latest.yml 文件放到服务器上就行了!

为了打包时生成latest.yml文件,须要在 build 参数中添加 publish 配置

八:自定义electron窗口导航栏

方法一:自定义electron窗口

1、在background.js文件中找到BrowserWindow中设置frame为false,关闭原生导航栏

background.js文件中找到BrowserWindow中设置frame为false

2、在App.vue中引入窗口组件mainHeader.vue

App.vue中引入窗口组件mainHeader.vue

3、mainHeader.vue代码如下

注意:使用上述代码方法时需要注意在background.js文件中找到BrowserWindow将enableRemoteModule设置为true

enableRemoteModule设置为true

方法二:Electron自定义导航栏

1、在主进程background.js文件中配置frame: false  关闭原生导航栏

配置frame: false  关闭原生导航栏

2、在App.vue中引入窗口组件mainHeader.vue

App.vue中引入窗口组件mainHeader.vue

3、mainHeader.vue代码如下

4、在主进程background.js中执行ipcMain方法。

import {BrowserWindow, ipcMain} from 'electron'

mainWindow = new BrowserWindow({

        height: 690,

        useContentSize: true,

        width: 1100,

        minWidth: 1100,

        minHeight: 690,

        frame: false

    })

ipcMain.on('min', () => mainWindow.minimize());

ipcMain.on('max', () => {

    if (mainWindow.isMaximized()) {

        mainWindow.restore();

    } else {

        mainWindow.maximize()

    }

});

ipcMain.on('close', () => mainWindow.close());

主进程background.js文件中设置

九:自定义桌面应用程序后,浏览器端无法识别window,客户端无法启动,因此可以检查代码是否正在Electron或浏览器中执行,来判断是否引用window.require('electron')

检查代码是否正在Electron或浏览器中执行

export function isElectron() {

    // Renderer process

    if (typeof window !== 'undefined' && typeof window.process === 'object' && window.process.type === 'renderer') {

        return true;

    }

    // Main process

    if (typeof process !== 'undefined' && typeof process.versions === 'object' && !!process.versions.electron) {

        return true;

    }

    // Detect the user agent when the `nodeIntegration` option is set to true

    if (typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Electron') >= 0) {

        return true;

    }

    return false;

}

在mainHeader组件中引入

import {isElectron} from '@/utils/isElectron'

if(isElectron()){//检查代码是否正在Electron或浏览器中执行    var {remote}=window.require('electron')    console.log("Electron中执行 !");}else{    console.log("浏览器中执行");}

如图所示:

检查代码是否正在Electron或浏览器中执行

十:去掉导航栏菜单

需要去掉的导航栏菜单
主要代码

十一:electron修改vue项目打包后的exe图标

注意:以下生成图标的地址和打包牡蛎都在dist_electron文件中

1、准备一张icon.png图片保存在public目录下

2.安装 electron-icon-builder

npm i electron-icon-builder --D

3、在package.json的scripts文件中添加一条命令并保存:

"electron:generate-icons": "electron-icon-builder --input=./public/icon.png --output=dist_electron --flatten"

3、执行以下命令

npm run electron:generate-icons

就会在dist_electron文件夹中生成一系列打包所需的图标文件,如下:

4、在vue.config.json中pluginOptions对象下配置图标路径,其中directories 为打包地址,win为图标路径。

你可能感兴趣的:(vue项目打包成exe桌面程序)