electron 常見需求怎么做

electron

常見需求


  • 在 Renderer 端創(chuàng)建菜單

相關(guān) issue: https://github.com/electron/electron/issues/7455

在 Electron 中,無(wú)論是應(yīng)用程序的主菜單(macOS 頂部的菜單)、窗口菜單(Windows/Linux)的窗口菜單酵幕、Tray 菜單届良、還是 Renderer 端的 Context 菜單等翘贮,凡是和菜單掛鉤的功能都是通過 Menu.buildFromTemplate 進(jìn)行創(chuàng)建的迂曲,他們只是被掛載到了不同的實(shí)體上,比如被掛載到了頁(yè)面上的叫做 Context 菜單佳遂,被 setApplicationMenu 掛載的成為了主菜單营袜。

Electron 的菜單其實(shí)限制也非常之大,其菜單在創(chuàng)建完成后便不能在運(yùn)行時(shí)被直接修改丑罪,需要修改時(shí)荚板,必須對(duì)整個(gè)菜單重新創(chuàng)建凤壁,從而達(dá)到動(dòng)態(tài)菜單的目的。這也是非常不友好的跪另。

另一方面拧抖,一個(gè)菜單項(xiàng)被點(diǎn)擊后,只接受三個(gè)參數(shù)免绿,也就是說 click 屬性的回調(diào)只接收: menuItem(被點(diǎn)擊的item)唧席、focusedWindow(點(diǎn)擊按鈕時(shí)focus的窗口) 以及毫無(wú)用途的 event (提供鍵盤是否被按下的信息),這也就造成的諸多的不便针姿。

例如袱吆,當(dāng)我們希望一個(gè)按鈕被點(diǎn)擊后厌衙,其他的菜單項(xiàng)的 enbalbed 屬性設(shè)置為 false距淫,將無(wú)從下手。

為了解決這一問題婶希,這時(shí)候我們可以巧妙的使用 Electron 的 ipc 機(jī)制在 renderer 端創(chuàng)建應(yīng)用菜單榕暇,在 renderer 端創(chuàng)建的菜單便可以在內(nèi)部使用 ipcRenderer.send() 這個(gè)方法,讓 ipcMain 來(lái)處理其他其他菜單項(xiàng)的操作喻杈。

不僅如此彤枢,甚至于可以讓整個(gè)菜單的 click 邏輯都交付于 main 端進(jìn)行管理,例如下面這樣的 menu template:

{
    type: 'separator',
    visible: (()=>{
        if (process.platfrom == 'darwin') return true;
        else return false;
    })()
},
{
    label: `Current Version: ${app.getVersion()}`,
    enabled: false
},
{
    type: 'separator'
},
{
    label: 'Logout',
    click: () => { ipcRenderer.send('application', 'logout'); }
},
{
    type: 'separator'
},
{
    label: 'Exit ${app.getAppName()}',
    accelerator: 'CmdOrCtrl+Q',
    click: () => { ipcRenderer.send('application', 'quit'); }
}

而在 main 端則可以:

ipcMain.on('application', (event, args) => {
    switch(args){
    case: 'logout':
        ....; break
    case: 'quit':
        ...; break
    default: ...
    }
})
  • 后臺(tái)網(wǎng)絡(luò)狀態(tài)監(jiān)測(cè)

相關(guān) issue: https://github.com/electron/electron/issues/6633

問題: Electron 的官方文檔其實(shí)就提供了網(wǎng)絡(luò)檢測(cè)的方法筒饰,思路是通過一個(gè)隱藏的后臺(tái)窗口缴啡,檢測(cè)網(wǎng)絡(luò)網(wǎng)絡(luò)狀態(tài),通過網(wǎng)絡(luò)狀態(tài)產(chǎn)生變化后瓷们,通過 ipcRenderer 發(fā)送消息給 ipcMain业栅,然后響應(yīng)所需的操作,見這里谬晕。

