有點久沒用 Electron 開發 PC app ,協助同事從旁實驗一下新的架構,這次研究後,直接用 electron-vite 初始化專案,核心就是 Electron + Vite 的研發環境,前台是 Vue 前端畫面。原先在想是不是一律自行安裝套件且故意都裝最新版,想完後還是縮一下,回歸別人整理好的工具,而這次強調用 Vite 整合,就不從 electron-vue 開始。
開發環境:
% sw_versProductName: macOSProductVersion: 15.2BuildVersion: 24C101% nvm use --ltsNow using node v22.12.0 (npm v10.9.0)
流水帳:
```% npm create electron-vite@latestNeed to install the following packages:create-electron-vite@0.7.1Ok to proceed? (y) y> npx> create-electron-vite✔ Project name: … my-electron-app✔ Project template: › VueScaffolding project in /private/tmp/my-electron-app...Done. Now run:cd my-electron-appnpm installnpm run devnpm noticenpm notice New major version of npm available! 10.9.0 -> 11.0.0npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.0.0npm notice To update run: npm install -g npm@11.0.0npm notice% cd my-electron-appmy-electron-app % npm install express multicast-dns axiosmy-electron-app % cat package.json{"name": "my-electron-app","private": true,"version": "0.0.0","type": "module","scripts": {"dev": "vite","build": "vue-tsc && vite build && electron-builder","preview": "vite preview"},"dependencies": {"axios": "^1.7.9","express": "^4.21.2","multicast-dns": "^7.2.5","vue": "^3.4.21"},"devDependencies": {"@vitejs/plugin-vue": "^5.0.4","electron": "^30.0.1","electron-builder": "^24.13.3","typescript": "^5.2.2","vite": "^5.1.6","vite-plugin-electron": "^0.28.6","vite-plugin-electron-renderer": "^0.14.5","vue-tsc": "^2.0.26"},"main": "dist-electron/main.js"}my-electron-app % npm run dev```
如此就有了一個畫面,而目錄結構:
```% tree -I 'node_modules|dist|build'.├── README.md├── dist-electron│ ├── main.js│ └── preload.mjs├── electron│ ├── electron-env.d.ts│ ├── main.ts│ └── preload.ts├── electron-builder.json5├── index.html├── package-lock.json├── package.json├── public│ ├── electron-vite.animate.svg│ ├── electron-vite.svg│ └── vite.svg├── src│ ├── App.vue│ ├── assets│ │ └── vue.svg│ ├── components│ │ └── HelloWorld.vue│ ├── main.ts│ ├── style.css│ └── vite-env.d.ts├── tsconfig.json├── tsconfig.node.json└── vite.config.ts7 directories, 22 files```
接著,要來擴充一下功能,讓這支程式可以使用 mDNS 協定偵測環境,這功能需實作在 Electron 架構上,在 main.ts 添加裝置搜尋:
```$ cat electron/main.ts...import mdns from 'multicast-dns'...// 使用一個全域變數紀錄偵測到的 mDNS 裝置,並記錄他初次出現的時間,也紀錄最後一次看到的時間const mdnsDevices: { [key: string]: { name: string, type: string, firstSeen: number, lastSeen: number, data: any[], answers: any[] } } = {}function startMdnsQuery() {const mdnsInstance = mdns()console.log('mDNS Query Start...')// 修改 name & type// dns-sd -B _services._dns-sd._udp localmdnsInstance.query({questions: [{//name: '_http._tcp.local',name: '_services._dns-sd._udp.local',type: 'PTR'}]})mdnsInstance.on('response', (response: { answers: string | any[] }) => {console.log('mDNS Response:', response)if (win && response.answers.length > 0) {for (const answer of response.answers) {try {// 建立一個 unique key 來代表一個裝置const key = `${answer.name}-${answer.type}`if (!mdnsDevices[key]) {mdnsDevices[key] = {name: answer.name,type: answer.type,data: [answer.data],firstSeen: Date.now(),lastSeen: Date.now(),answers: [answer]}console.log('ADD Device:')console.log(mdnsDevices[key])} else {mdnsDevices[key].lastSeen = Date.now()mdnsDevices[key].answers.push(answer)if (!mdnsDevices[key].data.includes(answer.data)) {mdnsDevices[key].data.push(answer.data)}}win.webContents.send('mdns-found', mdnsDevices)} catch (error) {console.error(error)}}}})}...app.whenReady().then(() => {createWindow()startMdnsQuery()})```
在 preload.ts 增加資料回傳到前端網頁上:
```$ cat electron/preload.ts...contextBridge.exposeInMainWorld('mdnsAPI', {onFound: (callback: (devices: any[]) => void) => {ipcRenderer.on('mdns-found', (_event, devices) => {callback(devices)})}})```
App.vue:
```<script setup lang="ts">import HelloWorld from './components/HelloWorld.vue'import { ref, onMounted } from 'vue'const devices = ref<any[]>([])onMounted(() => {// 若在 preload 中已暴露 mdnsAPIif (window.mdnsAPI && typeof window.mdnsAPI.onFound === 'function') {window.mdnsAPI.onFound((foundDevices) => {// 將接收到的裝置資料放入響應式變數devices.value = foundDevicesconsole.log('Found devices:')console.log(foundDevices)})}})</script><template><div><a href="https://electron-vite.github.io" target="_blank"><img src="/electron-vite.svg" class="logo" alt="Vite logo" /></a><a href="https://vuejs.org/" target="_blank"><img src="./assets/vue.svg" class="logo vue" alt="Vue logo" /></a></div><!--<HelloWorld msg="Vite + Vue" /><hr />--><div><h3>已搜尋到的裝置:</h3><ul id="deviceList"><li v-for="(device, index) in devices" :key="index">[{{ new Date(device.firstSeen).toLocaleString('zh-TW', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' }) }}] {{ device.name }} ({{ device.type }})<ul><li v-for="(data, i) in device.data" :key="I">{{ data }}</li></ul></li></ul></div></template><style scoped>.logo {height: 6em;padding: 1.5em;will-change: filter;transition: filter 300ms;}.logo:hover {filter: drop-shadow(0 0 2em #646cffaa);}.logo.vue:hover {filter: drop-shadow(0 0 2em #42b883aa);}#deviceList {text-align: left;list-style-type: none;padding: 0;}</style>```
收工 ,有空再來做些什麼應用吧:github.com/changyy/study-my-electron-app
沒有留言:
張貼留言