今天开始学习NodeJs 关于 桌面应用的内容,长期目标是 React + electron 实现一个桌面应用。
今天先实现一个简单的目标,搭建一个Electron + ts 项目架构,并实现主业务线程 和前端渲染线程的交互
一、代码结构和配置
例子项目结构大致如下:

package.json
            
            
              XML
              
              
            
          
          {
  "name": "electron-ts-demo",
  "version": "1.0.0",
  "main": "dist/main.js",
  "scripts": {
    "build": "tsc && xcopy /y src\\renderer\\*.html dist\\renderer\\",
    "watch": "tsc -w",
    "start": "electron .",
    "dev": "concurrently \"npm run watch\" \"npm run start\""
  },
  "devDependencies": {
    "concurrently": "^9.2.0",
    "electron": "^37.3.0",
    "typescript": "^5.3.2"
  },
  "dependencies": {}
}
        这里用到一个concurrently 包 是实现多条命令并行执行的工具。
tsconfig.json
            
            
              XML
              
              
            
          
          {
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "sourceMap": true,
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}
        二、代码实现
preload.ts
预处理实现
            
            
              TypeScript
              
              
            
          
          import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('electronAPI', {
  sendMessage: (message: string) => ipcRenderer.send('message-from-renderer', message),
  onReply: (callback: (response: string) => void) => 
    ipcRenderer.on('message-reply', (event, response) => callback(response))
});
        实现渲染线程(页面) 发送信息到 主线程,主返回信息到渲染线程 (通过回调的形式)
main.ts
            
            
              TypeScript
              
              
            
          
          import { app, BrowserWindow, ipcMain } from 'electron';
import path from 'path';
let mainWindow: BrowserWindow;
function createWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true
    }
  });
  mainWindow.loadFile('dist/renderer/index.html');
  ipcMain.on('message-from-renderer', (event, msg: string) => {
    console.log('Received:', msg);
    event.reply('message-reply', `Main process received: ${msg}`);
  });
}
app.whenReady().then(() => {
  createWindow();
  //macBook 兼容
  app.on("activate", () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow();
    }
  });
});
app.on("window-all-closed", () => {
  if (process.platform !== "darwin") {
    app.quit();
  }
});
        上面实现了一个简单的生成一个窗口的例子。使用我们预先准备的preload.js预处理。
通过 ipcMain 监听 发生消息和返回回调。
index.html
            
            
              XML
              
              
            
          
          <!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Electron TS Demo</title>
    <style>
      body {
        font-family: Arial;
        padding: 20px;
      }
      button {
        padding: 8px 16px;
      }
    </style>
  </head>
  <body>
    <h1>Electron + TypeScript Demo</h1>
    <button id="sendBtn">Send Message</button>
    <div id="response"></div>
    <script src="renderer.js"></script>
  </body>
</html>
        renderer.ts
            
            
              TypeScript
              
              
            
          
          const sendBtn = document.getElementById('sendBtn')!;
const responseDiv = document.getElementById('response')!;
sendBtn.addEventListener('click', () => {
  console.log("sendBtn Click");
  window?.electronAPI.sendMessage('Hello from TypeScript');
});
window?.electronAPI.onReply((response: string) => {
  console.log(response);
  responseDiv.textContent = response;
});
        这里添加监听 给主线程发送消息,通过 window.electronAPI 预处理暴露到主页面的对象设置监听方法监听主线程 返回消息。
三、效果