但事實(shí)上這個(gè)方法是基于 online 和 offline 事件的碘裕,換句話說這個(gè)方法只能檢測(cè)到當(dāng)系統(tǒng)網(wǎng)絡(luò)連接被切斷物理連接后的狀態(tài)變化,無(wú)法檢測(cè)網(wǎng)絡(luò)本身攒钳。

這個(gè)問題可以參考issue帮孔,但其實(shí)現(xiàn)思路就是向蘋果的 hotspot detect 頁(yè)面發(fā)起請(qǐng)求,當(dāng)未超時(shí)狀態(tài)下有 Success 返回時(shí)不撑,便說明網(wǎng)絡(luò)狀態(tài)正常文兢。在實(shí)際應(yīng)用編寫過程中,我們也并不需要為了這樣一個(gè)簡(jiǎn)單的功能而引入框架焕檬,只需定時(shí)向服務(wù)器發(fā)起任意一個(gè)能夠判斷網(wǎng)絡(luò)狀態(tài)的請(qǐng)求即可禽作,與心跳連接殊途同歸。

  • preload 執(zhí)行階段和使用場(chǎng)景

相關(guān) issue: https://github.com/electron/electron/issues/7455

在前面創(chuàng)建基本應(yīng)用中我們已經(jīng)談到了關(guān)于 preload 腳本用來(lái)引入 jQeury 的使用揩页,事實(shí)上我們可以用 preload 做更多的事情旷偿。preload 腳本會(huì)在整個(gè)頁(yè)面開始加載之前被執(zhí)行烹俗,所以如果我們直接執(zhí)行一些當(dāng)整個(gè) DOM 加載完成才能被執(zhí)行的操作,是必定會(huì)失效的萍程,因此這樣的兩個(gè)事件是非常有用的:DOMNodeInserted幢妄、DOMContentLoaded
為此茫负,我們可以把 preload 腳本大致分為三塊區(qū)域:

// ---------------------------------------------------
// 在頁(yè)面加載之前需要執(zhí)行的相關(guān)代碼
// ...

// ---------------------------------------------------


// -------------------------------------------------------
document.addEventListener('DOMNodeInserted', (event) => {
    // 頁(yè)面內(nèi)容加載之前需要引入的一些代碼
    // ...
})
// -------------------------------------------------------


// -------------------------------------------------------
document.addEventListener('DOMContentLoaded', (event) => {
    // 頁(yè)面內(nèi)容加載之后需要引入的一些操作
    // ...

})
// -------------------------------------------------------

preload 腳本的作用非常大蕉鸳,有時(shí)候會(huì)有這樣的需求:當(dāng)我們加載一個(gè)網(wǎng)絡(luò)上的頁(yè)面時(shí),我們不能控制從網(wǎng)絡(luò)中讀取到的頁(yè)面內(nèi)容忍法,但 preload 提供了這樣的可能性潮尝,使得我們能夠向頁(yè)面 注入 一些代碼,滿足一些神奇的需求饿序,比如對(duì)網(wǎng)絡(luò)加載頁(yè)面增加 Context Menu勉失。但也有使用時(shí)值得注意的地方:

Electron 的 main 進(jìn)程、preload 腳本原探、renderer 進(jìn)程乱凿、以及 document 對(duì)象分別有彼此的創(chuàng)建和執(zhí)行順序。首先 main 進(jìn)程會(huì)優(yōu)先被創(chuàng)建毫無(wú)疑問咽弦,preload 會(huì)在 document 對(duì)象被創(chuàng)建之前優(yōu)先加載(但能夠使用 document)徒蟆,而 renderer 進(jìn)程會(huì)在 document 創(chuàng)建之后被創(chuàng)建,而他們?nèi)哂质遣l(fā)創(chuàng)建的型型,如下圖所示段审。

image

那么,如果我們不小心在 preload 腳本中直接引入 ipcRenderer 發(fā)送一條消息給 ipcMain闹蒜,那么 ipcMain 可能不能收到這條早期消息寺枉。為了保證我們能夠收到這條消息,最好的方式就是:

// preload.js
// 不要再外面這么干
// ipcRenderer.send(...)

