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


多謝閱讀~~

相關文章
相關標籤/搜索