教你如何開發(fā)Vite3插件構(gòu)建Electron開發(fā)環(huán)境
開發(fā)新版本 Vue 項(xiàng)目推薦你使用 Vite 腳手架構(gòu)建開發(fā)環(huán)境,然而 Vite 腳手架更傾向于構(gòu)建純 Web 頁面,而不是桌面應(yīng)用,因此開發(fā)者要做很多額外的配置和開發(fā)工作才能把 Electron 引入到 Vue 項(xiàng)目中,這也是很多開發(fā)者都基于開源工具來構(gòu)建 Electron+Vue 的開發(fā)環(huán)境的原因。
但這樣做有兩個(gè)問題:第一個(gè)是這些開源工具封裝了很多技術(shù)細(xì)節(jié),導(dǎo)致開發(fā)者想要修改某項(xiàng)配置非常不方便;另一個(gè)是這些開源工具的實(shí)現(xiàn)方式我認(rèn)為也并不是很好。
所以,我還是建議你盡量 自己寫代碼構(gòu)建 Electron+Vue 的開發(fā)環(huán)境 ,這樣可以讓自己更從容地控制整個(gè)項(xiàng)目。
具體應(yīng)該怎么做呢?接下來我將帶你按如下幾個(gè)步驟構(gòu)建一個(gè) Vite+Electron 的開發(fā)環(huán)境:
創(chuàng)建項(xiàng)目
首先通過命令行創(chuàng)建一個(gè) Vue 項(xiàng)目:
npm create vite@latest electron-jue-jin -- --template vue-ts
接著安裝 Electron 開發(fā)依賴:
npm install electron -D
安裝完成后,你的項(xiàng)目根目錄下的 package.json 文件應(yīng)該與下面大體類似:
{ "name": "electron-jue-jin", "private": true, "version": "0.0.1", "scripts": { "dev": "vite", "build": "vue-tsc --noEmit && vite build", "preview": "vite preview" }, "dependencies": {}, "devDependencies": { "vue": "^3.2.37", "@vitejs/plugin-vue": "^3.0.0", "electron": "^19.0.8", "typescript": "^4.6.4", "vite": "^3.0.0", "vue-tsc": "^0.38.4" } }
注意:這里我們 把 vue 從 dependencies 配置節(jié)移至了 devDependencies 配置節(jié)。這是因?yàn)樵?Vite 編譯項(xiàng)目的時(shí)候,Vue 庫會(huì)被編譯到輸出目錄下,輸出目錄下的內(nèi)容是完整的,沒必要把 Vue 標(biāo)記為生產(chǎn)依賴;而且在我們將來制作安裝包的時(shí)候,還要用到這個(gè) package.json 文件,它的生產(chǎn)依賴?yán)锊粦?yīng)該有沒用的東西,所以我們在這里做了一些調(diào)整。
到這里,我們就創(chuàng)建了一個(gè)基本的 Vue+TypeScript 的項(xiàng)目,接下來我們就為這個(gè)項(xiàng)目引入 Electron 模塊。
創(chuàng)建主進(jìn)程代碼
創(chuàng)建好項(xiàng)目之后,我們創(chuàng)建主進(jìn)程的入口程序:src\main\mainEntry.ts。
這個(gè)入口程序的代碼很簡單,如下所示:
//src\main\mainEntry.ts import { app, BrowserWindow } from "electron"; let mainWindow: BrowserWindow; app.whenReady().then(() => { mainWindow = new BrowserWindow({}); mainWindow.loadURL(process.argv[2]); });
在這段代碼里,我們在 app ready 之后創(chuàng)建了一個(gè)簡單的 BrowserWindow 對象。app 是 Electron 的全局對象,用于控制整個(gè)應(yīng)用程序的生命周期。在 Electron 初始化完成后,app 對象的 ready 事件被觸發(fā),這里我們使用 app.whenReady() 這個(gè) Promise 方法來等待 ready 事件的發(fā)生。
mainWindow 被設(shè)置成一個(gè)全局變量,這樣可以避免主窗口被 JavaScript 的垃圾回收器回收掉。另外,窗口的所有配置都使用了默認(rèn)的配置。
這個(gè)窗口加載了一個(gè) Url 路徑,這個(gè)路徑是以命令行參數(shù)的方式傳遞給應(yīng)用程序的,而且是命令行的第三個(gè)參數(shù)。
app 和 BrowserWindow 都是 Electron 的內(nèi)置模塊,這些內(nèi)置模塊是通過 ES Module 的形式導(dǎo)入進(jìn)來的,我們知道 Electron 的內(nèi)置模塊都是通過 CJS Module 的形式導(dǎo)出的,這里之所以可以用 ES Module 導(dǎo)入,是因?yàn)槲覀兘酉聛碜龅闹鬟M(jìn)程編譯工作幫我們完成了相關(guān)的轉(zhuǎn)化工作。
開發(fā)環(huán)境 Vite 插件
主進(jìn)程的代碼寫好之后,只有編譯過之后才能被 Electron 加載,我們是 通過 Vite
插件的形式來完成這個(gè)編譯工作和加載工作 的,如下代碼所示:
//plugins\devPlugin.ts import { ViteDevServer } from "vite"; export let devPlugin = () => { return { name: "dev-plugin", configureServer(server: ViteDevServer) { require("esbuild").buildSync({ entryPoints: ["./src/main/mainEntry.ts"], bundle: true, platform: "node", outfile: "./dist/mainEntry.js", external: ["electron"], }); server.httpServer.once("listening", () => { let { spawn } = require("child_process"); let addressInfo = server.httpServer.address(); let httpAddress = `http://${addressInfo.address}:${addressInfo.port}`; let electronProcess = spawn(require("electron").toString(), ["./dist/mainEntry.js", httpAddress], { cwd: process.cwd(), stdio: "inherit", }); electronProcess.on("close", () => { server.close(); process.exit(); }); }); }, }; };
這是一個(gè)簡單的 Vite 插件,在這個(gè)插件中我們注冊了一個(gè)名為 configureServer
的鉤子,當(dāng) Vite 為我們啟動(dòng) Http 服務(wù)的時(shí)候,configureServer
鉤子會(huì)被執(zhí)行。
這個(gè)鉤子的輸入?yún)?shù)為一個(gè)類型為 ViteDevServer
的對象 server
,這個(gè)對象持有一個(gè) http.Server
類型的屬性 httpServer
,這個(gè)屬性就代表著我們調(diào)試 Vue 頁面的 http 服務(wù),一般情況下地址為:http://127.0.0.1:5173/
我們可以 通過監(jiān)聽 server.httpServer
的 listening
事件來判斷 httpServer 是否已經(jīng)成功啟動(dòng),如果已經(jīng)成功啟動(dòng)了,那么就啟動(dòng) Electron 應(yīng)用,并給它傳遞兩個(gè)命令行參數(shù),第一個(gè)參數(shù)是主進(jìn)程代碼編譯后的文件路徑,第二個(gè)參數(shù)是 Vue 頁面的 http 地址,這里就是 http://127.0.0.1:5173/
為什么這里傳遞了兩個(gè)命令行參數(shù),而主進(jìn)程的代碼接收第三個(gè)參數(shù)(process.argv[2]
)當(dāng)做 http 頁面的地址呢?因?yàn)?默認(rèn)情況下 electron.exe 的文件路徑將作為第一個(gè)參數(shù)。也就是我們通過 require("electron")
獲得的字符串。
這個(gè)路徑一般是:node_modules\electron\dist\electron.exe
,如果這個(gè)路徑下沒有對應(yīng)的文件,說明你的 Electron 模塊沒有安裝好。
我們是 通過 Node.js
child_process
模塊的 spawn
方法啟動(dòng) electron
子進(jìn)程的,除了兩個(gè)命令行參數(shù)外,還傳遞了一個(gè)配置對象。
這個(gè)對象的 cwd
屬性用于設(shè)置當(dāng)前的工作目錄,process.cwd()
返回的值就是當(dāng)前項(xiàng)目的根目錄。stdio
用于設(shè)置 electron 進(jìn)程的控制臺(tái)輸出,這里設(shè)置 inherit
可以讓 electron 子進(jìn)程的控制臺(tái)輸出數(shù)據(jù)同步到主進(jìn)程的控制臺(tái)。這樣我們在主進(jìn)程中 console.log
的內(nèi)容就可以在 VSCode 的控制臺(tái)上看到了。
當(dāng) electron 子進(jìn)程退出的時(shí)候,我們要關(guān)閉 Vite 的 http 服務(wù),并且控制父進(jìn)程退出,準(zhǔn)備下一次啟動(dòng)。
http 服務(wù)啟動(dòng)之前,我們 使用 esbuild
模塊完成了主進(jìn)程 TypeScript 代碼的編譯工作 ,這個(gè)模塊是 Vite
自帶的,所以我們不需要額外安裝,可以直接使用。
主進(jìn)程的入口文件是通過 entryPoints
配置屬性設(shè)置的,編譯完成后的輸出文件時(shí)通過 outfile
屬性配置的。
編譯平臺(tái) platform
設(shè)置為 node
,排除的模塊 external
設(shè)置為 electron
, 正是這兩個(gè)設(shè)置使我們可以在主進(jìn)程代碼中可以通過 import
的方式導(dǎo)入 electron 內(nèi)置的模塊 。非但如此,Node 的內(nèi)置模塊也可以通過 import
的方式引入。
這個(gè) Vite 插件的代碼編寫好后,在 vite.config.ts
文件中引入一下就可以使用了,如下代碼所示:
// vite.config.ts import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; import { devPlugin } from "./plugins/devPlugin"; import optimizer from "vite-plugin-optimizer"; export default defineConfig({ plugins: [devPlugin(), vue()], });
現(xiàn)在執(zhí)行命令 npm run dev
,你會(huì)看到 Electron 應(yīng)用加載了 Vue 的首頁,如下圖所示:
關(guān)閉窗口,主進(jìn)程和子進(jìn)程也會(huì)跟著退出。修改一下 Vue 組件里的內(nèi)容,窗口內(nèi)顯示的內(nèi)容也會(huì)跟著變化,說明熱更新機(jī)制在起作用。
渲染進(jìn)程集成內(nèi)置模塊
現(xiàn)在主進(jìn)程內(nèi)可以自由的使用 Electron 和 Node.js 的內(nèi)置模塊了,但渲染進(jìn)程還不行,接下去我們就為渲染進(jìn)程集成這些內(nèi)置模塊。
首先我們修改一下主進(jìn)程的代碼,打開渲染進(jìn)程的一些開關(guān),允許渲染進(jìn)程使用 Node.js 的內(nèi)置模塊,如下代碼所示:
// src\main\mainEntry.ts import { app, BrowserWindow } from "electron"; process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = "true"; let mainWindow: BrowserWindow; app.whenReady().then(() => { let config = { webPreferences: { nodeIntegration: true, webSecurity: false, allowRunningInsecureContent: true, contextIsolation: false, webviewTag: true, spellcheck: false, disableHtmlFullscreenWindowResize: true, }, }; mainWindow = new BrowserWindow(config); mainWindow.webContents.openDevTools({ mode: "undocked" }); mainWindow.loadURL(process.argv[2]); });
在這段代碼中,有以下幾點(diǎn)需要注意:
1:ELECTRON_DISABLE_SECURITY_WARNINGS
用于設(shè)置渲染進(jìn)程開發(fā)者調(diào)試工具的警告,這里設(shè)置為 true 就不會(huì)再顯示任何警告了。
如果渲染進(jìn)程的代碼可以訪問 Node.js 的內(nèi)置模塊,而且渲染進(jìn)程加載的頁面(或腳本)是第三方開發(fā)的,那么惡意第三方就有可能使用 Node.js 的內(nèi)置模塊傷害最終用戶 。這就是為什么這里要有這些警告的原因。如果你的應(yīng)用不會(huì)加載任何第三方的頁面或腳本。那么就不用擔(dān)心這些安全問題啦。
2:nodeIntegration
配置項(xiàng)的作用是把 Node.js 環(huán)境集成到渲染進(jìn)程中,contextIsolation
配置項(xiàng)的作用是在同一個(gè) JavaScript 上下文中使用 Electron API。其他配置項(xiàng)與本文主旨無關(guān),大家感興趣的話可以自己翻閱官方文檔。
3: webContents
的openDevTools
方法用于打開開發(fā)者調(diào)試工具。
完成這些工作后我們就可以在開發(fā)者調(diào)試工具中訪問 Node.js 和 Electron 的內(nèi)置模塊了。
設(shè)置 Vite 模塊別名與模塊解析鉤子
雖然我們可以在開發(fā)者調(diào)試工具中使用 Node.js 和 Electron 的內(nèi)置模塊,但現(xiàn)在還不能在 Vue 的頁面內(nèi)使用這些模塊。
這是因?yàn)?Vite 主動(dòng)屏蔽了這些內(nèi)置的模塊,如果開發(fā)者強(qiáng)行引入它們,那么大概率會(huì)得到如下報(bào)錯(cuò):
Module "xxxx" has been externalized for browser compatibility and cannot be accessed in client code.
接下去我們就介紹如何讓 Vite 加載 Electron 的內(nèi)置模塊和 Node.js 的內(nèi)置模塊。
首先我們?yōu)楣こ贪惭b一個(gè) Vite 組件:vite-plugin-optimizer
npm i vite-plugin-optimizer -D
然后修改 vite.config.ts 的代碼,讓 Vite 加載這個(gè)插件,如下代碼所示:
// vite.config.ts import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; import { devPlugin, getReplacer } from "./plugins/devPlugin"; import optimizer from "vite-plugin-optimizer"; export default defineConfig({ plugins: [optimizer(getReplacer()), devPlugin(), vue()], });
vite-plugin-optimizer 插件會(huì)為你創(chuàng)建一個(gè)臨時(shí)目錄:node_modules.vite-plugin-optimizer
然后把類似 const fs = require('fs'); export { fs as default }
這樣的代碼寫入這個(gè)目錄下的 fs.js 文件中。
渲染進(jìn)程執(zhí)行到:import fs from "fs" 時(shí),就會(huì)請求這個(gè)目錄下的 fs.js 文件,這樣就達(dá)到了在渲染進(jìn)程中引入 Node 內(nèi)置模塊的目的。
getReplacer 方法是我們?yōu)?vite-plugin-optimizer 插件提供的內(nèi)置模塊列表。代碼如下所示:
// plugins\devPlugin.ts export let getReplacer = () => { let externalModels = ["os", "fs", "path", "events", "child_process", "crypto", "http", "buffer", "url", "better-sqlite3", "knex"]; let result = {}; for (let item of externalModels) { result[item] = () => ({ find: new RegExp(`^${item}$`), code: `const ${item} = require('${item}');export { ${item} as default }`, }); } result["electron"] = () => { let electronModules = ["clipboard", "ipcRenderer", "nativeImage", "shell", "webFrame"].join(","); return { find: new RegExp(`^electron$`), code: `const {${electronModules}} = require('electron');export {${electronModules}}`, }; }; return result; };
我們在這個(gè)方法中把一些常用的 Node 模塊和 electron 的內(nèi)置模塊提供給了 vite-plugin-optimizer 插件,以后想要增加新的內(nèi)置模塊只要修改這個(gè)方法即可。而且 vite-plugin-optimizer 插件不僅用于開發(fā)環(huán)境,編譯 Vue 項(xiàng)目時(shí),它也會(huì)參與工作 。
再次運(yùn)行你的應(yīng)用,看看現(xiàn)在渲染進(jìn)程是否可以正確加載內(nèi)置模塊了呢?你可以通過如下代碼在 Vue 組件中做這項(xiàng)測試:
//src\App.vue import fs from "fs"; import { ipcRenderer } from "electron"; import { onMounted } from "vue"; onMounted(() => { console.log(fs.writeFileSync); console.log(ipcRenderer); });
不出意外的話,開發(fā)者調(diào)試工具將會(huì)輸出如下內(nèi)容:
總結(jié)
現(xiàn)在我們邁出了萬里長征的第一步,構(gòu)建好了 Vue3+Vite3+Electron 的開發(fā)環(huán)境 ,而且完成這項(xiàng)工作并不依賴于市面上任何一個(gè)現(xiàn)成的構(gòu)建工具,這個(gè)開發(fā)環(huán)境是我們自己動(dòng)手一點(diǎn)一點(diǎn)搭起來的,以后我們想增加或者修改一項(xiàng)功能,都可以很從容地自己動(dòng)手處理。
非但如此,我們還通過本講內(nèi)容向你介紹了 Vite 插件的開發(fā)技巧和如何創(chuàng)建一個(gè)簡單的 Electron 應(yīng)用等知識(shí)。下一講我們將在本節(jié)課的基礎(chǔ)上,進(jìn)一步介紹如何使用 Vite 插件制作 Electron 應(yīng)用的安裝包。
到此這篇關(guān)于如何開發(fā)Vite3插件構(gòu)建Electron開發(fā)環(huán)境的文章就介紹到這了,更多相關(guān)Vite3插件構(gòu)建Electron開發(fā)環(huán)境內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring boot 和Vue開發(fā)中CORS跨域問題解決
這篇文章主要介紹了Spring boot 和Vue開發(fā)中CORS跨域問題解決,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-09-09VUE+Java實(shí)現(xiàn)評論回復(fù)功能
這篇文章主要為大家詳細(xì)介紹了VUE+Java實(shí)現(xiàn)評論回復(fù)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04mockjs+vue頁面直接展示數(shù)據(jù)的方法
這篇文章主要介紹了mockjs+vue頁面直接展示數(shù)據(jù)的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-12-12Vue 開發(fā)必須知道的36個(gè)技巧(小結(jié))
這篇文章主要介紹了Vue 開發(fā)必須知道的36個(gè)技巧(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10vuejs2.0運(yùn)用原生js實(shí)現(xiàn)簡單拖拽元素功能
這篇文章主要為大家詳細(xì)介紹了vuejs2.0運(yùn)用原生js實(shí)現(xiàn)簡單拖拽元素功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12如何使用crypto-js對文件上傳下載進(jìn)行加密處理
這篇文章主要介紹了如何使用crypto-js對文件上傳下載進(jìn)行加密處理方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05