通过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