顯示具有 Electron 標籤的文章。 顯示所有文章
顯示具有 Electron 標籤的文章。 顯示所有文章

2024年12月17日 星期二

Node.js 開發筆記 - 使用 Electron + Vite + Vue 開發一款 PC app ,可支援 mDNS 裝置搜尋 @ macOS 15.2



有點久沒用 Electron 開發 PC app ,協助同事從旁實驗一下新的架構,這次研究後,直接用 electron-vite 初始化專案,核心就是 Electron + Vite 的研發環境,前台是 Vue 前端畫面。原先在想是不是一律自行安裝套件且故意都裝最新版,想完後還是縮一下,回歸別人整理好的工具,而這次強調用 Vite 整合,就不從 electron-vue 開始。

開發環境:

% sw_vers 
ProductName: macOS
ProductVersion: 15.2
BuildVersion: 24C101
% nvm use --lts         
Now using node v22.12.0 (npm v10.9.0)

流水帳:

```
% npm create electron-vite@latest
Need to install the following packages:
create-electron-vite@0.7.1
Ok to proceed? (y) y


> npx
> create-electron-vite

✔ Project name: … my-electron-app
✔ Project template: › Vue

Scaffolding project in /private/tmp/my-electron-app...

Done. Now run:

  cd my-electron-app
  npm install
  npm run dev

npm notice
npm notice New major version of npm available! 10.9.0 -> 11.0.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.0.0
npm notice To update run: npm install -g npm@11.0.0
npm notice

% cd my-electron-app
my-electron-app % npm install express multicast-dns axios
my-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.ts

7 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 local
  mdnsInstance.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 中已暴露 mdnsAPI
  if (window.mdnsAPI && typeof window.mdnsAPI.onFound === 'function') {
    window.mdnsAPI.onFound((foundDevices) => {
      // 將接收到的裝置資料放入響應式變數
      devices.value = foundDevices
      console.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

2024年1月23日 星期二

Javascript 開發筆記 - 盡可能使用 npm 指令處理套件安全更新,此例升級 electron 和 electron-builder



之前寫的練習擺在 github 一直收到套件安全的升級通知信(這只是其中一條),以前在處理這個時,都是人工改掉 package.json ,這次來用指令試試:

% git clone https://github.com/changyy/node-electron-based
% cd node-electron-based
% source env_nvm.sh 
Now using node v16.20.2 (npm v8.19.4)
% npm install
npm WARN deprecated asar@3.2.0: Please use @electron/asar moving forward.  There is no API change, just a package name change
npm WARN deprecated electron-osx-sign@0.6.0: Please use @electron/osx-sign moving forward. Be aware the API is slightly different

added 557 packages, and audited 558 packages in 10s

69 packages are looking for funding
  run `npm fund` for details

11 vulnerabilities (4 moderate, 7 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

接著就如上述所言,用 npm audit fix 查看一下:

% npm audit fix

up to date, audited 558 packages in 860ms

69 packages are looking for funding
  run `npm fund` for details

# npm audit report

electron  <=22.3.24
Severity: high
Depends on vulnerable versions of @electron/get
Electron vulnerable to out-of-package code execution when launched with arbitrary cwd - https://github.com/advisories/GHSA-7x97-j373-85x5
Electron context isolation bypass via nested unserializable return value - https://github.com/advisories/GHSA-p7v2-p9m8-qqg7
Electron affected by libvpx's heap buffer overflow in vp8 encoding - https://github.com/advisories/GHSA-qqvq-6xgj-jw8g
ASAR Integrity bypass via filetype confusion in electron - https://github.com/advisories/GHSA-7m48-wc93-9g85
fix available via `npm audit fix --force`
Will install electron@28.1.4, which is a breaking change
node_modules/electron

got  <11.8.5
Severity: moderate
Got allows a redirect to a UNIX socket - https://github.com/advisories/GHSA-pfrx-2q88-qq97
fix available via `npm audit fix --force`
Will install electron@28.1.4, which is a breaking change
node_modules/got
  @electron/get  <=1.14.1
  Depends on vulnerable versions of got
  node_modules/@electron/get

minimatch  <3.0.5
Severity: high
minimatch ReDoS vulnerability - https://github.com/advisories/GHSA-f8q6-p94x-37v3
fix available via `npm audit fix`
node_modules/dir-compare/node_modules/minimatch
  dir-compare  <=2.4.0
  Depends on vulnerable versions of minimatch
  node_modules/dir-compare
    @electron/universal  1.0.1 - 1.3.3
    Depends on vulnerable versions of dir-compare
    node_modules/@electron/universal
      app-builder-lib  22.10.4 - 24.0.0-alpha.13
      Depends on vulnerable versions of @electron/universal
      node_modules/app-builder-lib
        dmg-builder  22.10.4 - 24.0.0-alpha.13
        Depends on vulnerable versions of app-builder-lib
        node_modules/dmg-builder
          electron-builder  19.25.0 || 22.10.4 - 24.6.0
          Depends on vulnerable versions of app-builder-lib
          Depends on vulnerable versions of dmg-builder
          Depends on vulnerable versions of simple-update-notifier
          node_modules/electron-builder

semver  7.0.0 - 7.5.1
Severity: moderate
semver vulnerable to Regular Expression Denial of Service - https://github.com/advisories/GHSA-c2qf-rxjj-qqgw
fix available via `npm audit fix`
node_modules/simple-update-notifier/node_modules/semver
  simple-update-notifier  1.0.7 - 1.1.0
  Depends on vulnerable versions of semver
  node_modules/simple-update-notifier

11 vulnerabilities (4 moderate, 7 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

使用 npm audit fix --force 處理:

% npm audit fix --force
npm WARN using --force Recommended protections disabled.
npm WARN audit Updating electron to 28.1.4, which is a SemVer major change.

added 10 packages, removed 13 packages, changed 15 packages, and audited 555 packages in 4s

76 packages are looking for funding
  run `npm fund` for details

# npm audit report

minimatch  <3.0.5
Severity: high
minimatch ReDoS vulnerability - https://github.com/advisories/GHSA-f8q6-p94x-37v3
fix available via `npm audit fix`
node_modules/dir-compare/node_modules/minimatch
  dir-compare  <=2.4.0
  Depends on vulnerable versions of minimatch
  node_modules/dir-compare
    @electron/universal  1.0.1 - 1.3.3
    Depends on vulnerable versions of dir-compare
    node_modules/@electron/universal
      app-builder-lib  22.10.4 - 24.0.0-alpha.13
      Depends on vulnerable versions of @electron/universal
      node_modules/app-builder-lib
        dmg-builder  22.10.4 - 24.0.0-alpha.13
        Depends on vulnerable versions of app-builder-lib
        node_modules/dmg-builder
          electron-builder  19.25.0 || 22.10.4 - 24.6.0
          Depends on vulnerable versions of app-builder-lib
          Depends on vulnerable versions of dmg-builder
          Depends on vulnerable versions of simple-update-notifier
          node_modules/electron-builder

semver  7.0.0 - 7.5.1
Severity: moderate
semver vulnerable to Regular Expression Denial of Service - https://github.com/advisories/GHSA-c2qf-rxjj-qqgw
fix available via `npm audit fix`
node_modules/simple-update-notifier/node_modules/semver
  simple-update-notifier  1.0.7 - 1.1.0
  Depends on vulnerable versions of semver
  node_modules/simple-update-notifier

8 vulnerabilities (2 moderate, 6 high)

To address all issues, run:
  npm audit fix

% git diff
diff --git a/package.json b/package.json
index f470e2b..4b7ac3d 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,7 @@
     "concurrently": "^7.4.0",
     "copy-webpack-plugin": "^11.0.0",
     "cross-env": "^7.0.3",
-    "electron": "^20.1.1",
+    "electron": "^28.1.4",
     "electron-builder": "^23.3.3",
     "webpack-cli": "^4.10.0",
     "webpack-dev-server": "^4.11.0"

這樣應當算有處理到一個項目的更新,最後再看看之前的 build code 是否正常

% npm install
% npm run build

> simple-electron-app@1.0.0 build
> webpack-cli && electron-builder -mwl

assets by path ../../ 2.47 KiB
  assets by path ../../renderer/*.js 958 bytes
    asset ../../renderer/mainRenederer.js 910 bytes [emitted] [from: src/renderer/mainRenederer.js] [copied] [minimized]
    asset ../../renderer/index.js 48 bytes [emitted] [from: src/renderer/index.js] [copied] [minimized]
  asset ../../main/index.js 1.26 KiB [emitted] [from: src/main/index.js] [copied] [minimized]
  asset ../../preload/mainRenderer.js 289 bytes [emitted] [from: src/preload/mainRenderer.js] [copied] [minimized]
asset index.html 495 bytes [emitted] [from: src/html/mainRenderer/index.html] [copied]
asset index.js 253 bytes [emitted] [minimized] (name: mainRendererHTML)
./src/html/mainRenderer/index.js 380 bytes [built] [code generated]

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value.
Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/

webpack 5.89.0 compiled with 1 warning in 146 ms
  • electron-builder  version=23.6.0 os=23.2.0
  • description is missed in the package.json  appPackageFile=/Volumes/Data/UserData/tmp/node-electron-based/package.json
  • writing effective config  file=dist/builder-effective-config.yaml
  • packaging       platform=darwin arch=arm64 electron=28.1.4 appOutDir=dist/mac-arm64
  • downloading     url=https://github.com/electron/electron/releases/download/v28.1.4/electron-v28.1.4-darwin-arm64.zip size=95 MB parts=8
  • downloaded      url=https://github.com/electron/electron/releases/download/v28.1.4/electron-v28.1.4-darwin-arm64.zip duration=36.971s
  • default Electron icon is used  reason=application icon is not set
  • skipped macOS application code signing  reason=cannot find valid "Developer ID Application" identity or custom non-Apple code signing certificate, it could cause some undefined behaviour, e.g. macOS localized description not visible, see https://electron.build/code-signing allIdentities=     0 identities found
                                                Valid identities only
     0 valid identities found
  • building        target=macOS zip arch=arm64 file=dist/simple-electron-app-1.0.0-arm64-mac.zip
  • building        target=DMG arch=arm64 file=dist/simple-electron-app-1.0.0-arm64.dmg
  • Detected arm64 process, HFS+ is unavailable. Creating dmg with APFS - supports Mac OSX 10.12+
  • packaging       platform=linux arch=x64 electron=28.1.4 appOutDir=dist/linux-unpacked
  • downloading     url=https://github.com/electron/electron/releases/download/v28.1.4/electron-v28.1.4-linux-x64.zip size=102 MB parts=8
  • building block map  blockMapFile=dist/simple-electron-app-1.0.0-arm64.dmg.blockmap
  • building block map  blockMapFile=dist/simple-electron-app-1.0.0-arm64-mac.zip.blockmap
  • downloaded      url=https://github.com/electron/electron/releases/download/v28.1.4/electron-v28.1.4-linux-x64.zip duration=30.749s
  • building        target=snap arch=x64 file=dist/simple-electron-app_1.0.0_amd64.snap
  • building        target=AppImage arch=x64 file=dist/simple-electron-app-1.0.0.AppImage
  • application Linux category is set to default "Utility"  reason=linux.category is not set and cannot map from macOS docs=https://www.electron.build/configuration/linux
  • default Electron icon is used  reason=application icon is not set
  • application Linux category is set to default "Utility"  reason=linux.category is not set and cannot map from macOS docs=https://www.electron.build/configuration/linux
  • packaging       platform=win32 arch=arm64 electron=28.1.4 appOutDir=dist/win-arm64-unpacked
  • downloading     url=https://github.com/electron/electron/releases/download/v28.1.4/electron-v28.1.4-win32-arm64.zip size=108 MB parts=8
  • downloading     url=https://github.com/electron-userland/electron-builder-binaries/releases/download/snap-template-4.0-2/snap-template-electron-4.0-2-amd64.tar.7z size=1.5 MB parts=1
  • downloading     url=https://github.com/electron-userland/electron-builder-binaries/releases/download/appimage-12.0.1/appimage-12.0.1.7z size=1.6 MB parts=1
  • downloaded      url=https://github.com/electron-userland/electron-builder-binaries/releases/download/appimage-12.0.1/appimage-12.0.1.7z duration=5.225s
  • downloaded      url=https://github.com/electron-userland/electron-builder-binaries/releases/download/snap-template-4.0-2/snap-template-electron-4.0-2-amd64.tar.7z duration=5.425s
  • downloaded      url=https://github.com/electron/electron/releases/download/v28.1.4/electron-v28.1.4-win32-arm64.zip duration=42.26s
  • default Electron icon is used  reason=application icon is not set
  • downloading     url=https://github.com/electron-userland/electron-builder-binaries/releases/download/winCodeSign-2.6.0/winCodeSign-2.6.0.7z size=5.6 MB parts=1
  • downloaded      url=https://github.com/electron-userland/electron-builder-binaries/releases/download/winCodeSign-2.6.0/winCodeSign-2.6.0.7z duration=4.001s
  • downloading     url=https://github.com/electron-userland/electron-builder-binaries/releases/download/wine-4.0.1-mac/wine-4.0.1-mac.7z size=19 MB parts=3
  • downloaded      url=https://github.com/electron-userland/electron-builder-binaries/releases/download/wine-4.0.1-mac/wine-4.0.1-mac.7z duration=13.249s
  • building        target=nsis file=dist/simple-electron-app Setup 1.0.0.exe archs=arm64 oneClick=true perMachine=false
  • downloading     url=https://github.com/electron-userland/electron-builder-binaries/releases/download/nsis-3.0.4.1/nsis-3.0.4.1.7z size=1.3 MB parts=1
  • downloaded      url=https://github.com/electron-userland/electron-builder-binaries/releases/download/nsis-3.0.4.1/nsis-3.0.4.1.7z duration=5.637s
  • downloading     url=https://github.com/electron-userland/electron-builder-binaries/releases/download/nsis-resources-3.4.1/nsis-resources-3.4.1.7z size=731 kB parts=1
  • downloaded      url=https://github.com/electron-userland/electron-builder-binaries/releases/download/nsis-resources-3.4.1/nsis-resources-3.4.1.7z duration=4.013s
  • building block map  blockMapFile=dist/simple-electron-app Setup 1.0.0.exe.blockmap

編譯正常,接下來再繼續處理 

% npm audit fix

up to date, audited 555 packages in 730ms

76 packages are looking for funding
  run `npm fund` for details

# npm audit report

minimatch  <3.0.5
Severity: high
minimatch ReDoS vulnerability - https://github.com/advisories/GHSA-f8q6-p94x-37v3
fix available via `npm audit fix`
node_modules/dir-compare/node_modules/minimatch
  dir-compare  <=2.4.0
  Depends on vulnerable versions of minimatch
  node_modules/dir-compare
    @electron/universal  1.0.1 - 1.3.3
    Depends on vulnerable versions of dir-compare
    node_modules/@electron/universal
      app-builder-lib  22.10.4 - 24.0.0-alpha.13
      Depends on vulnerable versions of @electron/universal
      node_modules/app-builder-lib
        dmg-builder  22.10.4 - 24.0.0-alpha.13
        Depends on vulnerable versions of app-builder-lib
        node_modules/dmg-builder
          electron-builder  19.25.0 || 22.10.4 - 24.6.0
          Depends on vulnerable versions of app-builder-lib
          Depends on vulnerable versions of dmg-builder
          Depends on vulnerable versions of simple-update-notifier
          node_modules/electron-builder

semver  7.0.0 - 7.5.1
Severity: moderate
semver vulnerable to Regular Expression Denial of Service - https://github.com/advisories/GHSA-c2qf-rxjj-qqgw
fix available via `npm audit fix`
node_modules/simple-update-notifier/node_modules/semver
  simple-update-notifier  1.0.7 - 1.1.0
  Depends on vulnerable versions of semver
  node_modules/simple-update-notifier

8 vulnerabilities (2 moderate, 6 high)

To address all issues, run:
  npm audit fix

故事大概起源於 electron-builder ,只好先把他升級看看

% cat package.json | jq '.devDependencies["electron-builder"]'
"^23.3.3"
% npm update --dev electron-builder
% cat package.json | jq '.devDependencies["electron-builder"]'
"^23.3.3"

看來只好靠這招了:

% npm install electron-builder@latest --save-dev
% cat package.json | jq '.devDependencies["electron-builder"]'
"^24.9.1"

如此也確認都沒事了:

% npm audit
found 0 vulnerabilities

最終,其實也只是改 package.json 兩行 XD

% git diff package.json 
diff --git a/package.json b/package.json
index f470e2b..4c830f1 100644
--- a/package.json
+++ b/package.json
@@ -19,8 +19,8 @@
     "concurrently": "^7.4.0",
     "copy-webpack-plugin": "^11.0.0",
     "cross-env": "^7.0.3",
-    "electron": "^20.1.1",
-    "electron-builder": "^23.3.3",
+    "electron": "^28.1.4",
+    "electron-builder": "^24.9.1",
     "webpack-cli": "^4.10.0",
     "webpack-dev-server": "^4.11.0"
   }

2022年9月19日 星期一

Electron 開發筆記 - 使用 Vue.js 3 與 Vite 和最新 Electron Stable Release (20.x.x)

週末繼續把玩一下 Electron framework 也複習 Vue.js ,一直在想要怎樣兜再一起比較方便,類似的整合一直都有:
等等,後來自己想了一下,乾脆練習配置吧!自己先規劃想要的架構:
  1. 盡量把高變動的 code 都擺在 src 目錄中
  2. 盡量讓 vue.js code 可以統一在某個目錄中
  3. 直接使用最新的 Vue.js 及其最新工具
  4. 使用最新的 Electron Stable Release
  5. 盡量簡單,甚至不用 TypeScript ,讓其他新手想跨入也方便
就這樣,採用了 Electron 20.0.0 (2022.08) 和 Vue.js + Vite 環境

於是乎,先摸索了一個出來:github.com/changyy/node-electron-vue-vite

% tree src 
src
├── html
│   └── mainRenderer
│       ├── index.html
│       ├── src
│       │   ├── App.vue
│       │   ├── components
│       │   │   └── HelloWorld.vue
│       │   └── main.js
│       └── vite.config.js
├── main
│   └── index.js
├── preload
│   └── mainRenderer.js
└── renderer
    ├── index.js
    └── mainRenederer.js

7 directories, 9 files

之後再來想想看有什麼題目可以把玩吧!

2022年9月1日 星期四

Electron 開發筆記 - 使用 @electron-forge/plugin-webpack

評估 electron 到底要跟哪套網頁開發的流程合在一起用,先研究一下 @electron-forge/plugin-webpack,粗略整合還堪用,但還有一些路徑或程式碼架構要處理。

先規劃了 src 目錄:

  % tree src 
  src
  ├── html
  │   └── mainRenderer
  │       ├── index.html
  │       └── index.js
  ├── main
  │   └── index.js
  ├── preload
  │   └── mainRenderer.js
  └── renderer
      ├── index.js
      └── mainRenederer.js
  
  5 directories, 6 files

其中 html 就是預期每個 renderer 的網頁所在地;而 main/index.js 就是 Electron main process 處理的項目;而 renderer/mainRenederer.js 是預期在 Electron main process 中處理 createWindow 的項目抽取出來重複使用;而 preload/mainRenderer.js 就是 Electron Renderer 展現前的 preload script

如此,想讓純前端網頁的程式碼都可以賴在 html 目錄下,屆時要靠常見工具封裝成 SPA 也方便。

而使用 electron-forge/plugin-webpack 上,只需要留意二件事:

1. 在 package.json 描述要被 webpack 封裝的項目

% cat package.json | jq '.config'
{
  "forge": {
    "plugins": [
      [
        "@electron-forge/plugin-webpack",
        {
          "mainConfig": "./webpack.main.config.js",
          "renderer": {
            "config": "./webpack.renderer.config.js",
            "entryPoints": [
              {
                "name": "main_window",
                "html": "./src/html/mainRenderer/index.html",
                "js": "./src/html/mainRenderer/index.js",
                "preload": {
                  "js": "./src/preload/mainRenderer.js"
                }
              }
            ]
          }
        }
      ]
    ]
  }
}

2. 在 Electron main process 中,使用 createWindow 後要讀取的 index.html 跟 preload.js 要改用變數

MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY, MAIN_WINDOW_WEBPACK_ENTRY

目前還會卡到 webpack dev server 運行時,有些 js code 執行異常,正考慮不要靠 webpack dev server,可能就要正式離開 @electron-forge/plugin-webpack" 用法了

2022年8月30日 星期二

Electron 開發筆記 - 初探 ElectronJS 框架開發 PC app

約莫一年前團隊同事接了個用 ElectronJS 開發的程式,並且依照其框架成果再做出另一個 PC app ,最近播點時間來研究一下 ElectronJS 。幾個月前接觸 Golang 時,也曾想過用 Golang + Electron 練習一下 PC app ,只是整體上運作起來還是太卡了點。

整體上,要認識 ElectronJS 建議都先看一次官方文件 www.electronjs.org/docs/latest/ ,基本上寫的精簡不錯,看完一輪 Processes in Electron 就會學會:
  • Electron 的流程架構 (Process Model),可以分成 main process (node.js / main.js) 和 renderer process (BrowserWindow / renderer.js ),並且透過 preload.js 串起 main process 跟 renderer process 溝通橋樑
  • preload.js 是在 renderer process 執行前運行的,等於進入到 renderer process 前,可以打造一些溝通環境
  • 資安領域上,如何讓 renderer process 的權限不要太大 (Context Isolation)
  • 關於 main process 和 renderer process 溝通時,該怎樣進行 (ipcMain, ipcRenderer),可以分成 Renderer to Main 單向溝通、Renderer to Main 雙向溝通、Main to Renderer 溝通等
  • 看一眼 Process Sandboxing 情況,再多了解一下 MessagePorts 用法 (像 window.postMessage 之 iframe 溝通)
如此,就差不多可以來用 Electron 開發 PC app,然而有更多眉眉角角躲在 package.json 中以及一堆神秘的指令,如 electron-forge 等等。

我想,開發 Electron 的複雜度,源自於整個專案進行會跨非常多領域(指令)吧,每個混再一起就會覺得爆炸大,每個分離看待就會舒適許多。

像我自己也弄了:
  • env_nvm.sh:靠 nvm 切換 node.js 版本
  • env_vim.sh:靠 .vimrc 和 .vimrc_coc.nvim 完成簡易 coding style 和 auto-complete 設置
  • 把上述包裝在 init_env.sh 運行,每次開發就先跑一下 source init_env.sh 就行了
最後,對於 Electron PC app 開發,後續也都還會再做一些程式碼封裝方式(webpack等)和程式碼混淆等,當然,若還有開發框架 vue.js, react 等,也都還有對應的任務,所以連續動作還是很多的,之後繼續筆記一下。

2022年7月30日 星期六

Go 開發筆記 - 透過 Electron 實現 PC app GUI 之 go-astilectron / go-astilectron-bundler / go-astilectron-bootstrap


前陣子練習 Golang 後,除了建制後端 API 及網頁服務外,在想該怎樣做個 Windows / macOS 的 PC App 時,逛了一下熱門的 GUI 套件,有常見的 QT, GTK3/GTK4 等等,最後想起 Electron 這套,找了一下果真也有人串好 Go 與 Electron 整合方式。就來試試看 go-astilectron 這套吧!

處理的項目:
專案初始化:

% cat main.go 
package main

import (
    "github.com/asticode/go-astilectron"
)

func main() {

}

% go mod init github.com/changyy/study-go-electron
% go mod tidy

接著切一個目錄 web 做網頁開發管理:

% tree -L 1 web
web
├── README.md
├── dist
├── env_nvm.sh
├── env_vim.sh
├── node_modules
├── package-lock.json
├── package.json
├── src
├── webpack.common.js
├── webpack.development.js
└── webpack.production.js

3 directories, 8 files

其中 package.json 內使用 webpack 安置開發模式和發版封裝、使用 eslint 做 coding style 規範:

% cat web/package.json | jq '.scripts'
{
  "eslint": "eslint --ext .js src/",
  "eslint-fix": "eslint --fix --ext .js src/",
  "watch": "webpack --config webpack.development.js --watch",
  "start": "webpack serve --config webpack.development.js --open",
  "build": "webpack --config webpack.production.js"
}

而 web 程式碼入口點:

% tree web/src     
web/src
├── index.html
└── js
    ├── helper-old-style.js
    ├── helper.js
    └── index.js

1 directory, 4 files

試試看在 web/src/index.html 內,在 body.onload 內呼叫 hehe(),而 web/src/js/index.js 是一個統整區,而 webpack 在 production mode 會精簡程式碼,因此要匯出的功能都必須做點處理,像是故意埋在 windows.xx 等等,或是要調整 webpack 包裝的機制,如 output 區要多描述 libraryTarget 和 library 資訊,會自動綁定在 window.xx 環境。

回過頭來,關於 go astilectron 的使用,在不需要封裝成各平台的環境時,可以很簡單地用以下程式碼,並在根目錄用 `% go run .` 就可以運行了:

    a, _ := astilectron.New(log.New(os.Stderr, "", 0), astilectron.Options{
        AppName: "MyGoAstilectronProject",
    })
    defer a.Close()

    // Start astilectron
    a.Start()

    w, _ := a.NewWindow("./resources/app/index.html", &astilectron.WindowOptions{
        Center: astikit.BoolPtr(true),
        Height: astikit.IntPtr(600),
        Width:  astikit.IntPtr(600),
    })
    w.Create()

    // Blocking pattern
    a.Wait()

如此,在開發網頁版型時,單純在 web 目錄做事,如 npm run start 就可以喚起瀏覽器來瀏覽,等做完事後,在靠 npm run build 封裝到 web/dist 目錄以及複製一份到 resource/app 目錄中,後續就著靠 ~/go/bin/astilectron-bundler 封裝成 PC App,在封裝成 PC app 時,會面臨到 resources 目錄內的文件也得跟著封裝進去,也就是上述程式碼指定的 "./resources/app/index.html" 網頁資源,或者網頁位址要改成絕對路徑也是一招,但等同也要求使用此 PC app 者,下需要自行配置好網頁資料。

這時,就要使用 go-astilectron-bundler 跟 go-astilectron-bootstrap ,前者是編譯出 PC app ,後者是封裝好資源,使得執行 PC app 時,可以順道把對應的資源("./resources/app/")擺定位,這時程式碼就會變得稍微複雜一點,並且執行執行 `go run .` 不見得能常運行,將使得開發週期拉的很長(每次編譯要花幾分鐘的時間)

% ~/go/bin/astilectron-bundler cc
% time ~/go/bin/astilectron-bundler
...
~/go/bin/astilectron-bundler  63.79s user 10.56s system 108% cpu 1:08.21 total

最後,就把他配置成可以傳一個 -dev 參數,使用該參數時,不經過 ~/go/bin/astilectron-bundler 運行(運行的速度也只是小減 20% 時間而已 XD)

% go run . -dev

目前覺得用 Go 與 Electron framework 一起使用的好處還不夠強烈,以及整合開發的優勢頂多是用 Go 寫工具(api)等等,以及使 Electron 時,本來就是重度倚賴 HTML/JS/CSS 在 UI 呈現,因此直接 node.js 的整合優勢仍是最佳的。

更多資訊就在這邊:github.com/changyy/study-go-electron ,或是直接觀看 github.com/asticode/go-astilectron 內有提到 demo 範例,可以直接看 demo 內的實作。