新聞中心
你是否曾經(jīng)想過可以用 HTML、CSS 和 JavaScript 這些前端技術(shù)來構(gòu)建跨平臺(tái)的桌面應(yīng)用?

使用 Electron 就能做到。
本文帶著你深入 Electron 的核心概念。
閱讀本文后,你會(huì)知道如何使用 Electron、HTML 和 CSS 構(gòu)建跨平臺(tái)桌面應(yīng)用。
在開始之前,你可以提前看看我們在本教程中要構(gòu)建的應(yīng)用。
Hear Me Type [譯者注:本文的示例應(yīng)用名為 Hear Me Type] 功能簡單直接,應(yīng)用中每個(gè)鍵都會(huì)播放特有的聲音。比如我按下了 “A”,應(yīng)用會(huì)播放字母 A 特有的聲音。
該應(yīng)用目前有兩個(gè)版本可供下載:本教程的源碼,以及一個(gè)推薦給更有經(jīng)驗(yàn)的 Electron 用戶的高級版本。
因?yàn)槲疫€在為改善應(yīng)用以及添加一些新功能,所以代碼會(huì)發(fā)生變化,請一定注意看看有哪些更新。
就此打住,我們現(xiàn)在開始學(xué)習(xí) Electron 并用它來創(chuàng)建***個(gè)應(yīng)用!
什么是 Electron?
Electron 是一個(gè)基于 Chrominum 和 Node.js 的跨平臺(tái)桌面應(yīng)用框架。
在這個(gè)框架中很容易構(gòu)建基于 HTML、CSS 和 JavaScript 技術(shù)的跨平臺(tái)應(yīng)用。構(gòu)建出來的應(yīng)用會(huì)很好地兼容 Mac、Windows 和 Linux 操作系統(tǒng)。
它還有其它一些特性:
- 自動(dòng)更新 —— 應(yīng)用支持自動(dòng)更新
- 原生菜單和通知 —— 可以創(chuàng)建原生應(yīng)用菜單和上下文菜單
- 應(yīng)用崩潰報(bào)告?—— 可以將崩潰報(bào)告提交到遠(yuǎn)程服務(wù)器
- 調(diào)試和分析 —— Chrominum 的內(nèi)容模塊可以發(fā)生性能瓶頸和緩慢的操作。你也可以在應(yīng)用中使用自己喜歡的 Chrome 開發(fā)者工具。
- Windows installer?—— 可以快速便捷地創(chuàng)建安裝包。
如果你對 Electron 的功能感到滿意,我們就繼續(xù)深入,創(chuàng)建一個(gè)簡單的 Electron 應(yīng)用。
動(dòng)手之前需要先安裝 Node.js。你還應(yīng)該申請一個(gè) GitHub 賬戶來保存和更新應(yīng)用。雖然這個(gè)賬戶并不是必須的,但我非常建議你去申請一個(gè)。Github 是一個(gè)行業(yè)標(biāo)準(zhǔn),學(xué)會(huì)使用 Github 非常重要。
我會(huì)在教程中使用 Github。
開始
準(zhǔn)備好之后,打開系統(tǒng)終端窗口。
按照下面的介紹將 Electron Quick Start 這個(gè) Git 庫克隆到計(jì)算機(jī)上。
我們會(huì)基于 Electron Quick Start 來創(chuàng)建自己的軟件。
- # Clone the Quick Start repository
- git clone
- # Go into the repository
- cd electron-quick-start
- # Install the dependencies and run
- npm install && npm start
完成上面的步驟之后,你會(huì)看到一個(gè)像瀏覽器窗口的應(yīng)用打開。它確實(shí)是一個(gè)瀏覽器窗口!
這個(gè)窗口顯示的樣子在不同的操作系統(tǒng)上會(huì)有所不同。我選擇使用 Windows 的經(jīng)典樣式。非常贊!
Quick-Start Electron 應(yīng)用的主窗口
正如我之前所說,你可以在應(yīng)用中使用 Chrome 開發(fā)者工具,這個(gè)工具的用法跟在瀏覽器中的開發(fā)者工具一樣,再贊一個(gè)!
Electron 應(yīng)用程序架構(gòu)
我們來看看這個(gè)應(yīng)用的源代碼及其文件結(jié)構(gòu)??梢允褂媚阕约合矚g的編輯器或者 IDE 打開項(xiàng)目,我使用 Atom …… 你猜到了 …… 它就是用 Electron 構(gòu)建的![譯者注:我比較喜歡 VSCode,也是基于 Electron 構(gòu)建的]
新應(yīng)用的目錄和文件結(jié)構(gòu)。
我們有一個(gè)基本的文件結(jié)構(gòu):
electron-quick-start
- - index.html
- - main.js
- - package.json
- - render.js
文件結(jié)構(gòu)類似于我們創(chuàng)建網(wǎng)頁的結(jié)構(gòu)。
我們有:
- index.html 是一個(gè) HTML5 頁面,它具有很重要的作用:提供畫布
- main.js 創(chuàng)建窗口并處理系統(tǒng)事件
- package.json 是應(yīng)用的啟動(dòng)腳本。它包含了應(yīng)用的信息,在主進(jìn)行中運(yùn)行
- render.js 處理應(yīng)用的渲染進(jìn)程
也許你對主進(jìn)程和渲染進(jìn)程存有疑問。它們到底是什么,用來干什么?
很高興你有此疑問。注意了,如果你來自瀏覽器的 JavaScript 領(lǐng)域,這對你來說可能是一塊新的領(lǐng)域!
什么是進(jìn)程?
看到“進(jìn)程”這個(gè)詞的時(shí)候,想像一下操作系統(tǒng)級的進(jìn)程。那是運(yùn)行在系統(tǒng)中的計(jì)算機(jī)程序?qū)嵗?/p>
啟動(dòng) Electron 應(yīng)用之后,查看 Windows 的任務(wù)管理器或者 macOS 的活動(dòng)監(jiān)視器,就可以看到與這個(gè)應(yīng)用相關(guān)的進(jìn)程。
這些進(jìn)程都是并行運(yùn)行的,為每個(gè)進(jìn)程分配的內(nèi)存和資源相互隔離。
如果我想創(chuàng)建一個(gè) for 循環(huán)在渲染進(jìn)程中逐步處理一些事件。
- var a = 1;
- for ( a = 1; a < 10; a ++) {
- console.log('This is a for loop');
- }
這些改變只在渲染進(jìn)程中有效,根本不會(huì)對主進(jìn)行造成影響?!癟his is a for loop”消息只會(huì)出現(xiàn)在渲染模塊中。
主進(jìn)程
主進(jìn)程控制著應(yīng)用的生命周期。它內(nèi)置了完整的 Node.js API,可以打開對話框,創(chuàng)建渲染進(jìn)程,還可以處理其它其它與操作系統(tǒng)的交互操作,包括啟動(dòng)和退出應(yīng)用。
按照慣例,這個(gè)進(jìn)程寫在名為 main.js 的文件中。不過你想使用其它名字也沒有問題。
你可以在 package.json 文件中配置主進(jìn)程文件的名稱。
試驗(yàn)一下,打開 package.json 并將
- “main”: “main.js”,
修改為
- “main”: “mainTest.js”,
啟動(dòng)應(yīng)用看看它是否仍然正常運(yùn)行。
注意,主進(jìn)程只有一個(gè)。
渲染進(jìn)程
應(yīng)用中的渲染進(jìn)程是一個(gè)瀏覽器窗口。與主進(jìn)程不同,可以存在多個(gè)獨(dú)立的渲染進(jìn)程。
因?yàn)殇秩具M(jìn)程是各自獨(dú)立的,如果其中一個(gè)崩潰了并不會(huì)影響到其它進(jìn)程,這得益于 Chrominum 的多進(jìn)程架構(gòu)。
這些瀏覽器窗口就像演示網(wǎng)頁一樣,也可以被隱藏或自定義。
不過 Electron 內(nèi)置了完整的 Node.js API,也就是說我們可以打開對話框或進(jìn)行其它與操作系統(tǒng)的交互。
這樣考慮;
[來源: Kristian Poslek]
還有一個(gè)問題,它們能以某種方式聯(lián)系起來嗎?
這些進(jìn)程都在獨(dú)立運(yùn)行,但他們?nèi)匀恍枰ㄐ牛驗(yàn)樗鼈冐?fù)責(zé)不同的任務(wù),這尤其需要通信。
為此,存在一個(gè)進(jìn)程間的通信系統(tǒng)或者 IPC。你可以使用 IPC 在主進(jìn)程和渲染進(jìn)程間進(jìn)行通信。對于這個(gè)知識(shí)點(diǎn)更深入一些的解釋,請閱讀 Christian Engvall 的文章。
上面說的都是開發(fā) Electron 應(yīng)用的基礎(chǔ)知識(shí)。
現(xiàn)在回到我們的代碼!
私有化
讓我們給應(yīng)用所在的目錄起一個(gè)合適的名稱。
將目錄名從 electron-quick-start 改為 hear-me-type-tutorial。
重新在編輯器或 IDE 中打開這個(gè)目錄,我們打開 package.json 來進(jìn)一步定制應(yīng)用標(biāo)識(shí)。
package.json 包含了至關(guān)重要的應(yīng)用信息。這里定義應(yīng)用的名稱、版本、主文件、作者、許可協(xié)議等。
現(xiàn)在把作者改成自己的名稱,自豪感油然而生。
找到 “author” 參數(shù),然后將值改成自己的名稱。它看起來像這樣:
- “author”: “Carol Pelu”,
我們還要改其它一些參數(shù)。在 package.json 中找到 name(名稱) 和 description(說明)并修改它們:
帥呆了!現(xiàn)在應(yīng)用有了新名稱,還有簡短而清晰的說明。
記住,你可以在終端運(yùn)行 npm start 來運(yùn)行應(yīng)用,以觀察所做的改變。
我們會(huì)繼續(xù)在應(yīng)用中添加一些期望的功能。我們想在按下每個(gè)鍵的時(shí)候播放不同的聲音。
哦,有趣的功能!
沒有功能的應(yīng)用是什么?什么都不是……
現(xiàn)在我們要給它添加功能。
為了讓應(yīng)用響應(yīng)輸入,我們必須定義一個(gè)元素來捕捉事件,然后觸發(fā)期望的動(dòng)作。
為此,我們會(huì)創(chuàng)建一個(gè)具有特殊名稱的若干 audio 元素,對應(yīng)于按鍵。然后我們會(huì)使用一個(gè) switch 語句來定位按下的鍵,播放與之關(guān)聯(lián)的聲音。
如果你現(xiàn)在覺得有點(diǎn)復(fù)雜,不要怕,我會(huì)指引你一步步進(jìn)行。
下載這個(gè)壓縮包,它包含了我們要使用的所有聲音文件。很快就會(huì)用到!
我們會(huì)打開 index.html 文件,創(chuàng)建一個(gè)
在
元素內(nèi)部,創(chuàng)建一個(gè) div 元素,將其 class 屬性設(shè)置為 audio。在剛剛創(chuàng)建的 div 元素,創(chuàng)建
preload=”auto” 用于告訴應(yīng)用在頁面加載的時(shí)候就加載完整的聲音文件。index.html 是應(yīng)用的主文件,所有聲音都會(huì)在應(yīng)用啟動(dòng)的時(shí)候加載。
下面是代碼:
你的 index.html 應(yīng)該就像這樣。
現(xiàn)在 指向一個(gè)未知的文件。我們要?jiǎng)?chuàng)建一個(gè)名為 soudes 的目錄,并將所有聲音文件解壓到這個(gè)目錄中。
非常好!現(xiàn)在唯一缺少的是 JavaScriopt 代碼。
創(chuàng)建一個(gè)叫 functions.js 的新文件,并在 index.html 中通過 require 引用它,這樣應(yīng)用運(yùn)行的時(shí)候才會(huì)執(zhí)行 JS 代碼。
在示例的 require(./renderer.js') 下載添加這樣一行:
- require('./functions.js')
之后項(xiàng)目看起來是這樣的:
不錯(cuò)!一切就緒,下面是見證奇跡的時(shí)刻。
打開 functions.js 文件并將下面的代碼添加到文件中。我稍后解釋這段代碼。
- document.onkeydown = function(e) {
- switch (e.keyCode) {
- case 65:
- document.getElementById('A').play();
- break;
- default:
- console.log("Key is not found!");
- }
- };
代碼現(xiàn)在是這樣:
打開 Bash 或者終端窗口,確保當(dāng)前是在項(xiàng)目目錄下,運(yùn)行 npm start 來啟動(dòng)應(yīng)用。
調(diào)整揚(yáng)聲器的音量并敲下按鍵。
- #MindBlown
JS 代碼非常簡單明了。
我們使用了 document 對象上的 onkeydown 事件,在這里找到被訪問的元素。記住,document 對象是應(yīng)用的主窗口。
我們在在匿名函數(shù)中使用了 switch 語句,根據(jù) Unicode 值來判斷按鍵。
如果找到按鍵對應(yīng)的 Unicode 值,就會(huì)播放相應(yīng)的聲音,否則拋出 “not found” 錯(cuò)誤。這個(gè)錯(cuò)誤要在控制臺(tái)中去找。
多么愉快的過程!
你可能注意到了,我們的聲音文件包含了 A-Z 和 0-9 這些鍵,把它們都用起來。
在 index.html 中為每個(gè)有對應(yīng)聲音文件的鍵都創(chuàng)建一個(gè) 元素。
之后代碼就像這樣:
當(dāng)然你可以用拷貝粘貼:
現(xiàn)在在 functions.js 中加點(diǎn)代碼。
你可以在這個(gè)網(wǎng)站上查到字符碼(charCode 或 keyCode)。
當(dāng)然,也可以使用拷貝粘貼:
- document.onkeydown = function(e) {
- switch (e.keyCode) {
- case 48:
- document.getElementById('0').play();
- break;
- case 49:
- document.getElementById('1').play();
- break;
- case 50:
- document.getElementById('2').play();
- break;
- case 51:
- document.getElementById('3').play();
- break;
- case 52:
- document.getElementById('4').play();
- break;
- case 53:
- document.getElementById('5').play();
- break;
- case 54:
- document.getElementById('6').play();
- break;
- case 55:
- document.getElementById('7').play();
- break;
- case 56:
- document.getElementById('8').play();
- break;
- case 57:
- document.getElementById('9').play();
- break;
- case 65:
- document.getElementById('A').play();
- break;
- case 66:
- document.getElementById('B').play();
- break;
- case 67:
- document.getElementById('C').play();
- break;
- case 68:
- document.getElementById('D').play();
- break;
- case 69:
- document.getElementById('E').play();
- break;
- case 70:
- document.getElementById('F').play();
- break;
- case 71:
- document.getElementById('G').play();
- break;
- case 72:
- document.getElementById('H').play();
- break;
- case 73:
- document.getElementById('I').play();
- break;
- case 74:
- document.getElementById('J').play();
- break;
- case 75:
- document.getElementById('K').play();
- break;
- case 76:
- document.getElementById('L').play();
- break;
- case 77:
- document.getElementById('M').play();
- break;
- case 78:
- document.getElementById('N').play();
- break;
- case 79:
- document.getElementById('O').play();
- break;
- case 80:
- document.getElementById('P').play();
- break;
- case 81:
- document.getElementById('Q').play();
- break;
- case 82:
- document.getElementById('R').play();
- break;
- case 83:
- document.getElementById('S').play();
- break;
- case 84:
- document.getElementById('T').play();
- break;
- case 85:
- document.getElementById('U').play();
- break;
- case 86:
- document.getElementById('V').play();
- break;
- case 87:
- document.getElementById('W').play();
- break;
- case 88:
- document.getElementById('X').play();
- break;
- case 89:
- document.getElementById('Y').play();
- break;
- case 90:
- document.getElementById('Z').play();
- break;
- default:
- console.log("Key is not found!");
- }
- };
大功告成!
應(yīng)用的主要功能已經(jīng)完成了,但仍然還有些工作要做!
完善!
應(yīng)用程序的功能已經(jīng)完成,但它尚不完善。
比如,可以在 index.html 中修應(yīng)用的標(biāo)題和主窗口的內(nèi)容。
此外,這個(gè)應(yīng)用沒有設(shè)計(jì)炫麗的色彩,也沒有使用漂亮的圖片。
充分發(fā)揮你的想像,找出改進(jìn)應(yīng)用設(shè)計(jì)的方法。
代碼也不***,我們有很多相同的代碼需要優(yōu)化以減少代碼行數(shù),至少看起來不那么難受。
重復(fù)代碼真不是好做法!
測試!就是測試!
好的軟件需要通過測試。
我建議你先按鍵看看,會(huì)發(fā)生什么事。
***的情況是你會(huì)聽到每個(gè)鍵對應(yīng)的聲音。但如果你快速的按下多個(gè)鍵的時(shí)候會(huì)發(fā)生什么呢?如果按下了非預(yù)期的鍵,比如 Home 和 NumLock,又會(huì)發(fā)生什么呢?
如果你最小化程序再嘗試著按鈕會(huì)怎樣?能聽到聲音嗎?如果沒有選擇應(yīng)用程序窗口,按下鍵盤時(shí),還會(huì)聽到聲音嗎?
答案是否定的。
Electron 的架構(gòu)決定了其行為。它允許你可以像 C# 語言那樣使用所有按鍵,但你不能注冊個(gè)性化的按鍵。這已經(jīng)超出了 Electron 應(yīng)用的使用范圍。
一行行的執(zhí)行代碼,并深度中斷它,看看會(huì)發(fā)生什么,Electron 會(huì)拋出什么樣的錯(cuò)誤。這一練習(xí)能幫助你更好地進(jìn)行調(diào)試。如果你知道應(yīng)用的缺陷,那么你就知道該如何去修復(fù),讓應(yīng)用變得更好。
我故意在 functions.js 文件中使用了一個(gè)廢棄的 JavaScript 事件,你能發(fā)現(xiàn)嗎?
如果你找到了,我希望你能在不改變應(yīng)用程序功能的情況下替換它。
使用廢棄的代碼很不好,這可能會(huì)導(dǎo)致嚴(yán)重錯(cuò)誤,你甚至可能意識(shí)不到這些錯(cuò)誤的存在。關(guān)注語言的***文件,了解可能發(fā)生的變化,始終了解***狀態(tài)。
分享標(biāo)題:如何利用Electron開發(fā)一個(gè)桌面APP
分享地址:http://www.dlmjj.cn/article/dpgodjh.html


咨詢
建站咨詢