document.addEventListener('DOMContentLoaded', (event) => {
    // 頁(yè)面內(nèi)容加載之后需要引入的一些操作
    // ...
    // 正確的做法
    ipcRenderer.send(...)
})
  • 下載

下載也是一個(gè)常見的需求嫂用,比如型凳,你正在基于 Electron 實(shí)現(xiàn)一個(gè) Web 文本應(yīng)用,用戶可能需要下載保存在服務(wù)器上的一個(gè)編輯好的文件嘱函,這時(shí)候當(dāng)點(diǎn)擊 Web 界面中的下載時(shí)甘畅,Electron 并不需要專門針對(duì)這個(gè)下載行為進(jìn)行單獨(dú)的處理,Electron 會(huì)想瀏覽器那樣直接跳出一個(gè)保存的文件選擇器往弓,讓用戶獲得下一步的操作疏唾。當(dāng)我們真正需要處理一些特殊的下載操作時(shí),同樣可以用 electron 的 DownloadItem 來(lái)實(shí)現(xiàn)函似,但其接口設(shè)計(jì)著實(shí)有點(diǎn)讓筆者難以接受槐脏,這里推薦可以嘗試 electron-userland/electron-download 這個(gè)庫(kù),雖然其本質(zhì)也是 DownloadItem撇寞,但其接口相比之下友善許多顿天,因?yàn)閹?kù)本身也并不復(fù)雜堂氯,也可以在項(xiàng)目中自行實(shí)現(xiàn)這部分邏輯。

  • 軟件更新

軟件的日后更新一直都是產(chǎn)品日后迭代的殺手牌废,一個(gè)需要被分發(fā)的桌面應(yīng)用咽白,在沒有確定的更新機(jī)制之前,切忌發(fā)布鸟缕。

Electron 雖然本身自帶 audoUpdater 模塊晶框,但作為框架的使用者來(lái)說,筆者很難說它做得優(yōu)秀懂从,因?yàn)樾枰渲玫膬?nèi)容相較于其他功能來(lái)說略加繁瑣授段。因此這里推薦使用 electron-updater。下面的代碼相當(dāng)于一個(gè)純粹的更新功能的封裝番甩,使用成本非常簡(jiǎn)單侵贵,只需根據(jù) electron-builder wiki 的說明配置好 publisher 即可實(shí)現(xiàn)更新功能:

// updater.js
const { dialog } = require('electron')
const { autoUpdater } = require('electron-updater')


let updater
// 禁用自動(dòng)下載,給用戶選擇余地
autoUpdater.autoDownload = false
autoUpdater.on('error', (event, error) => {
  dialog.showErrorBox('Error: ', error)
})
autoUpdater.on('update-available', () => {
  dialog.showMessageBox({
    type: 'info',
    title: 'Found Updates',
    message: 'Found updates, do you want update now?',
    buttons: ['Sure', 'No']
  }, (buttonIndex) => {
    if (buttonIndex === 0) {
      autoUpdater.downloadUpdate()
    } else {
      updater.enabled = true
      updater = null
    }
  })
})
autoUpdater.on('update-not-available', () => {
  dialog.showMessageBox({
    title: 'No Updates', 
    message: 'Current version is up-to-date.'
  })
  updater.enabled = true
  updater = null
})

// 下載完成時(shí)对室,提醒用戶
autoUpdater.on('update-downloaded', () => {
  dialog.showMessageBox({
    title: 'Install Updates',
    message: 'Updates downloaded, application will be quit for update...'
  }, () => {
    autoUpdater.quitAndInstall()
  })
})


// 將這個(gè)回調(diào)輸出給更新功能所在的菜單項(xiàng)的 click 回調(diào)
function checkForUpdates (menuItem, focusedWindow, event) {
  updater = menuItem
  updater.enabled = false
  autoUpdater.checkForUpdates()
}
module.exports.checkForUpdates = checkForUpdates
  • 發(fā)布

打包工具

