從零搭建一個Electron應用
{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Electron 是一個優秀的跨平臺桌面應用程序開源庫,目前接觸 Electron 的開發者也越來越多。但是筆者發現,目前社區裏缺少對初學者足夠友好的入門教程來幫助初學者用 Electron 搭建一個完整的開發框架。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了解決這個問題,筆者將結合平時的一些 Electron 開發經驗,漸近式的帶領讀者從零開始搭建一個完整的 Electron 應用。在這個教程中,筆者將使用 React 構建渲染進程。當然,讀者也可以用其他框架來構建渲染進程,各種前端框架腳手架已經足夠友好,所以這一點不用擔心。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"閱讀完這篇教程,讀者將會了解到:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" - Electron的核心知識點"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" - 如何搭建一個最簡單的 Electron"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" - 如何將 Electron 和前端應用相結合"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" - 如何配置 TypeScript 以保證代碼質量"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" - 如何跨平臺打包 Electron 應用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" - 如何調試 Electron"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"筆者將通過以下 8 個小 Demo 來介紹上面的知識點,爲了保證學習質量,建議讀者手把手跟着練習這些 Demo,讀者可以"},{"type":"link","attrs":{"href":"https://github.com/WangYuLue/electron-demos","title":""},"content":[{"type":"text","text":"點擊這裏"}]},{"type":"text","text":"來下載項目代碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"搭建一個最簡單的Electron"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從零搭建一個React應用(TypeScript,Scss,熱更新)"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將 Electron 與 React 結合"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"打包 Electron 應用"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實際開發一個小 Demo"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主進程使用 TypeScript 構建"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主進程監聽文件變化並重啓"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 vscode 中調試主進程和渲染進程"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在開始之前,我們先聊一聊 Electron 的基礎概念"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Electron 基礎概念"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Electron 是什麼?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Electron 是一個可以用 JavaScript、HTML 和 CSS 構建桌面應用程序的庫。這些應用程序能打包到 Mac、Windows 和 Linux 系統上運行,也能上架到 Mac 和 Windows 的 App Store。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Electron 由什麼組成?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Electron 結合了 Chromium、Node.js 以及 操作系統本地的 API(如打開文件窗口、通知、圖標等)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"一些歷史"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2013年4月Atom Shell 項目啓動 。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2014年5月Atom Shell 被開源 。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2015年4月Atom Shell 被重命名爲 Electron 。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2016年5月Electron 發佈了 v1.0.0 版本 。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2016年5月Electron 構建的應用程序可上架 Mac App Store 。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2016年8月Windows Store 支持 Electron 構建的應用程序 。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Electron 基礎架構"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Electron 與 Chromium 在架構上很相似"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Chromium運行時有一個 "},{"type":"codeinline","content":[{"type":"text","text":"Browser Process"}]},{"type":"text","text":",以及一個或者多個 "},{"type":"codeinline","content":[{"type":"text","text":"Renderer Process"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Renderer Process"}]},{"type":"text","text":" 顧名思義負責渲染Web頁面。"},{"type":"codeinline","content":[{"type":"text","text":"Browser Process"}]},{"type":"text","text":" 則負責管理各個 "},{"type":"codeinline","content":[{"type":"text","text":"Renderer Process"}]},{"type":"text","text":" 以及其他部分(比如菜單欄,收藏夾等等),如下圖:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/07/0713e4dac97ca1c937f5e9c86624ab6e.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Electron中,結構仍然類似,不過這裏是一個 "},{"type":"codeinline","content":[{"type":"text","text":"Main Process"}]},{"type":"text","text":" 管理多個 "},{"type":"codeinline","content":[{"type":"text","text":"Renderer Process"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b2/b2fb3ed4de65b4410e6761074771dc86.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而且在 "},{"type":"codeinline","content":[{"type":"text","text":"Renderer Process"}]},{"type":"text","text":" 可以使用 "},{"type":"codeinline","content":[{"type":"text","text":"Node.js"}]},{"type":"text","text":" 的 API,這就賦予來 Electron 極大的能力,以下是主進程以及渲染進程可以訪問到的API:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b5/b5176047251cef39b74152227e02c8b3.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"如何將 Chromium 與 Node 整合"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Electron 最讓人興奮的地方在於 Chromium 與 Node 的整合。通俗的講,我們可以在 Chromium 的控制檯上做任何 Node 可以做的事。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"能夠做這個整合,首先得益於 Chromium 和 Node.js 都是基於 v8 引擎來執行 js 的,所以給了一種可能,他們是可以一起工作的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是有一個問題,Chromium 和 Node.js 的事件循環機制不同。我們知道,Node.js 是基於 libuv 的,Chromium 也有一套自己的事件循環方式,要讓他們一起工作,就必須整合這兩個事件循環機制。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e4/e4776763c3992d3723a3d1c50775941e.png","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如上圖所示,Electron 採用了這樣一種方式,它起了一個新的線程輪詢 libuv 中的 backend fd,從而監聽 Node.js 中的事件,一旦發現有新的事件發生,就會立即把它 post 到 Chromium 的事件循環中,喚醒主線程處理這個事件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Electron 與 NW.js 的對比以及區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"和 Electron 同樣出名的跨平臺桌面應用開源庫還有 NW.js。他們都有非常出名的應用,例如用Electron開發的有 vscode,用 NW.js 開發的有釘釘。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Electron"}]},{"type":"text","text":" 的原名叫 "},{"type":"codeinline","content":[{"type":"text","text":"Atom Shell"}]},{"type":"text","text":","},{"type":"codeinline","content":[{"type":"text","text":"NW.js"}]},{"type":"text","text":" 的原名叫 "},{"type":"codeinline","content":[{"type":"text","text":"node-webkit"}]},{"type":"text","text":";他們起初是同一個作者開發,而且這個這個作者是國人,先向"},{"type":"link","attrs":{"href":"https://github.com/zcbenz","title":""},"content":[{"type":"text","text":"大佬"}]},{"type":"text","text":"致敬,爲我們開源這麼優秀的開源工具。後來種種原因分爲兩個產品,一個命名爲 "},{"type":"codeinline","content":[{"type":"text","text":"NW.js"}]},{"type":"text","text":"(英特爾公司提供技術支持)、 另一命名爲 "},{"type":"codeinline","content":[{"type":"text","text":"Electron"}]},{"type":"text","text":"(Github 公司提供技術支持)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"兩者在GitHub上的數據對比"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"nw.js (36.7k star, 4051 commits, 256 releases, 748 open issues, 5862 closed)\nelectron (81.7k star, 23364 commits, 849 releases, 1047 open issues, 11612 closed)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看出 "},{"type":"codeinline","content":[{"type":"text","text":"Electron"}]},{"type":"text","text":" 更加活躍。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"兩者程序的入口不同"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 "},{"type":"codeinline","content":[{"type":"text","text":"NW.js"}]},{"type":"text","text":" 中,應用的主入口是網頁或者JS腳本。 你需要在 "},{"type":"codeinline","content":[{"type":"text","text":"package.json"}]},{"type":"text","text":" 中指定一個html或者js文件,一旦應用的主窗口(在html作爲主入口點的情況下)或腳本被執行,應用就會在瀏覽器窗口打開。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 "},{"type":"codeinline","content":[{"type":"text","text":"Electron"}]},{"type":"text","text":" 中,入口是一個 "},{"type":"codeinline","content":[{"type":"text","text":"JavaScript"}]},{"type":"text","text":" 腳本。 不同於直接提供一個URL,"},{"type":"text","marks":[{"type":"strong"}],"text":"你需要手動創建一個瀏覽器窗口"},{"type":"text","text":",然後通過 API 加載 HTML 文件。 你還可以監聽窗口事件,決定何時讓應用退出。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Electron 的工作方式更像 Node.js 運行時 ,Electron 的 APIs 更加底層。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Node 集成"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 "},{"type":"codeinline","content":[{"type":"text","text":"NW.js"}]},{"type":"text","text":",網頁中的 Node 集成需要通過給 "},{"type":"codeinline","content":[{"type":"text","text":"Chromium"}]},{"type":"text","text":" 打補丁來實現。但在 "},{"type":"codeinline","content":[{"type":"text","text":"Electron"}]},{"type":"text","text":" 中,我們選擇了另一種方式:通過各個平臺的消息循環與 "},{"type":"codeinline","content":[{"type":"text","text":"libuv"}]},{"type":"text","text":" 的循環集成,避免了直接在 "},{"type":"codeinline","content":[{"type":"text","text":"Chromium"}]},{"type":"text","text":" 上做改動。這就意味着 "},{"type":"codeinline","content":[{"type":"text","text":"Electron"}]},{"type":"text","text":" 迭代的成本更低。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"準備工作"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有了上面這些基礎概念,接下來開始將下面 8 個 Demo 的學習。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在開始之前,我們先做一些基礎的準備,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"安裝依賴:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"yarn\n\n# or\n\nnpm install"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了防止意外報錯,我們約定 "},{"type":"codeinline","content":[{"type":"text","text":"cd"}]},{"type":"text","text":" 到每個 demo 裏來運行相應 "},{"type":"codeinline","content":[{"type":"text","text":"package.json"}]},{"type":"text","text":" 中的腳本。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以下的 demo 都將基於 Electron 8.0.0 版本講解。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Demo01: 搭建一個最簡單的 Electron"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,我們會搭建一個最簡單的 Electron 應用,它只有 3 個文件,"},{"type":"link","attrs":{"href":"https://github.com/WangYuLue/electron-demos/tree/master/demo01","title":""},"content":[{"type":"text","text":"點這裏"}]},{"type":"text","text":"查看Demo01代碼"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"創建 "},{"type":"codeinline","content":[{"type":"text","text":"demo01"}]},{"type":"text","text":" 目錄:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、新建 "},{"type":"codeinline","content":[{"type":"text","text":"package.json"}]},{"type":"text","text":" 文件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"json"},"content":[{"type":"text","text":"{\n \"name\": \"demo01\",\n \"version\": \"1.0.0\",\n \"main\": \"main.js\",\n \"scripts\": {\n \"start\": \"../node_modules/.bin/electron .\"\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、新建 "},{"type":"codeinline","content":[{"type":"text","text":"index.html"}]},{"type":"text","text":" 文件"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"html"},"content":[{"type":"text","text":"\n\n \n \n Hello World! \n \n \n {\n constructor(props: any) {\n super(props);\n this.state = {\n path: '',\n data: [],\n addFileName: '',\n addFileContent: '',\n }\n }\n\n onChooseFile = () => {\n dialog.showOpenDialog({\n properties: ['openDirectory']\n }).then((res: OpenDialogReturnValue) => {\n const filenames = res.filePaths;\n if (filenames && filenames.length > 0) {\n this.setState({ path: filenames[0] })\n readDistFiles(filenames[0], (data: string[]) => {\n console.log('data', data);\n this.setState({ data })\n })\n }\n });\n }\n\n onAppendFile = (name: string, content: string) => {\n fs.appendFile(path.resolve(this.state.path, name), content, (err: any) => {\n if (err) throw err;\n console.log('Saved!');\n let myNotification = new Notification({ title: '渲染進程通知', body: '新文件添加成功' });\n myNotification.show();\n readDistFiles(this.state.path, (data: string[]) => {\n this.setState({ data })\n })\n });\n }\n\n render() {\n return (\n \n \n \n )\n }\n}\n\nexport default FileList;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的組件比較簡單,我們可以看到,在選擇文件夾時,會調用 "},{"type":"codeinline","content":[{"type":"text","text":"remote.dialog.showOpenDialog"}]},{"type":"text","text":", 它會打開系統的文件窗口。然後,我們可以用 node 的 "},{"type":"codeinline","content":[{"type":"text","text":"fs"}]},{"type":"text","text":" 模塊來寫入或者讀取文件。在讀取成功後,我們還可以通過"},{"type":"codeinline","content":[{"type":"text","text":"remote"}]},{"type":"text","text":" 的 "},{"type":"codeinline","content":[{"type":"text","text":"Notification"}]},{"type":"text","text":" 來調用系統的通知功能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"總的來說,在前端項目中調用 node 相關的模塊,體驗很奇妙。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3、通過 "},{"type":"codeinline","content":[{"type":"text","text":"ipcMain"}]},{"type":"text","text":" 和 "},{"type":"codeinline","content":[{"type":"text","text":"ipcRenderer"}]},{"type":"text","text":" 我們可以實現渲染進程與主進程之間的通信。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ipcMain 在主進程中使用,用來處理渲染進程(網頁)發送的同步和異步的信息:"}]},{"type":"codeblock","attrs":{"lang":"js"},"content":[{"type":"text","text":"const {ipcMain} = require('electron')\n\n// 監聽渲染程序發來的事件\nipcMain.on('something', (event, data) => {\n event.sender.send('something1', '我是主進程返回的值')\n})"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ipcRenderer 在渲染進程中使用,用來發送同步或異步的信息給主進程,也可以用來接收主進程的回覆信息。"}]},{"type":"codeblock","attrs":{"lang":"js"},"content":[{"type":"text","text":"const { ipcRenderer} = require('electron') \n\n// 發送事件給主進程\nipcRenderer.send('something', '傳輸給主進程的值') \n\n// 監聽主進程發來的事件\nipcRenderer.on('something1', (event, data) => {\n console.log(data) // 我是主進程返回的值\n})"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然,我們還可以在 Render 進程中直接使用 "},{"type":"link","attrs":{"href":"https://electronjs.org/docs/api/remote","title":""},"content":[{"type":"text","text":"remote"}]},{"type":"text","text":" 模塊, 這樣的話就可以直接調用 main 進程對象的方法, 而不必顯式發送進程間消息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"js"},"content":[{"type":"text","text":"const { dialog } = require('electron').remote\ndialog.showMessageBox({type: 'info', message: '在渲染進程中直接使用主進程的模塊'})"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經過以上改造,運行 "},{"type":"codeinline","content":[{"type":"text","text":"yarn start"}]},{"type":"text","text":" 我們就可以體驗真正意義上的桌面應用了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Demo05 詳細的代碼可以"},{"type":"link","attrs":{"href":"https://github.com/WangYuLue/electron-demos/tree/master/demo05","title":""},"content":[{"type":"text","text":"戳這裏"}]},{"type":"text","text":"查看。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Demo06: 在主進程中使用 Typescript"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在之前的 Demo 中,我們會發現,在渲染進程中,我們已經用上來 TypeSctipt。但是在主進程中,用的依舊是 javascript。考慮將來項目會越來越大,爲了保證項目的可靠性,在這個 demo 中,我們會將主進程也改造成 Typescipt。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,拷貝 Demo05 文件夾,將其改名爲 Demo06,並進入 Demo06:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、新建 "},{"type":"codeinline","content":[{"type":"text","text":"webpack.main.config.js"}]},{"type":"text","text":" 文件,之後我們會用這個文件的 wabpack 配置來打包主進程的代碼,配置如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"js"},"content":[{"type":"text","text":"const path = require('path');\n\nmodule.exports = {\n target: 'electron-main',\n mode: 'development',\n entry: './main.ts',\n output: {\n filename: './main.js',\n path: path.resolve(__dirname, '')\n },\n module: {\n rules: [\n {\n test: /\\.ts$/,\n use: 'ts-loader',\n exclude: /node_modules/\n }\n ]\n },\n resolve: {\n extensions: ['.ts', '.js'],\n },\n node: {\n __dirname: false,\n __filename: false\n },\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這是一段很簡單的 webpack 配置,其中主要注意兩點:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.1、需要將 "},{"type":"codeinline","content":[{"type":"text","text":"target"}]},{"type":"text","text":" 配置爲 "},{"type":"codeinline","content":[{"type":"text","text":"electron-main"}]},{"type":"text","text":" 表示以接下來 打包的代碼將在 Electron 的 mian 進程中執行"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1.2、由於 webpack 會對 "},{"type":"codeinline","content":[{"type":"text","text":"__dirname"}]},{"type":"text","text":" 和 "},{"type":"codeinline","content":[{"type":"text","text":"__filename"}]},{"type":"text","text":" 做其他額外的處理,爲了保證 "},{"type":"codeinline","content":[{"type":"text","text":"__dirname"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"__filename"}]},{"type":"text","text":" 的行爲和在 node 中保持一致,添加如下配置:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"js"},"content":[{"type":"text","text":"node: {\n __dirname: false,\n __filename: false\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果不這樣配置,打包後 "},{"type":"codeinline","content":[{"type":"text","text":"__dirname"}]},{"type":"text","text":" 和 "},{"type":"codeinline","content":[{"type":"text","text":"__filename"}]},{"type":"text","text":" 將都是 "},{"type":"codeinline","content":[{"type":"text","text":"/"}]},{"type":"text","text":";"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、將 "},{"type":"codeinline","content":[{"type":"text","text":"webpack.config.js"}]},{"type":"text","text":" 改名爲 "},{"type":"codeinline","content":[{"type":"text","text":"webpack.renderer.config.js"}]},{"type":"text","text":",用來和 "},{"type":"codeinline","content":[{"type":"text","text":"webpack.main.config.js"}]},{"type":"text","text":" 保持對應。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3、修改 "},{"type":"codeinline","content":[{"type":"text","text":"package.json"}]},{"type":"text","text":" 中的 "},{"type":"codeinline","content":[{"type":"text","text":"script"}]},{"type":"text","text":" 字段:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"diff"},"content":[{"type":"text","text":" {\n- \"start-electron\": \"../node_modules/.bin/cross-env NODE_ENV=development ../node_modules/.bin/electron .\",\n+ \"start-electron\": \"npm run build-main && ../node_modules/.bin/cross-env ENV=development ../node_modules/.bin/electron .\",\n- \"start\": \"../node_modules/.bin/webpack-dev-server --config webpack.config.js\",\n+ \"start\": \"../node_modules/.bin/webpack-dev-server --config webpack.renderer.config.js\",\n- \"build-render\": \"../node_modules/.bin/webpack --config webpack.config.js\",\n+ \"build-render\": \"../node_modules/.bin/webpack --config webpack.renderer.config.js\",\n+ \"build-main\": \"../node_modules/.bin/webpack --config webpack.main.config.js\",\n \"build-electron\": \"../node_modules/.bin/electron-builder build -mwl\",\n- \"build\": \"npm install && npm run build-render && npm run build-electron\"\n+ \"build\": \"npm install && npm run build-render && npm run build-main && npm run build-electron\"\n }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中注意兩點:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.1、"},{"type":"codeinline","content":[{"type":"text","text":"start-electron"}]},{"type":"text","text":" 將在運行 "},{"type":"codeinline","content":[{"type":"text","text":"electron ."}]},{"type":"text","text":" 先打包主進程的 typescrip 代碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3.2、"},{"type":"codeinline","content":[{"type":"text","text":"build"}]},{"type":"text","text":" 執行後將先打包渲染進程,再打包主進程,最後再打包整個 Electron 應用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經過以上改造,主進程也改造成 typescript 了,項目的可靠性大大增強。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Demo06 詳細的代碼可以"},{"type":"link","attrs":{"href":"https://github.com/WangYuLue/electron-demos/tree/master/demo06","title":""},"content":[{"type":"text","text":"戳這裏"}]},{"type":"text","text":"查看。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經過以上 6 個 demo 的學習,同學們已經有能力搭建一個完整的 Electron 應用。但是我們還有一些進階的用法,感興趣的同學可以繼續往下閱讀。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Demo07: 主進程監聽文件變化並重啓"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面的開發中我們發現,渲染進程修改後可以自動刷新,而主進程卻不行,這可能會影響到我們的開發效率。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以,在 Demo07 中,通過 "},{"type":"codeinline","content":[{"type":"text","text":"nodemon"}]},{"type":"text","text":",我們將主進程也改造成修改後可以自動刷新。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,拷貝 Demo06 文件夾,將其改名爲 Demo07,並進入 Demo07:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、安裝 "},{"type":"codeinline","content":[{"type":"text","text":"nodemon"}]},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"yarn add nodemon -D"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、在 "},{"type":"codeinline","content":[{"type":"text","text":"package.json"}]},{"type":"text","text":" 添加一行腳本:"}]},{"type":"codeblock","attrs":{"lang":"js"},"content":[{"type":"text","text":"{\n \"start-electron-with-nodemon\": \"nodemon --watch main.ts --exec 'npm run start-electron'\",\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們之後會通過 "},{"type":"codeinline","content":[{"type":"text","text":"nodemon"}]},{"type":"text","text":" 來啓動 Electron,它將監聽 "},{"type":"codeinline","content":[{"type":"text","text":"main.ts"}]},{"type":"text","text":" 的文件變化。如果發生變化,則會從新運行 "},{"type":"codeinline","content":[{"type":"text","text":"start-electron"}]},{"type":"text","text":" 命令。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3、將 "},{"type":"codeinline","content":[{"type":"text","text":"webpack.renderer.config.js"}]},{"type":"text","text":" 中 "},{"type":"codeinline","content":[{"type":"text","text":"devServer"}]},{"type":"text","text":" 的 after 鉤子中的 "},{"type":"codeinline","content":[{"type":"text","text":"start-electron"}]},{"type":"text","text":" 改爲 "},{"type":"codeinline","content":[{"type":"text","text":"start-electron-with-nodemon"}]},{"type":"text","text":":"}]},{"type":"codeblock","attrs":{"lang":"diff"},"content":[{"type":"text","text":" devServer: {\n port: 3000,\n after() {\n+ spawn('npm', ['run', 'start-electron-with-nodemon'], {\n- spawn('npm', ['run', 'start-electron'], {\n shell: true,\n env: process.env,\n stdio: 'inherit'\n })\n .on('close', code => process.exit(code))\n .on('error', spawnError => console.error(spawnError));\n }\n }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經過以上的簡單配置,我們的主進程也能修改文件後自動刷新了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Demo07 詳細的代碼可以"},{"type":"link","attrs":{"href":"https://github.com/WangYuLue/electron-demos/tree/master/demo07","title":""},"content":[{"type":"text","text":"戳這裏"}]},{"type":"text","text":"查看。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Demo08: 在 vscode 中調試主進程和渲染進程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"任何時候,如果代碼出了 bug,我們的第一反應是打印 log 看一下出了什麼問題。但是這樣的調試方式相對低效,有些時候,我們需要借用編輯器的調試功能來幫助我們調試代碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這個 Demo 中,筆者將嘗試用 vscode 自帶的調試工具來調試 electron 的主進程和渲染進程;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於 vscode 中的調試配置項相對較多,所以強烈建議大家先看一遍 vscode 調試的"},{"type":"link","attrs":{"href":"https://code.visualstudio.com/docs/nodejs/nodejs-debugging","title":""},"content":[{"type":"text","text":"官方文檔"}]},{"type":"text","text":",或者看一下 github 上 [Electron調試實際案例](https://github.com/Microsoft/vscode-recipes/tree/master/Electron)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文的配置與上面提到的"},{"type":"link","attrs":{"href":"https://github.com/Microsoft/vscode-recipes/tree/master/Electron","title":""},"content":[{"type":"text","text":"實際案例"}]},{"type":"text","text":"有一些差異,因爲我們 demo 開發環境的 render 進程是用一個 web server 啓動的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,拷貝 Demo07 文件夾,將其改名爲 Demo08,並進入 Demo08:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1、在 "},{"type":"codeinline","content":[{"type":"text","text":"webpack.main.config.js"}]},{"type":"text","text":" 和 "},{"type":"codeinline","content":[{"type":"text","text":"webpack.render.config.js"}]},{"type":"text","text":" 中添加 "},{"type":"codeinline","content":[{"type":"text","text":"devtool: 'source-map'"}]},{"type":"text","text":"。因爲主進程和渲染進程都是用 "},{"type":"codeinline","content":[{"type":"text","text":"typescript"}]},{"type":"text","text":" 寫的,需要在打包時生成 "},{"type":"codeinline","content":[{"type":"text","text":"source maps"}]},{"type":"text","text":" 以形成映射,才能在 "},{"type":"codeinline","content":[{"type":"text","text":"typescript"}]},{"type":"text","text":" 文件中正確的調試代碼。詳情可以查看"},{"type":"link","attrs":{"href":"https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_source-maps","title":""},"content":[{"type":"text","text":"官方文檔"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2、改造"},{"type":"codeinline","content":[{"type":"text","text":"webpack.renderer.config.js"}]},{"type":"text","text":",將 "},{"type":"codeinline","content":[{"type":"text","text":"devServer"}]},{"type":"text","text":" 的 after 鉤子函數。"}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" devServer: {\n port: 3000,\n after() {\n- spawn('npm', ['run', 'start-electron-with-nodemon'], {\n+ spawn('npm', ['run', 'build-main'], {\n shell: true,\n env: process.env,\n stdio: 'inherit'\n })\n- .on('close', code => process.exit(code))\n .on('error', spawnError => console.error(spawnError));\n }\n }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這也就意味着運行渲染進程後,只會將主進程的代碼打包一下,而不再將主進程啓動起來。因爲在稍後的調試中我們會在 vscode 的 "},{"type":"codeinline","content":[{"type":"text","text":"launch.json"}]},{"type":"text","text":" 中啓動主進程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3、運行"},{"type":"codeinline","content":[{"type":"text","text":"yarn start"}]},{"type":"text","text":",啓動渲染進程,並且給主進程打包。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4、在當前項目目錄下創建一個 "},{"type":"codeinline","content":[{"type":"text","text":".vscode"}]},{"type":"text","text":" 目錄,並且在該目錄下創建一個 "},{"type":"codeinline","content":[{"type":"text","text":"launch.json"}]},{"type":"text","text":" 文件,在該文件裏添加如下配置:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"json{\n \"version\": \"0.2.0\",\n \"configurations\": [\n {\n \"type\": \"node\",\n \"request\": \"launch\",\n \"name\": \"Electron: Main\",\n \"protocol\": \"inspector\",\n \"runtimeExecutable\": \"${workspaceFolder}/node_modules/.bin/electron\",\n \"runtimeArgs\": [\n \"--remote-debugging-port=9233\",\n \"./demo08/main.js\"\n ]\n },\n {\n \"type\": \"chrome\",\n \"request\": \"attach\",\n \"name\": \"Electron: Renderer\",\n \"port\": 9233,\n \"url\": \"http://localhost:3000\",\n \"webRoot\": \"${workspaceFolder}/demo08\",\n },\n ],\n \"compounds\": [\n {\n \"name\": \"Electron: All\",\n \"configurations\": [\n \"Electron: Main\",\n \"Electron: Renderer\"\n ]\n }\n ]\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以看到,在 "},{"type":"codeinline","content":[{"type":"text","text":"configurations"}]},{"type":"text","text":" 中有兩個對象:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中 \"Electron: Main\" 表示的是對主進程的調試,它的 "},{"type":"codeinline","content":[{"type":"text","text":"request"}]},{"type":"text","text":" 爲 "},{"type":"codeinline","content":[{"type":"text","text":"launch"}]},{"type":"text","text":",表示在調試的時候啓動主進程。我們還可以通過 "},{"type":"codeinline","content":[{"type":"text","text":"env"}]},{"type":"text","text":" 來傳入環境變量。它還會暴露一個 "},{"type":"codeinline","content":[{"type":"text","text":"--remote-debugging-port"}]},{"type":"text","text":" 的遠程調試端口,這個端口很重要,因爲在接下來調試渲染進程時會用到它。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"\"Electron: Renderer\" 表示的是對渲染進程的調試,它的 "},{"type":"codeinline","content":[{"type":"text","text":"request"}]},{"type":"text","text":" 爲 "},{"type":"codeinline","content":[{"type":"text","text":"attach"}]},{"type":"text","text":",表示只是連接到正在調試的進程。而它的 "},{"type":"codeinline","content":[{"type":"text","text":"port"}]},{"type":"text","text":" 剛好是 "},{"type":"codeinline","content":[{"type":"text","text":"--remote-debugging-port"}]},{"type":"text","text":" 暴露出來的端口,"},{"type":"codeinline","content":[{"type":"text","text":"url"}]},{"type":"text","text":" 則是本地渲染進程的地址。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接着,我們會看到 "},{"type":"codeinline","content":[{"type":"text","text":"compounds"}]},{"type":"text","text":",它的作用很簡單,就是把主進程調試和渲染進程調試結合起來。當調試時選擇 "},{"type":"codeinline","content":[{"type":"text","text":"Electron: All"}]},{"type":"text","text":" 時,就可以把 \"Electron: Main\" 和 \"Electron: Renderer\" 都拉起來。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這一塊的配置比較多,其中每個配置的作用可以參考 vscode 的"},{"type":"link","attrs":{"href":"https://code.visualstudio.com/docs/nodejs/nodejs-debugging","title":""},"content":[{"type":"text","text":"官方文檔"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5、點擊 vscode 自帶的調試按鈕,選擇 "},{"type":"text","marks":[{"type":"strong"}],"text":"Electron: All"},{"type":"text","text":",就可以將 electron 啓動起來了,這時候在主進程的 "},{"type":"codeinline","content":[{"type":"text","text":".ts"}]},{"type":"text","text":" 文件和渲染進程的 "},{"type":"codeinline","content":[{"type":"text","text":".ts"}]},{"type":"text","text":" 文件中打斷點就可以發現都能起作用了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">注意,實際測試發現,主進程的調試需要在打包後的 "},{"type":"codeinline","content":[{"type":"text","text":"main.js"}]},{"type":"text","text":" 中打一次斷點,然後在通過 "},{"type":"codeinline","content":[{"type":"text","text":"source map"}]},{"type":"text","text":" 和 js 文件生成的只讀的 typescipt 文件中打斷點才能順利調試。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經過以上的配置,我們可以順利的在主進程和渲染進程中調試代碼了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Demo08 詳細的代碼可以"},{"type":"link","attrs":{"href":"https://github.com/WangYuLue/electron-demos/tree/master/demo08","title":""},"content":[{"type":"text","text":"戳這裏"}]},{"type":"text","text":"查看。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過以上的一系列 Demo,我們重零搭建一個完整的 Electron 應用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們瞭解了 Electron的核心知識點、搭建一個最簡單的 Electron,將 Electron 和前端應用相結合,配置 TypeScript 以保證代碼質量,跨平臺打包 Electron 應用以及 如何調試 Electron。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這些 demo 的完整代碼可以"},{"type":"link","attrs":{"href":"https://github.com/WangYuLue/electron-demos","title":""},"content":[{"type":"text","text":"點這裏"}]},{"type":"text","text":"查看,如果感覺這些 demo 寫的不錯,可以給筆者一個 star,謝謝大家閱讀"}]}]}
當前文件夾路徑:{this.state.path}
\n 文件夾下文件列表:
\n \n {\n this.state.data.map(file => {\n return (\n
\n \n {file}\n
\n )\n })\n }\n \n
\n \n 文件名稱:\n this.setState({ addFileName: e.target.value })} \n />\n
\n \n 文件內容:\n this.setState({ addFileContent: e.target.value })} \n />\n
\n \n 發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.