天天都要寫次日的 todoList。有一天在寫的時候忽然想到,爲了讓本身清楚知道本身須要作啥、作了多少、還剩多少沒作,想寫一個電腦端程序,在技術選型的時候就選了 electron。
3天時間寫了個 PC 端應用程序。先看看結果吧javascript
爲何要選 electron 做爲 pc 端開發方案?
史前時代,以 MFC 爲表明的技術棧,開發效率較低,維護成本高。
後來使用 QT 技術,特色是使用 DirectUI + 面向對象 + XML 定義 UI,適用於小型軟件、性能要求、包大小、UI 複雜度叫高的需求。
再到後來,以 QT Quick 爲表明的技術,特色是框架自己提供子控件,基於子控件組合來建立新的控件。相似於 ActionScript 的腳本化界面邏輯代碼。
新時代主要是以 electron 和 Cef 爲 表明。特色是界面開發以 Web 技術爲主,部分邏輯須要 Native 代碼實現。你們都熟悉的 VS Code 就是使用 electron 開發的。適用於 UI 變化較多、體積限制不大、開發效率高的場景。css
拿 C 系列寫應用程序的體驗很差,累到奔潰。再加上有 Hybrid、React Native、iOS、Vue、React 等開發經驗,electron 是不二選擇。html
執行下面命令快速體驗 Hello world,也是官方給的一個 Demo。前端
git clone https://github.com/electron/electron-quick-start cd electron-quick-start npm install && npm start
簡單介紹下 Demo 工程,工程目錄以下所示
vue
在終端執行 npm start
執行的是 package.json 中的 scripts
節點下的 start 命令,也就是 electron .
,.
表明執行 main.js 中的邏輯。java
// Modules to control application life and create native browser window const {app, BrowserWindow} = require('electron') const path = require('path') function createWindow () { // Create the browser window. const mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js') } }) // and load the index.html of the app. mainWindow.loadFile('index.html') // Open the DevTools. mainWindow.webContents.openDevTools() } // 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.whenReady().then(createWindow) // Quit when all windows are closed. app.on('window-all-closed', function () { // On macOS 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', function () { // On macOS 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 (BrowserWindow.getAllWindows().length === 0) 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 require them here.
寫過 Vue、React、Native 的人看代碼很容易,由於應用程序的生命週期鉤子函數是很重要的,開發者根據需求在鉤子函數裏面作相應的視圖建立、初始化、銷燬對象等等。好比 electron 中的 activate、window-all-closed 等。node
app 對象在 whenReady
的時候執行 createWindow 方法。內部建立了一個 BrowserWindow
對象,指定了大小和功能設置(webPreferences Object (可選) - 網頁功能的設置。其中 preload String (可選) - 在頁面運行其餘腳本以前預先加載指定的腳本 不管頁面是否集成 Node, 此腳本均可以訪問全部 Node API 腳本路徑爲文件的絕對路徑。 當 node integration 關閉時, 預加載的腳本將從全局範圍從新引入 node 的全局引用標誌)。git
mainWindow.loadFile('index.html')
加載了同級目錄下的 index.html 文件。也能夠加載服務器資源(部署好的網頁),好比 win.loadURL('https://github.com/FantasticLBP')
github
// All of the Node.js APIs are available in the preload process. // It has the same sandbox as a Chrome extension. window.addEventListener('DOMContentLoaded', () => { const replaceText = (selector, text) => { const element = document.getElementById(selector) if (element) element.innerText = text } console.table(process) console.info(process.versions) for (const type of ['chrome', 'node', 'electron']) { replaceText(`${type}-version`, process.versions[type]) } })
接下去看看 preload.js。在頁面運行其餘腳本以前預先加載指定的腳本,不管頁面是否集成 Node, 此腳本均可以訪問全部 Node API 腳本路徑爲文件的絕對路徑。Demo 中的邏輯很簡單,就是讀取 process.versions 對象中的 node、chrome、electron 的版本信息並展現出來。web
index.html 中的內容就是主頁面顯示的內容。
electron 分爲渲染進程和主進程。 😂 和 Native 中的概念不同的是 electron 中主進程只有一個,渲染進程(也就是 UI 進程) 有多個。主進程在後臺運行,每次打開一個界面,會新開一個新的渲染進程。
這張圖是 chromium 多進程架構圖。早在2007年以前,市面上的瀏覽器都是單進程架構。單進程瀏覽器指的是瀏覽器的全部功能模塊都是運行在同一個進程裏的,這些模塊包括網絡、插件、Javascript 運行環境、渲染引擎和頁面等。如此複雜的功能都在一個進程內運行,因此致使瀏覽器出現不穩定、不安全、不流暢等問題。
多進程架構的瀏覽器解決了上述問題,至於如何解決的之後的文章會專門講解,不是本文的主要內容。
簡單描述下。
RenderProcessHost
和 render 進程中的 RenderProcess
是用來處理進程間通訊的(IPC)。ResourceDispatcher
是用來處理資源請求的。Render 進程中若是有請求則建立一個請求 ID,轉發到 IPC,由 Browser 進程中處理後返回對於 chromium 多進程架構感興趣的能夠點擊這個連接查看更多資料-Multi-process Architecture。
Electron 架構和 Chromium 架構相似,也是具備1個主進程和多個渲染進程。可是也有區別
技術難點:因爲 Electron 內部整合了 Chromium 和 Node.js,主線程在某個時刻只能夠執行一個事件循環,可是2者的事件循環機制不同,Node.js 的事件循環基於 libuv,可是 Chromium 基於 message bump。
因此 Electron 原理的重點就是「如何整合事件循環」。2種思路
後來隨着 libuv 引入 backend_fd 的概念,至關因而 libuv 輪詢事件的文件描述符。經過輪訓 backend_fd 能夠知道 libuv 的新事件。因此 Electron 採起的作法就是將 Node.js 集成到 Chromium 中。
上圖描述了 Node.js 如何融入到 Chromium 中。描述下原理
上述2個步驟完成了 Electron 的事件循環。
調試分爲主進程調試和渲染進程調試。
看到 Demo 工程中執行 npm start
以後能夠看到主界面,Mac 端快捷鍵 comand + option + i
,喚出調試界面,相似於 chrome 下的 devtools。其實就是無頭瀏覽器的那些東西。或者在代碼裏打開調試模式 mainWindow.webContents.openDevTools()
。
工程採用 Electron + Vue 技術,下面截圖 Vue-devtools 很方便查看 Vue 組件層級等 Vue 相關的調試
主進程調試有2種方法
方法一:利用 chrome inspect 功能進行調試
package.json
中的 scripts 節點下的 start 命令加上調試開關--inspect=[port] // electrom --inspect=8001 yourApp
chrome://inspect
configure
,在彈出的面板中填寫須要調試的端口信息npm start
,在 chrome inspect 面板的 Target
節點中選擇須要調試的頁面main.js
。能夠加斷點進行調試方法二:利用 VS Code 調試 electron 主進程。
launch.json
編輯 launch.json 的文件內容。以下
{ "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Debug main process", "cwd": "${workspaceRoot}", "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron", "windows": { "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd" }, "args": ["."], "outputCapture": "std" } ] }
Electron 的渲染進程中的代碼改變了,使用 Command + R 能夠刷新,可是修改主進程中的代碼則必須從新啓動 yarn run dev
。效率低下,因此爲了提高開發效率,有必要解決這個問題
Webpack 有一個 api: watch-run
,能夠針對代碼文件檢測,有變化則 Restart
render.js
中。render.js
中寫 Node 代碼的時候須要在 main.js
初始化 BrowserWindow 的時候,在 webPreferences 節點下添加 nodeIntegration: true
。否則會報錯:renderer.js:9 Uncaught ReferenceError: process is not defined。從 Chrome Extenstion V2 開始,不容許執行任何 inline javascript 代碼在 html 中。不支持之內聯方式寫事件綁定代碼。好比 <button onclick="handleCPU">查看</button>
Refused to execute inline event handler because it violates the following Content Security Policy directive:
在使用 Vue、React 開發 electron 應用時,可使用 npm 或 yarn install 包,也可使用 electron-vue 腳手架工具。
vue init simulatedgreg/electron-vue my-project cd my-project npm install npm run dev
新開項目建立後會報錯.
初始化工程後會報錯 ERROR in Template execution failed: ReferenceError: process is not defined
。解決辦法是使用 nvm 將 node 版本將爲 10。
繼續運行仍是報錯,以下
┏ Electron ------------------- [11000:0615/095124.922:ERROR:CONSOLE(7574)] "Extension server error: Object not found: <top>", source: chrome-devtools://devtools/bundled/inspector.js (7574) ┗ ----------------------------
解決辦法是在 main/index.dev.js 修改代碼
- require('electron-debug')({ showDevTools: true }); + // NB: Don't open dev tools with this, it is causing the error + require('electron-debug')();
在 In main/index.js in the createWindow() function:
mainWindow.loadURL(winURL); // Open dev tools initially when in development mode if (process.env.NODE_ENV === 'development"') { mainWindow.webContents.on('did-frame-finish-load', () => { mainWindow.webContents.once('devtools-opened', () => { mainWindow.focus() }) mainWindow.webContents.openDevTools() }) }
其實一個技術自己的難易程度並非可否在本身企業、公司、團隊內順利使用的惟一標尺,其配套的 CI/CD、APM、埋點系統、發佈更新、灰度測試等可否與現有的系統以較小成本融合纔是很大的決定要素。由於某個技術並非很是難,要是大多數開發者以爲很難,那它設計上就是失敗的。
Electron 提供的 crash 信息進行包裝。
引伸資料