期初的 Electron 打包是一個(gè)比較惱人的問題模燥,因?yàn)榭捎玫墓ぞ咂鋵?shí)不多咖祭,electron-packager 是一個(gè)很原始的打包工具掩宜,雖然具備打包的功能,但是其提供以開發(fā) API 為藍(lán)本的入口使得構(gòu)建還需要額外編寫腳本進(jìn)行么翰,而它其實(shí)只具備將應(yīng)用進(jìn)行打包編譯的功能牺汤,這與最終發(fā)布的 Installer 還有一步之遙,所以使用 electron-packager 是非常消耗開發(fā)成本的一件事情浩嫌。好在有一個(gè)取而代之的工具 electron-builder檐迟。它的好處在本文前面的部分也已經(jīng)多次提及,它不僅擁有方便的配置 protocol 的功能码耐、內(nèi)置的 Auto Update追迟、簡(jiǎn)單的配置 package.json 便能完成整個(gè)打包工作,用戶體驗(yàn)是相當(dāng)優(yōu)秀的骚腥。

代碼簽名

代碼簽名對(duì)于發(fā)布作為正式商業(yè)產(chǎn)品應(yīng)用來(lái)說是非常重要的敦间。如今的 electron-builder 對(duì)于代碼簽名已經(jīng)做得相當(dāng)友好,對(duì)于 macOS 來(lái)說束铭,它能夠自動(dòng)獲取系統(tǒng)中 Keychain 中的開發(fā)者證書廓块,自動(dòng)對(duì)代碼進(jìn)行簽名,而 Windows 也可以通過配置一個(gè) .p12 證書來(lái)達(dá)到簽名的目的契沫。

因此带猴,到目前為止,代碼的簽名成本已經(jīng)非常低懈万,只需購(gòu)買好證書拴清,基本上沒有什么煩心的事情靶病,唯一一個(gè)值得注意的事情是:如果要分發(fā) macOS 上的應(yīng)用,那么構(gòu)建平臺(tái)將只有 macOS 是被推薦的口予,因?yàn)樗俏ㄒ灰粋€(gè)能夠同時(shí)構(gòu)建 macOS/Linux/Windows 三平臺(tái)應(yīng)用的平臺(tái)嫡秕。但是,如果使用 CSC_LINK 將會(huì)出現(xiàn)沖突苹威,因?yàn)?CSC_LINK 已被用于 macOS 平臺(tái)的簽名昆咽,因此額外在 package.json 中配置 Windows 平臺(tái)的證書。

由于證書最終不會(huì)被分發(fā)牙甫,可以在簽名時(shí)使用一個(gè)移除密碼的證書掷酗;亦或者對(duì)兩個(gè)平臺(tái)的證書使用相同的密碼,方便最終的簽名和打包窟哺。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末泻轰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子且轨,更是在濱河造成了極大的恐慌浮声,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旋奢,死亡現(xiàn)場(chǎng)離奇詭異泳挥,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)至朗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門屉符,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人锹引,你說我怎么就攤上這事矗钟。” “怎么了嫌变?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵吨艇,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我腾啥,道長(zhǎng)东涡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任碑宴,我火速辦了婚禮软啼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘延柠。我一直安慰自己祸挪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布贞间。 她就那樣靜靜地躺著贿条,像睡著了一般雹仿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上整以,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天胧辽,我揣著相機(jī)與錄音,去河邊找鬼公黑。 笑死邑商,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的凡蚜。 我是一名探鬼主播人断,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼朝蜘!你這毒婦竟也來(lái)了恶迈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤谱醇,失蹤者是張志新(化名)和其女友劉穎暇仲,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體副渴,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡奈附,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了佳晶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桅狠。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡讼载,死狀恐怖轿秧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情咨堤,我是刑警寧澤菇篡,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站一喘,受9級(jí)特大地震影響驱还,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜凸克,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一议蟆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧萎战,春花似錦咐容、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)路狮。三九已至,卻和暖如春蔚约,著一層夾襖步出監(jiān)牢的瞬間奄妨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工苹祟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留砸抛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓树枫,卻偏偏與公主長(zhǎng)得像锰悼,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子团赏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359