Skip to content

最小示例

目录结构

  • assets
    • index.html
    • render.js
  • handle.js
  • main.js
  • preload.js

main.js 入口

javascript
const path = require('path')
const { app, BrowserWindow, ipcMain ,Menu} = require('electron');
// app.name 程序名

// 窗口相关的方法
const handle = require("./handle")

function createWindow() {
  // 创建一个浏览器窗口
  const mainWin = new BrowserWindow({
    minWidth: 850,
    minHeight: 600,
    resizable: true, // 允许窗口调整大小
    frame: false, // 禁用默认的窗口装饰 开启不能拖动
    webPreferences: {
      preload: path.join(__dirname, './preload.js'),
      nodeIntegration: true // 允许在渲染进程中使用 Node.js API
    },
    transparent: true,
  });

  const menu = Menu.buildFromTemplate([
    {
      label: "打开",
      submenu: [
        {//向渲染器(浏览器进程)发送
          click: () => mainWin.webContents.send('update-counter', 1),
          label: '按钮1'
        },
        {//向渲染器(浏览器进程)发送
          click: () => mainWin.webContents.send('update-counter', -1),
          label: '按钮2'
        }
      ]
    }
  ])

  Menu.setApplicationMenu(menu)

  // ipcMain创建监听器监听ipcRenderer发送的消息
  ipcMain.on('set-title', handle.SetTitle)//模式1同步 建议仅单向通行,也可双向 查看官网文档
  ipcMain.handle('dialog:openFile', handle.FileOpen)//模式2异步 可双向 推荐



  // 加载应用程序的主界面
  // mainWin.loadFile('assets/index.html');
  mainWin.loadURL('http://127.0.0.1:5173/')

  // 打开开发工具
  // mainWin.webContents.openDevTools()

  // 监听窗口调整大小事件
  // mainWin.on('resize', () => {
  //   const { width, height } = mainWindow.getBounds();// 获取窗口的大小
  //   mainWindow.webContents.send('window-resized', { width, height });
  // });

}


// 当 Electron 完成初始化并准备创建浏览器窗口时,调用 createWindow 函数
app.whenReady().then(() => {
  createWindow();

  // 如果没有窗口打开则打开一个窗口 (macOS)
  app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow() })
});

// 关闭所有窗口时退出应用 (Windows & Linux)
app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit() })

preload.js

javascript

const { contextBridge, ipcRenderer } = require('electron')
// 所有的 Node.js API接口 都可以在 preload 进程中被调用.
// 它拥有与Chrome扩展一样的沙盒。

// 将myApi导入到window对象中
contextBridge.exposeInMainWorld('myApi', {
    desktop: true
})
// 将electronAPI导入到window对象中
contextBridge.exposeInMainWorld('electronAPI', {
    // ipcRenderer发送消息给监听器ipcMain
    //向主程序内
    setTitle: (title) => ipcRenderer.send('set-title', title),//模式1同步
    openFile: () => ipcRenderer.invoke('dialog:openFile'),//模式2异步

    //向浏览器内
    handleCounter: (callback) => ipcRenderer.on('update-counter', callback),
})



// 通过预加载脚本从渲染器访问Node.js
// 访问 Node.js process.versions 对象,并运行一个基本的 replaceText 辅助函数将版本号插入到 HTML 文档中。
window.addEventListener('DOMContentLoaded', () => {
    const replaceText = (selector, text) => {
        const element = document.getElementById(selector)
        if (element) element.innerText = text
    }

    for (const dependency of ['chrome', 'node', 'electron']) {
        replaceText(`${dependency}-version`, process.versions[dependency])
    }
})

handle.js

javascript
const { BrowserWindow, dialog } = require('electron')
/**
 * 修改打开的窗口名字
 * @param {Window} event 
 * @param {string} title 
 */
function SetTitle(event, title) {
    const webContents = event.sender
    const win = BrowserWindow.fromWebContents(webContents)
    win.setTitle(title)
}

/**
 * 打开文件选择
 * @returns 
 */
async function FileOpen() {
    const { canceled, filePaths } = await dialog.showOpenDialog()
    if (!canceled) {
        return filePaths[0]
    }
}

module.exports = {
    SetTitle,
    FileOpen,
}

index.html 前端

javascript
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'" />
    <meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    Current value: <strong id="counter">0</strong>
    <h1>你好!</h1>
    我们正在使用 Node.js <span id="node-version"></span>,
    Chromium <span id="chrome-version"></span>,
    和 Electron <span id="electron-version"></span>.
    <br>
    Title: <input id="title"/>
    <button id="btn" type="button">Set</button>


    <button type="button" id="btn2">选择文件</button>
    文件绝对路径: <strong id="filePath"></strong>


    <script src="./renderer.js"></script>

</body>

</html>

render.js

javascript
const setButton = document.getElementById('btn')
const titleInput = document.getElementById('title')
setButton.addEventListener('click', () => {
  const title = titleInput.value
  window.electronAPI.setTitle(title)
})

const btn = document.getElementById('btn2')

btn.addEventListener('click', async () => {
  const filePath = await window.electronAPI.openFile()
  document.getElementById('filePath').innerText = filePath
})

window.electronAPI.handleCounter((event, value) => {
  const oldValue = Number(counter.innerText)
  const newValue = oldValue + value
  counter.innerText = newValue
  event.sender.send('counter-value', newValue)
})