Electron介紹
簡單來說个束,Electron就是可以讓你用Javascript慕购、HTML、CSS來編寫運行于Windows茬底、macOS沪悲、Linux系統(tǒng)之上的桌面應(yīng)用的庫。本文的目的是通過使用Electron開發(fā)一個完整但簡單的小應(yīng)用:記事本阱表,來體驗一下這個神器的開發(fā)過程殿如。本文猶如Hello World
一樣的存在,是個入門級筆記最爬,但如果你之前從未接觸過Electron涉馁,而又對它有興趣,某想信這會是一篇值得一看的入門教程爱致。
PS:這篇文章是基于Windows的開發(fā)過程烤送,未對macOS、Linux作測試糠悯。
開發(fā)環(huán)境安裝
安裝Node.js
點擊 這里 進(jìn)入官網(wǎng)下載胯努、安裝。
安裝cnpm
由于眾所周知的原因逢防,你需要一個cnpm
代替npm
叶沛,這里 是官網(wǎng)。安裝命令(打開系統(tǒng)的cmd.exe來執(zhí)行命令):
npm install -g cnpm --registry=https://registry.npm.taobao.org
安裝Electron
cnpm install -g electron
安裝Electron-forge
這是一個類似于傻瓜開發(fā)包的Electron工具整合項目忘朝。具體介紹點擊 這里灰署。
cnpm install -g electron-forge
新建項目
- 假設(shè)項目要放到
H:\Electron
目錄下,項目名為notepad
(字母全部小寫,多個單詞之間可以用“-”連接)溉箕。 - 打開
cmd.exe
晦墙,一路cd到H:\Electron
。(也可以在Electron
文件夾下肴茄,按住Shift
鍵并右鍵單擊空白處晌畅,選擇在此處打開命令窗口
來啟動cmd.exe
。) - 執(zhí)行下面的命令來生成名為
notepad
的項目文件夾寡痰,同時安裝項目所需要的模塊抗楔、依賴項等。
electron-forge init notepad
- cd到
notepad
目錄下拦坠,執(zhí)行下面的命令來啟動app(也可以簡單的用npm start
來運行)连躏。
electron-forge start
-
這樣就可以看到基本的app界面了。
模板文件
- 這里某使用Visual Studio Code來開發(fā)app贞滨。
- 將
notepad
文件夾整個拖到VS Code中打開(或者點菜單文件-打開文件夾
選擇notepad
文件夾打開項目)入热,可以看一下項目的目錄結(jié)構(gòu):node_modules
文件夾下是各種模塊、類庫晓铆,src
下是app的源代碼文件勺良,package.json
是描述包的文件。
- 看一下
package.json
骄噪,注意這里默認(rèn)已經(jīng)將主進(jìn)程入口文件配置為index.js
(而不是main.js
)尚困。
為避免后面混亂,某還是將這里的src/index.js
改成src/main.js
腰池,同時也要將文件index.js
改名為main.js
尾组。
- 看一下
main.js
,這是app主進(jìn)程的入口示弓,在這里創(chuàng)建了mainWindow
瀏覽器窗口讳侨,使用mainWindow.loadURL("file://${__dirname}/index.html")
來加載index.html
主頁;使用mainWindow.webContents.openDevTools()
來打開開發(fā)者工具用于調(diào)試(這個操作通常在發(fā)布app時刪除)奏属。然后是app的事件處理:
-
ready
: 當(dāng)Electron完成初始化后觸發(fā)跨跨,這里初始化后就會去創(chuàng)建瀏覽器窗口并加載主頁面。 -
window-all-closed
: 當(dāng)所有瀏覽器窗口被關(guān)閉后觸發(fā)囱皿,一般此時就退出應(yīng)用了勇婴。 -
activate
: 當(dāng)app激活時觸發(fā),一般針對macOS要需要處理嘱腥。
- 看一眼
index.html
耕渴,這是主頁面,除了顯示Well hey there!!!
的信息外齿兔,沒什么具體內(nèi)容橱脸。 - 于是础米,現(xiàn)在整個app只有二個源碼文件:
main.js
和index.html
。main.js
是主進(jìn)程入口添诉,index.html
是一個web頁面屁桑,它需要使用一個瀏覽器窗口(BrowserWindow
)來加載和顯示,作為應(yīng)用的UI栏赴,它處在一個獨立的渲染進(jìn)程中蘑斧。app啟動時執(zhí)行main.js
中的代碼創(chuàng)建窗口,加載頁面等须眷。主進(jìn)程與渲染進(jìn)程之間不能直接互相訪問竖瘾,需要通過ipcMain
和ipcRenderer
進(jìn)行IPC通信(Inter-process communication),或者使用remote
模塊在渲染進(jìn)程中使用主進(jìn)程中的資源(反過來柒爸,在主進(jìn)程中使用webContents.executeJavascript
方法可以訪問渲染進(jìn)程)准浴。
Notepad App功能設(shè)計
這里將實現(xiàn)一個類似于Windows的記事本的App事扭。這個App具備以下功能:
-
主菜單:包括
File
,Edit
,View
,Help
四個主菜單捎稚。重點是File
菜單下的三個子菜單:New
(新建文件)、Open
(打開文件)求橄、Save
(保存文件)今野,這三個菜單需要自定義點擊事件,其它的菜單基本使用內(nèi)建的方法處理罐农,所以沒什么難度条霜。 -
文本框:用于文本編輯。這也是這個App上的唯一一個組件涵亏,它的寬和高自動平鋪滿整個窗口大小宰睡。當(dāng)修改了文本框中的文字后,會在App標(biāo)題欄上最右側(cè)添加一個
*
號以表示文檔尚未保存气筋。 -
加載和保存文本:可以打開本地文本文件拆内,支持
.txt
,.js
,.html
,.md
等文本文件;可以將文本內(nèi)容保存為本地文本文件宠默。在打開或新建文件前麸恍,如果當(dāng)前文檔尚未保存,會提示用戶先保存文檔搀矫。 - 退出程序:退出窗口或程序時抹沪,會檢測當(dāng)前文檔是否需要保存,如果尚未保存瓤球,提示用戶保存融欧。
-
右鍵菜單:支持右鍵菜單,可以通過菜單右鍵執(zhí)行一些基本的操作卦羡,如:復(fù)制噪馏、粘貼等权她。
下面是這個記事本App的演示效果,源碼下載點擊 這里逝薪。
Notepad App功能細(xì)節(jié)
由于主進(jìn)程與渲染進(jìn)程不能直接互相訪問隅要,所以部分細(xì)節(jié)有必要先考慮清楚。
-
主菜單:因為菜單只存在于主進(jìn)程中董济,所以在執(zhí)行某些涉及頁面(渲染進(jìn)程)的菜單命令時步清,比如
Open
(打開文件)命令,就需要與渲染進(jìn)程進(jìn)行通信虏肾,這可以使用ipcMain
和ipcRenderer
來實現(xiàn)廓啊。 -
右鍵菜單、對話框:所謂右鍵菜單其實和主菜單并無分別封豪,只是顯示方式不同谴轮。由于菜單、對話框等都只存在于主進(jìn)程中吹埠,要在渲染進(jìn)程中使用它們第步,就需要向主進(jìn)程發(fā)送進(jìn)程間消息,為簡化操作缘琅,Electron提供了一個
remote
模塊粘都,可以在渲染進(jìn)程中調(diào)用主進(jìn)程的對象和方法,而無需顯式地發(fā)送進(jìn)程間消息刷袍,所以這一部分可以由它來實現(xiàn)翩隧。PS:對于從主進(jìn)程訪問渲染進(jìn)程(反向操作),可以使用webContents.executeJavascript
方法呻纹。 -
退出時保存檢測:用戶點擊窗口的
關(guān)閉
按鈕堆生,或者點擊Exit
菜單就會關(guān)閉窗口退出程序。在退出時雷酪,有必要檢查文檔是否需要保存淑仆,如果尚未保存就提示用戶保存。要實現(xiàn)這一效果太闺,首先糯景,在主進(jìn)程監(jiān)測到用戶關(guān)閉窗口時,向渲染進(jìn)程發(fā)送一個特定的消息表明窗口準(zhǔn)備關(guān)閉省骂,渲染進(jìn)程獲得該消息后查看文檔是否需要保存蟀淮,如果需要就彈窗提示用戶保存,用戶保存或取消保存后钞澳,渲染進(jìn)程再向主進(jìn)程發(fā)送一個消息表明可以關(guān)閉程序了怠惶,主進(jìn)程獲得該消息后關(guān)閉窗口退出程序。這個過程也由ipcMain
和ipcRenderer
來實現(xiàn)轧粟。
Notepad App的實現(xiàn)
整個App功能比較簡單策治,最終實現(xiàn)后也只用到了三個主要文件脓魏,包括:main.js
,index.html
通惫,index.js
茂翔。
main.js
這是主進(jìn)程的入口,在這里創(chuàng)建App窗口履腋,生成菜單珊燎,載入頁面等。下面是該文件的完整源碼遵湖,二個//-------
之間是某根據(jù)功能需要添加的代碼悔政,其余是模板自動生成的代碼。
import { app, BrowserWindow } from 'electron';
//-----------------------------------------------------------------
import { Menu, MenuItem, dialog, ipcMain } from 'electron';
import { appMenuTemplate } from './appmenu.js';
//是否可以安全退出
let safeExit = false;
//-----------------------------------------------------------------
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;
const createWindow = () => {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
});
// and load the index.html of the app.
mainWindow.loadURL(`file://${__dirname}/index.html`);
// Open the DevTools.
//mainWindow.webContents.openDevTools();
//-----------------------------------------------------------------
//增加主菜單(在開發(fā)測試時會有一個默認(rèn)菜單延旧,但打包后這個菜單是沒有的谋国,需要自己增加)
const menu=Menu.buildFromTemplate(appMenuTemplate); //從模板創(chuàng)建主菜單
//在File菜單下添加名為New的子菜單
menu.items[0].submenu.append(new MenuItem({ //menu.items獲取是的主菜單一級菜單的菜單數(shù)組,menu.items[0]在這里就是第1個File菜單對象迁沫,在其子菜單submenu中添加新的子菜單
label: "New",
click(){
mainWindow.webContents.send('action', 'new'); //點擊后向主頁渲染進(jìn)程發(fā)送“新建文件”的命令
},
accelerator: 'CmdOrCtrl+N' //快捷鍵:Ctrl+N
}));
//在New菜單后面添加名為Open的同級菜單
menu.items[0].submenu.append(new MenuItem({
label: "Open",
click(){
mainWindow.webContents.send('action', 'open'); //點擊后向主頁渲染進(jìn)程發(fā)送“打開文件”的命令
},
accelerator: 'CmdOrCtrl+O' //快捷鍵:Ctrl+O
}));
//再添加一個名為Save的同級菜單
menu.items[0].submenu.append(new MenuItem({
label: "Save",
click(){
mainWindow.webContents.send('action', 'save'); //點擊后向主頁渲染進(jìn)程發(fā)送“保存文件”的命令
},
accelerator: 'CmdOrCtrl+S' //快捷鍵:Ctrl+S
}));
//添加一個分隔符
menu.items[0].submenu.append(new MenuItem({
type: 'separator'
}));
//再添加一個名為Exit的同級菜單
menu.items[0].submenu.append(new MenuItem({
role: 'quit'
}));
Menu.setApplicationMenu(menu); //注意:這個代碼要放到菜單添加完成之后芦瘾,否則會造成新增菜單的快捷鍵無效
mainWindow.on('close', (e) => {
if(!safeExit){
e.preventDefault();
mainWindow.webContents.send('action', 'exiting');
}
});
//-----------------------------------------------------------------
// Emitted when the window is closed.
mainWindow.on('closed', () => {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null;
});
};
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
createWindow();
}
});
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and import them here.
//-----------------------------------------------------------------
//監(jiān)聽與渲染進(jìn)程的通信
ipcMain.on('reqaction', (event, arg) => {
switch(arg){
case 'exit':
//做點其它操作:比如記錄窗口大小、位置等弯洗,下次啟動時自動使用這些設(shè)置旅急;不過因為這里(主進(jìn)程)無法訪問localStorage逢勾,這些數(shù)據(jù)需要使用其它的方式來保存和加載牡整,這里就不作演示了。這里推薦一個相關(guān)的工具類庫溺拱,可以使用它在主進(jìn)程中保存加載配置數(shù)據(jù):https://github.com/sindresorhus/electron-store
//...
safeExit=true;
app.quit();//退出程序
break;
}
});
//-----------------------------------------------------------------
首先逃贝,app.on('ready', createWindow)
也就是當(dāng)Electron完成初始化后,就調(diào)用createWindow
方法來創(chuàng)建瀏覽器窗口mainWindow
(與主進(jìn)程只能有1個不同迫摔,可以根據(jù)需要適時創(chuàng)建更多個瀏覽器窗口沐扳,這些窗口由主進(jìn)程負(fù)責(zé)創(chuàng)建和管理,每個瀏覽器窗口使用一個獨立的渲染進(jìn)程句占;本文只需使用一個瀏覽器窗口沪摄,即mainWindow
)。同時纱烘,使用Menu.buildFromTemplate(appMenuTemplate)
通過一個菜單模板來創(chuàng)建app應(yīng)用主菜單杨拐,模板代碼存放在appmenu.js
文件中(這個文件包含在本文的源碼中,也可以點擊這里查看)擂啥,這個模板的寫法可以參考官方的 Electron API Demos
中Customize Menus
的例子哄陶。模板的第一個菜單是File
菜單,它的子菜單被設(shè)計成空的哺壶,在這里使用menu.items[0].submenu.append
方法向這個File
菜單添加四個子菜單屋吨,分別是:New
(新建文檔)蜒谤,Open
(打開文檔),Save
(保存文檔)至扰,Exit
(退出程序)鳍徽。其中,前三個菜單在點擊后都會向渲染進(jìn)程發(fā)送信息敢课,通知渲染進(jìn)程執(zhí)行相關(guān)處理旬盯。如對于New
菜單,使用mainWindow.webContents.send('action', 'new')
的方式翎猛,通知渲染進(jìn)程要新建一個文檔胖翰。渲染進(jìn)程會使用ipcRenderer.on
方法來執(zhí)行監(jiān)聽,監(jiān)聽到消息后就會執(zhí)行相應(yīng)處理(這部分在index.js
中實現(xiàn))切厘。最后使用Menu.setApplicationMenu(menu)
將主菜單安裝到瀏覽器窗體中(所有窗體會共享主菜單)萨咳。
index.html
這是App的文本編輯頁面。這個頁面很簡單疫稿,整個頁面就只有一個TextArea
控件(id為txtEditor
)培他,平鋪滿整個窗口。該頁面使用require('./index.js')
載入index.js
遗座。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Notepad</title>
<style type="text/css">
body,html{
margin:0px;
height:100%;
}
#txtEditor{
width:100%;
height:99.535%;
padding:0px;
margin:0px;
border:0px;
font-size: 18px;
}
</style>
</head>
<body>
<textarea id="txtEditor"></textarea>
</body>
<script>
require('./index.js');
</script>
</html>
index.js
所有主頁面index.html
涉及到的頁面處理舀凛、與主進(jìn)程交互等的操作都會放到該js文件中。該文件完整代碼:
import { ipcRenderer, remote } from 'electron';
const { Menu, MenuItem, dialog } = remote;
let currentFile = null; //當(dāng)前文檔保存的路徑
let isSaved = true; //當(dāng)前文檔是否已保存
let txtEditor = document.getElementById('txtEditor'); //獲得TextArea文本框的引用
document.title = "Notepad - Untitled"; //設(shè)置文檔標(biāo)題途蒋,影響窗口標(biāo)題欄名稱
//給文本框增加右鍵菜單
const contextMenuTemplate=[
{ role: 'undo' }, //Undo菜單項
{ role: 'redo' }, //Redo菜單項
{ type: 'separator' }, //分隔線
{ role: 'cut' }, //Cut菜單項
{ role: 'copy' }, //Copy菜單項
{ role: 'paste' }, //Paste菜單項
{ role: 'delete' }, //Delete菜單項
{ type: 'separator' }, //分隔線
{ role: 'selectall' } //Select All菜單項
];
const contextMenu=Menu.buildFromTemplate(contextMenuTemplate);
txtEditor.addEventListener('contextmenu', (e)=>{
e.preventDefault();
contextMenu.popup(remote.getCurrentWindow());
});
//監(jiān)控文本框內(nèi)容是否改變
txtEditor.oninput=(e)=>{
if(isSaved) document.title += " *";
isSaved=false;
};
//監(jiān)聽與主進(jìn)程的通信
ipcRenderer.on('action', (event, arg) => {
switch(arg){
case 'new': //新建文件
askSaveIfNeed();
currentFile=null;
txtEditor.value='';
document.title = "Notepad - Untitled";
//remote.getCurrentWindow().setTitle("Notepad - Untitled *");
isSaved=true;
break;
case 'open': //打開文件
askSaveIfNeed();
const files = remote.dialog.showOpenDialog(remote.getCurrentWindow(), {
filters: [
{ name: "Text Files", extensions: ['txt', 'js', 'html', 'md'] },
{ name: 'All Files', extensions: ['*'] } ],
properties: ['openFile']
});
if(files){
currentFile=files[0];
const txtRead=readText(currentFile);
txtEditor.value=txtRead;
document.title = "Notepad - " + currentFile;
isSaved=true;
}
break;
case 'save': //保存文件
saveCurrentDoc();
break;
case 'exiting':
askSaveIfNeed();
ipcRenderer.sendSync('reqaction', 'exit');
break;
}
});
//讀取文本文件
function readText(file){
const fs = require('fs');
return fs.readFileSync(file, 'utf8');
}
//保存文本內(nèi)容到文件
function saveText(text, file){
const fs = require('fs');
fs.writeFileSync(file, text);
}
//保存當(dāng)前文檔
function saveCurrentDoc(){
if(!currentFile){
const file = remote.dialog.showSaveDialog(remote.getCurrentWindow(), {
filters: [
{ name: "Text Files", extensions: ['txt', 'js', 'html', 'md'] },
{ name: 'All Files', extensions: ['*'] } ]
});
if(file) currentFile=file;
}
if(currentFile){
const txtSave=txtEditor.value;
saveText(txtSave, currentFile);
isSaved=true;
document.title = "Notepad - " + currentFile;
}
}
//如果需要保存猛遍,彈出保存對話框詢問用戶是否保存當(dāng)前文檔
function askSaveIfNeed(){
if(isSaved) return;
const response=dialog.showMessageBox(remote.getCurrentWindow(), {
message: 'Do you want to save the current document?',
type: 'question',
buttons: [ 'Yes', 'No' ]
});
if(response==0) saveCurrentDoc(); //點擊Yes按鈕后保存當(dāng)前文檔
}
首先,前面說了号坡,在渲染進(jìn)程中不能直接訪問菜單懊烤,對話框等,它們只存在于主進(jìn)程中宽堆,但可以通過remote
來使用這些資源腌紧。
import { remote } from 'electron';
const { Menu, MenuItem, dialog } = remote;
然后,const contextMenu=Menu.buildFromTemplate(contextMenuTemplate)
即使用contextMenuTemplate
模板來創(chuàng)建編輯器的右鍵菜單(雖然創(chuàng)建過程在渲染進(jìn)程中進(jìn)行畜隶,但實際上使用remote
來創(chuàng)建的菜單壁肋、對話框等,仍然只存在于主進(jìn)程內(nèi))籽慢,由于這里涉及到的菜單都只需要使用系統(tǒng)的內(nèi)建功能浸遗,不需要自定義,所以這里比較簡單嗡综。使用txtEditor.addEventListener('contextmenu')
來監(jiān)聽右鍵菜單請求乙帮,使用contextMenu.popup(remote.getCurrentWindow())
來彈出右鍵菜單。
txtEditor.oninput
用于監(jiān)控文本框內(nèi)容變化极景,如果有改變察净,則將文檔標(biāo)記為尚未保存驾茴,并在標(biāo)題欄最右側(cè)顯示一個*
號作為提示。
PS:在Win7上如果沒有啟用Aero效果氢卡,使用document.title = xxx
或remote.getCurrentWindow().setTitle(xxx)
都看不到程序標(biāo)題欄的標(biāo)題變化锈至,只當(dāng)你比如縮放一下窗口后這個修改才會被刷新。
ipcRenderer.on
用于監(jiān)聽由主進(jìn)程發(fā)來的消息译秦。前面說過峡捡,主進(jìn)程使用mainWindow.webContents.send('action', 'new')
的方式向渲染進(jìn)程發(fā)送特定消息,渲染進(jìn)程監(jiān)聽到消息后筑悴,根據(jù)消息內(nèi)容做出相應(yīng)處理们拙。比如,這里阁吝,當(dāng)主進(jìn)程發(fā)來new
的消息后砚婆,渲染進(jìn)程就開始著手新建一個文檔,在新建前會使用askSaveIfNeed
方法檢測文檔是否需要保存突勇,并提示用戶保存装盯;對于open
的消息就會調(diào)用remote.dialog.showOpenDialog
來顯示一個文件打開對話框,由用戶選擇要打開的文檔然后加載文本數(shù)據(jù)甲馋;而對于save
消息就會對當(dāng)前文檔進(jìn)行保存操作埂奈。
退出時保存檢測的實現(xiàn)過程
正如前面在App功能細(xì)節(jié)中討論的一樣,在關(guān)閉程序前定躏,友好的做法是檢測文檔是否需要保存账磺,如果尚未保存,通知用戶保存共屈。要實現(xiàn)這一功能绑谣,需要在主進(jìn)程和渲染進(jìn)程間進(jìn)行相互通信,以獲得窗體關(guān)閉和文檔保存的確認(rèn)拗引,實現(xiàn)安全退出。
主進(jìn)程端
首先在main.js
中幌衣,使用mainWindow.on('close')
來監(jiān)控mainWindow
窗口的關(guān)閉矾削。
mainWindow.on('close', (e) => {
if(!safeExit){
e.preventDefault();
mainWindow.webContents.send('action', 'exiting');
}
});
這里safeExit
開關(guān)用于標(biāo)記渲染進(jìn)程是否已經(jīng)向主進(jìn)程反饋它已經(jīng)完成所有操作了。如果尚未反饋豁护,則使用e.preventDefault()
阻止窗口關(guān)閉哼凯,并使用mainWindow.webContents.send('action', 'exiting')
向渲染進(jìn)程發(fā)送一個exiting
消息,告訴渲染進(jìn)程:嘿楚里,我要關(guān)掉窗口了断部,你趕緊看看還要什么沒做完的,做完后通知我班缎。
既然主進(jìn)程要等渲染進(jìn)程的反饋蝴光,就需要監(jiān)聽渲染進(jìn)程發(fā)回的消息她渴,所以主進(jìn)程使用ipcMain.on
來執(zhí)行監(jiān)聽。如果渲染進(jìn)程發(fā)送一個exit
消息過來蔑祟,就表示可以安全退出了趁耗。
ipcMain.on('reqaction', (event, arg) => {
switch(arg){
case 'exit':
safeExit=true;
app.quit();
break;
}
});
渲染進(jìn)程端
在渲染進(jìn)程這邊的index.js
中,在ipcRenderer.on
監(jiān)聽方法中疆虚,相應(yīng)的有一個消息處理是針對主進(jìn)程發(fā)來的exiting
消息的苛败,當(dāng)獲知主進(jìn)程準(zhǔn)備關(guān)閉窗口,渲染進(jìn)程就先去檢查文檔是否保存過了径簿,如果尚未保存就通知用戶保存罢屈,用戶保存或取消保存后,使用ipcRenderer.sendSync('reqaction', 'exit')
來向主進(jìn)程發(fā)送一個exit
消息篇亭,表示:我要做的都做完了儡遮,你想退就退吧。
case 'exiting':
askSaveIfNeed();
ipcRenderer.sendSync('reqaction', 'exit');
break;
主進(jìn)程監(jiān)聽到這個消息后暗赶,將safeExit
標(biāo)記為true
鄙币,表示已經(jīng)得到渲染進(jìn)程的確認(rèn),然后就可以使用app.quit()
安全退出了蹂随。當(dāng)然十嘿,在退出前,可以再執(zhí)行一些其它操作(比如保存參數(shù)配置等)岳锁。
編譯打包
- 鍵入以下命令進(jìn)行編譯打包:
npm run make
該命令會將文件打包到當(dāng)前項目目錄下的out
文件夾下绩衷。打包后發(fā)現(xiàn),源碼直接暴露在[app項目目錄]\out\notepad-win32-x64\resources\app\src
目錄下激率。
- 修改
package.json
咳燕,在electronPackagerConfig
部分添加"asar": true
。
"electronPackagerConfig": {
"asar": true
}
重新打包后源碼文件會被打包進(jìn)app.asar
文件中(該文件仍然在src
目錄下)乒躺。
- 可以直接運行打包后的
notepad.exe
啟動程序招盲。
by Mandarava(鰻駝螺) 2017.07.12