electron桌面应用的使用

 不少人知道Vscode这一强大的编辑应用正是electron框架写的,出于兴趣和其方便性,我就把公司的项目进行了一个优化,使用electron 来把项目搞成桌面应用,废话很少说,步骤以下:
html

一: 安装Electronnode

electron 有点大,安装起来有点久,多点耐心哦~react

npm install --save-dev electron复制代码

二: 项目的启动git

开发环境下,在根目录下添加main.js,并附上github

const { app, BrowserWindow } = require('electron')

// 保持对window对象的全局引用,若是不这么作的话,当JavaScript对象被
// 垃圾回收的时候,window对象将会自动的关闭
let win

function createWindow () {
  // 建立浏览器窗口。
  win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  })

  // 加载index.html文件  这里不必定是index.html, 看你目录结构,我react 项目就是'./public/index.html'
  win.loadFile('index.html')

  // 打开开发者工具
  win.webContents.openDevTools()

  // 当 window 被关闭,这个事件会被触发。
  win.on('closed', () => {
    // 取消引用 window 对象,若是你的应用支持多窗口的话,
    // 一般会把多个 window 对象存放在一个数组里面,
    // 与此同时,你应该删除相应的元素。
    win = null
  })
}

// Electron 会在初始化后并准备
// 建立浏览器窗口时,调用这个函数。
// 部分 API 在 ready 事件触发后才能使用。
app.on('ready', createWindow)

// 当所有窗口关闭时退出。
app.on('window-all-closed', () => {
  // 在 macOS 上,除非用户用 Cmd + Q 肯定地退出,
  // 不然绝大部分应用及其菜单栏会保持激活。
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  // 在macOS上,当单击dock图标而且没有其余窗口打开时,
  // 一般在应用程序中从新建立一个窗口。
  if (win === null) {
    createWindow()
  }
})

// 在这个文件中,你能够续写应用剩下主进程代码。
// 也能够拆分红几个文件,而后用 require 导入。复制代码

而后在package.json 上添加命令web

"scripts": {
    "start": "electron ."
  }复制代码

此时运行npm start / yarn start   electron 应用就启起来了,是否是很简单;npm

三, 项目的打包json

electron 应用打包有两种方式:electron-packager 和 electron-builder;segmentfault

一开始我是使用electron-packager,可是后面想作自动更新的功能,就转electron-builder进行打包数组

"package": "electron-packager ./build/ electron-test --all --out ~/ --electron-version 1.0.0",复制代码

后面使用electron-builder  进行打包,并结合electron-updater自动化更新;

3.1 首先就是安装啦:

yarn add electron-builder --dev
yarn add electron-updater
npm install electron-updater --save
复制代码

3.2 配置package.json 文件

注:nsis配置不会影响自动更新功能,可是能够优化用户体验,好比是否容许用户自定义安装位置、是否添加桌面快捷方式、安装完成是否当即启动、配置安装图标等

"build": {    "appId": "****.app",    "copyright": "back-manage",    "productName": "back-manage",    "nsis": {      "oneClick": true,      "perMachine": true,      "allowElevation": true,      "allowToChangeInstallationDirectory": true,      "createDesktopShortcut": true,      "runAfterFinish": true,      "installerIcon": "public/favicon1.png",      "uninstallerIcon": "public/favicon1.png"    },    "publish": [      {        "provider": "generic",        "url": "https://***"      }    ],    "dmg": {      "contents": [        {          "x": 410,          "y": 150,          "type": "link",          "path": "/Applications"        },        {          "x": 130,          "y": 150,          "type": "file"        }      ]    },    "mac": {      "icon": "public/favicon1.png",      "artifactName": "${productName}_setup_${version}.${ext}",      "target": [        "dmg",        "zip"      ]    },    "win": {      "icon": "public/favicon1.png",      "target": [        "nsis",        "zip"      ]    }  },复制代码

3.2 修改主进程main.js 文件(引入 electron-updater 文件,添加自动更新检测和事件监听)

