vue + electron + electron-builder 桌面应用

通过electron创建一个浏览器框架,在框架里运行vue打包好的dist文件

1、准备工作

assets 目录存放 vue打包后的文件
icon 目录下备好 后缀为ico宽高为256*256 图标

注:ico 图标请使用工具进行转换,请勿随意更改后缀名,图片大小不宜过大(icon处理不妥当会直接影响到后续的打包)

目录结构
.
├── assets(dist文件)
│   ├── static
│   │   └── ...
│   └── index.html
├── icon(icon图标)
│   ├── icon.ico (windows打包图标)
│   ├── icon.icns(mac 打包图标)
│   ├── icon.png(mac dock图标)
│   ├── icon.sh(脚本)
│   └── logo-1024.png(生成icon.png原图)
├── main.js (主文件入口)
└── package.json

2、icon.sh(mac生成图标)

echo "用法:sh icon.sh logo-1024.png icon.icns"

PNG=$1
OUT=$2
if [ -z "$PNG" ] || [ -z "$OUT" ];then
	echo "[error]:please input png filename and out filename!"
	exit
fi
if [ ! -d "icons.iconset" ];then
	echo "[info]:create dir icons.iconset"
	mkdir icons.iconset
else
	echo "[error]:icons.iconset dir already exists"
exit
fi
 
sips -z 16 16 $PNG --out icons.iconset/icon_16x16.png
sips -z 32 32 $PNG --out icons.iconset/icon_16x16@2x.png
sips -z 32 32 $PNG --out icons.iconset/icon_32x32.png
sips -z 64 64 $PNG --out icons.iconset/icon_32x32@2x.png
sips -z 64 64 $PNG --out icons.iconset/icon_64x64.png
sips -z 128 128 $PNG --out icons.iconset/icon_64x64@2x.png
sips -z 128 128 $PNG --out icons.iconset/icon_128x128.png
sips -z 256 256 $PNG --out icons.iconset/icon_128x128@2x.png
sips -z 256 256 $PNG --out icons.iconset/icon_256x256.png
sips -z 512 512 $PNG --out icons.iconset/icon_256x256@2x.png
sips -z 512 512 $PNG --out icons.iconset/icon_512x512.png
sips -z 1024 1024 $PNG --out icons.iconset/icon_512x512@2x.png
iconutil -c icns icons.iconset -o $OUT
rm -rf icons.iconset
echo "[info]:complated!"

3、package.json配置

{
  "name": "", //项目名称(文件未加载前,会默认显示该名称)
  "version": "1.0.0", //项目版本号(用于升级版本)
  "description": "Hello World!", //项目描述
  "author": "", //作者
  "license": "", //许可证
  "private": true, //npm拒绝发布(防止意外发布私有存储库)
  "main": "main.js", //主文件入口
  "scripts": {
    "dev": "electron .", //运行
    "build": "electron-builder" //打包
  },
  "build": {
    "productName": "", //打包项目名称 Mac
    "appId": "",
    "asar": true,
    "directories": {
      "output": "out" //输出文件夹
    },
    "publish": [
      {
        "provider": "generic", // 服务器提供商,GitHub等等
        "url": "" // 服务器地址(用于自动更新)
      }
    ],
    "nsis": { //nsis相关配置,windows生效
      "oneClick": false, // 是否一键安装
      "allowElevation": true, // 允许请求提升,如果为false,则用户必须使用提升的权限重新启动安装程序。
      "allowToChangeInstallationDirectory": true, // 允许修改安装目录
      "installerIcon": "./icon/icon.ico", // 安装图标
      "uninstallerIcon": "./icon/icon.ico", //卸载图标
      "installerHeaderIcon": "./icon/icon.ico", // 安装时头部图标
      "createDesktopShortcut": true, // 创建桌面图标
      "createStartMenuShortcut": true, // 创建开始菜单图标
      "shortcutName": "" // 打包名称 Windows
    },
    "files": [ //需要打包的文件目录
      "assets",
      "icon",
      "main.js"
    ],
    "win": {
      "icon": "./icon/icon.ico",
      "artifactName": "${productName}-${version}.${ext}" //打包生成的安装名称
      "target": [
        "nsis"
      ]
    },
    "mac": {
      "icon": "./icon/icon.icns",
      "artifactName": "${productName}-${version}.${ext}" //打包生成的安装名称
    }
  },
  "dependencies": {
    "request": "^2.88.2",
    "electron-updater": "^4.3.9"
  },
  "devDependencies": {
    "electron": "^13.1.8",
    "electron-builder": "^22.11.7"
  }
}

4、main.js



const { app, BrowserWindow, Menu, dialog, ipcMain } = require('electron');//引入electron
const fs = require('fs');
const path = require('path');
const request = require('request');

//检测版本更新
const { autoUpdater } = require('electron-updater');
// 检查版本更新的url
autoUpdater.setFeedURL("http://demo.tsinghong.com:8092/electron");