注意,我这里使用的是react,打包这里使用的主进程文件再也不是main.js, 而是public下面的electron.js

warn:  public/electron.js not found. Please see https://medium.com/@kitze/%EF%B8%8F-from-react-to-an-electron-app-ready-for-production-a0468ecb1da3复制代码

我修改后的eletron.js 

// 引入electron并建立一个Browserwindowconst { app, BrowserWindow,Menu,ipcMain } = require('electron');const { autoUpdater } = require('electron-updater');const path = require('path');const url = require('url');const uploadUrl = 'https://***/'; (这个地址是自动更新会去对比这个服务器上的latest.yml,检测
最新版本)// 保持window对象的全局引用,避免JavaScript对象被垃圾回收时,窗口被自动关闭.let mainWindow;// 检测更新,在你想要检查更新的时候执行,renderer事件触发后的操做自行编写function updateHandle() {  const message = {    error: '检查更新出错',    checking: '正在检查更新……',    updateAva: '检测到新版本,正在下载……',    updateNotAva: '如今使用的就是最新版本,不用更新'  };  const os = require('os');  autoUpdater.setFeedURL(uploadUrl);  autoUpdater.on('error', error => {    sendUpdateMessage(message.error);    // sendUpdateMessage(error);  });  autoUpdater.on('checking-for-update', () => {    sendUpdateMessage(message.checking);  });  autoUpdater.on('update-available', info => {    console.log(info)    mainWindow.webContents.send('updateAvailable', '<h3>检测到新版本' + info.version + ',须要升级?</h3>' + info.releaseNotes);    // sendUpdateMessage(message.updateAva);  });  autoUpdater.on('update-not-available', info => {    sendUpdateMessage(message.updateNotAva);  });  // 更新下载进度事件  autoUpdater.on('download-progress', progressObj => {    console.log(progressObj)    const winId = BrowserWindow.getFocusedWindow().id;        let win = BrowserWindow.fromId(winId);        win.webContents.send('downloadProgress', progressObj);  });  autoUpdater.on('update-downloaded', (    event,    releaseNotes,    releaseName,    releaseDate,    updateUrl,    quitAndUpdate  ) => {    console.log(event,      releaseNotes,      releaseName,      releaseDate,      updateUrl,      quitAndUpdate)    console.log('update-downloaded');    ipcMain.on('isUpdateNow', (e, arg) => {      console.log(arguments);      console.log('开始更新');      // some code here to handle event      autoUpdater.quitAndInstall();    });    mainWindow.webContents.send('isUpdateNow');  });  ipcMain.on("isDownload", () => {    autoUpdater.downloadUpdate();})  ipcMain.on('checkForUpdate', () => {    // 执行自动更新检查    autoUpdater.checkForUpdates();  });}// 经过main进程发送事件给renderer进程,提示更新信息function sendUpdateMessage(text) {  mainWindow.webContents.send('message', text);}function createWindow() {  // 建立浏览器窗口,宽高自定义具体大小你开心就好  mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: {webSecurity: false, allowDisplayingInsecureContent: true, allowRunningInsecureContent: true,    nativeWindowOpen: true,      webSecurity: false,      nodeIntegration: true, // 是否完整的支持 node. 默认值为true      nodeIntegrationInWorker: true, // 是否在Web工做器中启用了Node集成      preload: path.join(__dirname, './renderer.js')  }});  /*    * 加载应用-----  electron-quick-start中默认的加载入口    mainWindow.loadURL(url.format({      pathname: path.join(__dirname, 'index.html'),      protocol: 'file:',      slashes: true    }))  */  // 加载应用----适用于 react 项目  //   mainWindow.loadURL('http://localhost:3000/');  // 加载应用----react 打包  mainWindow.loadURL(    url.format({      pathname: path.join(__dirname, './index.html'),      protocol: 'file:',      slashes: true    })    // 'http://192.168.0.11:8082'  );  // 打开开发者工具,默认不打开  // mainWindow.webContents.openDevTools()  // 关闭window时触发下列事件.  mainWindow.on('closed', () => {    mainWindow = null;  });  ipcMain.on('openDev', () => {    mainWindow.openDevTools();  });  updateHandle()}// 当 Electron 完成初始化并准备建立浏览器窗口时调用此方法app.on('ready', createWindow);// 全部窗口关闭时退出应用.app.on('window-all-closed', () => {  // macOS中除非用户按下 `Cmd + Q` 显式退出,不然应用与菜单栏始终处于活动状态.  if (process.platform !== 'darwin') {    app.quit();  }});app.on('activate', () => {  // macOS中点击Dock图标时没有已打开的其他应用窗口时,则一般在应用中重建一个窗口  if (mainWindow === null) {    createWindow();  }});// 你能够在这个脚本中续写或者使用require引入独立的js文件.复制代码

3.3 在项目启动app.js添加相应的检测更新的代码

由于在app.js 中是获取不到electron 的,咱们须要在index.html中注入electron

window.electron = require('electron');复制代码

而后在app.js 

const ipcRenderer = window.electron && window.electron.ipcRenderer;
而后在componentDidMount 或者useEffect 函数中,添加:    const self = this;    if (ipcRenderer) {      ipcRenderer.send('checkForUpdate');      ipcRenderer.on('message', (event, message) => {        console.log(message);      });      // 注意:“downloadProgress”事件可能存在没法触发的问题,只须要限制一下下载网速就行了      ipcRenderer.on('downloadProgress', (event, progressObj) => {        console.log('下载', progressObj);        this.downloadPercent = progressObj.percent || 0;      });      ipcRenderer.on('isUpdateNow', () => {        console.log('是否如今更新');        ipcRenderer.send('isUpdateNow');      });      // 检测到新版本      ipcRenderer.on('updateAvailable', (event, message) => {        console.log(event, message);        self.$notification.open({          message: 'Notification',          description: '检测到新版本,正在更新中……',          onClick: () => {            console.log('Notification Clicked!');          }        });        ipcRenderer.send('isUpdateNow');      });    }

在componentWillUnmount 钩子函数中移除事件在componentWillUnmount 钩子函数中移除事件  componentWillUnmount() {    if (ipcRenderer) {      ipcRenderer.removeAll([        'message',        'downloadProgress',        'isUpdateNow',        'updateAvailable'      ]);    }  }复制代码

万事具有,只差一个执行命令的赶脚了有木有,走你……

可是,window 系统的包是打包好了,可是macOs 的出现了签名问题,若是没有自动更新的问题,用packager 打包出来的是不须要签名的,可是要实现自动更新,签名是必须的,

cannot find valid "Developer ID Application" identity or custom non-Apple code signing certificate复制代码


首先说一下,要处理这个问题,首先你要有个苹果开发者帐号,我的或者公司的都行,我当时花了99美圆买了一个我的开发者帐号,须要里面的cer 证书来生成p12文件

具体能够参考连接: segmentfault.com/a/119000001…

好了,通过这一系列操做事后,你已经很够打包出一个可以自动更新的桌面应用,是否是颇有成就感?

可是,问题,又来了,打包出来的应用没法进行复制黏贴,咋整?

解决:

1. 安装 electron-localshortcut

yarn add electron-localshortcut复制代码

2. 在主进程文件createWindow 函数添加以下代码:

if (process.platform === 'darwin') {    Menu.setApplicationMenu(Menu.buildFromTemplate([]));    const contents = mainWindow.webContents;    const localShortcut = require('electron-localshortcut');    localShortcut.register(mainWindow, 'CommandOrControl+A', () => {      contents.selectAll();    });    localShortcut.register(mainWindow, 'CommandOrControl+C', () => {      contents.copy();    });    localShortcut.register(mainWindow, 'CommandOrControl+V', () => {      contents.paste();    });  }复制代码

最后附上相应的git地址

refer git address: https://github.com/catherine201/electron-example.git


多谢阅读~~

相关文章
相关标签/搜索