let win;
// https://www.electronjs.org/docs/api/browser-window 文档具体BrowserWindow配置信息
let windowConfig = {
  width: 1080, //窗口的宽度
  height: 620, //窗口的高度
  center: true, //窗口是否在屏幕居中
  // icon: 'icon/icon.ico', //窗口图标
  icon: path.join(__dirname, 'icon/icon.ico'),
  show: false,//隐藏窗口,默认true(这里隐藏用作处理窗口最大化)
  autoHideMenuBar: true,//是否隐藏菜单栏(只在windows有效)
  // titleBarStyle: 'hidden', //是否隐藏边框 mac
  // frame: false,//隐藏边框
  webPreferences: {
    nativeWindowOpen: true, //是否使用原生的window.open(). 默认值为 false.
    nodeIntegration: true,
    contextIsolation: false
  }
};
// mac模式下更换icon图
if (process.platform === 'darwin') {
  app.dock.setIcon(path.join(__dirname, 'icon/icon.png'));
}
//窗口配置程序运行窗口的大小
function createWindow() {
  win = new BrowserWindow(windowConfig);//创建一个窗口
  //在窗口内要展示的内容index.html 就是打包生成的index.html
  // win.loadURL(`file://${__dirname}/assets/index.html`);
  win.loadURL('https://baidu.com');

  // 菜单配置(主要处理mac复制粘贴失效问题)
  const template = [
    {
      label: '编辑',
      submenu: [{
        label: '剪切',
        accelerator: 'CmdOrCtrl+X',
        role: 'cut'
      }, {
        label: '复制',
        accelerator: 'CmdOrCtrl+C',
        role: 'copy'
      }, {
        label: '粘贴',
        accelerator: 'CmdOrCtrl+V',
        role: 'paste'
      }, {
        label: '刷新',
        accelerator: 'F5',
        click: (item, focusedWindow) => {
          if (focusedWindow) {
            if (focusedWindow.id === 1) {
              BrowserWindow.getAllWindows().forEach((win) => {
                if (win.id > 1) {
                  win.close();
                }
              });
            }
            focusedWindow.reload();
          }
        }
      }]
    },
    {
      label: 'Window ( 窗口 )',
      role: 'window',
      submenu: [{
        label: 'Minimize ( 最小化 )',
        accelerator: 'CmdOrCtrl+M',
        role: 'minimize'
      }, {
        label: 'Close ( 关闭 )',
        accelerator: 'CmdOrCtrl+W',
        role: 'close'
      }, {
        label: '切换开发者工具',
        accelerator: (function () {
          if (process.platform === 'darwin') {
            return 'Alt+Command+I';
          } else {
            return 'F12';
          }
        })(),
        click: (item, focusedWindow) => {
          if (focusedWindow) {
            focusedWindow.toggleDevTools();
          }
        }
      }, {
        type: 'separator'
      }]
    },
  ];
  var menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);
  win.maximize(); //窗口最大化
  win.once('ready-to-show', () => {
    win.show(); //显示窗口
  });
  // win.webContents.openDevTools();  //开启调试工具
  // 创建窗口监听
  win.webContents.on('new-window', (event, url, frameName, disposition, options, additionalFeatures) => {
    console.log('url', url);
    if (disposition == 'foreground-tab') {
      event.preventDefault();
      // 获取导出路径文件名称(主要处理mac导出文件,文件名不显示)
      // 获取导出路径文件名称
      let defaultPath = JSON.parse(JSON.stringify(url)).split('/');
      // 文件选择弹窗
      dialog.showSaveDialog(win, {
        filters: [
          { name: 'All Files', extensions: ['*'] }
        ],
        properties: ['openDirectory'],
        defaultPath: decodeURI(defaultPath[1]),//文件名称
      }).then(res => {
        console.log('filePath', res);
        // 获取用户选择的保存文件路径
        if (res.filePath) {
          let arr = res.filePath.split('\\');
          // 分割用户选择的保存路径和文件名称
          let filename = arr[arr.length - 1];
          arr.splice(arr.length - 1, 1);
          let filePath = arr.join('\\');
          // 下载网络文件到指定保存路径
          let stream = fs.createWriteStream(path.join(filePath, filename));
          request(url).pipe(stream);
        }
      }).catch(err => {
        console.log(err);
      });
    }
  });
  win.webContents.session.on('will-download', (event, item, webContents) => {
    //设置文件存放位置,如果用户没有设置保存路径,Electron将使用默认方式来确定保存路径(通常会提示保存对话框)
    // item.setSavePath(savepath + item.getFilename());
    item.on('updated', (event, state) => {
      if (state === 'interrupted') {
        console.log('Download is interrupted but can be resumed');
      } else if (state === 'progressing') {
        if (item.isPaused()) {
          console.log('Download is paused');
        } else {
          console.log(`Received bytes: ${item.getReceivedBytes()}`);
        }
      }
    });
    item.once('done', (event, state) => {
      if (state === 'completed') {
        console.log('Download successfully');
        //回显 调用渲染进程方法
        win.webContents.send('downstate', state);
      } else {
        console.log(`Download failed: ${state}`);
        //回显 调用渲染进程方法
        win.webContents.send('downstate', state);
      }
    });
  });
  win.on('close', (e) => {
    if (process.platform !== 'darwin') {
      win = null;
    } else {
      // mac隐藏窗口
      // e.preventDefault(); 加上这个之后无法Dock右键退出
      win.hide();
    }
  });
  // 当窗口关闭时调用的方法
  win.on('closed', () => {
    // 解除窗口对象的引用,通常而言如果应用支持多个窗口的话,你会在一个数组里
    // 存放窗口对象,在窗口关闭的时候应当删除相应的元素。
    win = null;
  });
  win.on('resize', () => {
    win.reload();
  });
  //检测版本更新
  function sendUpdateMessage(text) {
    win.webContents.send('message', text);
  }
  //监听升级失败事件
  autoUpdater.on('error', (error) => {
    sendUpdateMessage({
      cmd: 'error',
      message: error
    });
  });
  //监听开始检测更新事件
  autoUpdater.on('checking-for-update', (message) => {
    sendUpdateMessage({
      cmd: 'checking-for-update',
      message: message
    });
  });
  //监听发现可用更新事件
  autoUpdater.on('update-available', (message) => {
    sendUpdateMessage({
      cmd: 'update-available',
      message: message
    });
  });
  //监听没有可用更新事件
  autoUpdater.on('update-not-available', (message) => {
    sendUpdateMessage({
      cmd: 'update-not-available',
      message: message
    });
  });

  // 更新下载进度事件
  autoUpdater.on('download-progress', (progressObj) => {
    sendUpdateMessage({
      cmd: 'download-progress',
      message: progressObj
    });
  });
  //监听下载完成事件
  autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName, releaseDate, updateUrl) => {
    sendUpdateMessage({
      cmd: 'update-downloaded',
      message: {
        releaseNotes,
        releaseName,
        releaseDate,
        updateUrl
      }
    });
    //退出并安装更新包
    autoUpdater.quitAndInstall();
  });

  //接收渲染进程消息,开始检查更新
  ipcMain.on("checkForUpdate", (e, arg) => {
    //执行自动更新检查
    autoUpdater.checkForUpdates();
  });
}

// 当Electron完成初始化并且已经创建了浏览器窗口,则该方法将会被调用。// 有些API只能在该事件发生后才能被使用。
app.on('ready', createWindow);
// 当所有的窗口被关闭后退出应用
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});
// activate 只会在mac 触发
app.on('activate', () => {
  // 对于OS X系统,当dock图标被点击后会重新创建一个app窗口,并且不会有其他窗口打开
  if (win == null) {
    createWindow();
  } else {
    win.show();
  }
});

5、自动更新

<template>
  <div>
    <router-view />
    <!-- electron -->
    <el-dialog title="正在更新最新版本,请稍候..." :visible.sync="dialogVisible" width="40%" :close-on-click-modal="false" :close-on-press-escape="false" :show-close="false" center>
      <el-progress status="success" :text-inside="true" :stroke-width="20" :percentage="percentage" :width="strokeWidth"></el-progress>
    </el-dialog>
  </div>
</template>

<script>
var ipcRenderer = '';
if (window.require) {
  ipcRenderer = window.require("electron").ipcRenderer;
}
export default {
  data() {
    return {
      // electron自动升级
      dialogVisible: false,
      percentage: 0,
      strokeWidth: 200,
      timeInterval: null
    };
  },
  mounted() {
    if (ipcRenderer) {
      //进入程序开始检测新版本
      ipcRenderer.send("checkForUpdate");
      //开启程序后 间隔1小时检测一次
      this.timeInterval = setInterval(() => {
        ipcRenderer.send("checkForUpdate");
      }, 3600000);
      ipcRenderer.on("message", (event, arg) => {
        if ("update-available" == arg.cmd) {
          //显示升级对话框
          this.dialogVisible = true;
        } else if ("download-progress" == arg.cmd) {
          //更新升级进度
          // 模拟数据{
          //     bytesPerSecond: 47673,
          //     delta: 48960,
          //     percent: 0.11438799862426002,
          //     total: 42801693,
          //     transferred: 48960,
          // }
          let percent = Math.round(parseFloat(arg.message.percent));
          this.percentage = percent;
        } else if ("error" == arg.cmd) {
          this.dialogVisible = false;
        }
      });
    }
  },
  methods: {
  },
  destroyed() {
    if (this.timeInterval) {
      clearInterval(this.timeInterval);
    }
  },
};
</script>
<style>
</style>

6、运行指令

npm run dev

7、打包指令

npm run build
需要用windows和mac电脑打包对应exe和dmg安装程序
注:windows打包时请注意关闭电脑管家、360等安全卫士,查看当前电脑账号是否有权限修改文件,否则会出现打包失败问题
打包成功后查看out目录下 对应安装程序

  • name-1.0.0.exe //windows
  • name-1.0.0.dmg //mac
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值