Node Electron 开发快速入门指南

84 阅读5分钟

一、环境准备

1.1 Node.js 版本选择

推荐版本:Node.js 18.x 或 20.x LTS(长期支持版本)

重要说明:Electron 内置 Node.js 与系统 Node.js 的关系

  1. 系统 Node.js(开发时用)

    • 用于运行 npm 命令、安装依赖、执行打包工具
    • 用于开发调试、运行构建脚本
    • 这就是你电脑上安装的 Node.js
  2. Electron 内置 Node.js(运行时用)

    • Electron 内部集成了特定版本的 Node.js
    • 应用运行时使用的是 Electron 内置的 Node.js,不是系统的 Node.js
    • 可通过 process.versions.node 查看 Electron 内置的 Node 版本
    • 不同 Electron 版本内置的 Node.js 版本不同
  3. 为什么两个都需要?

    • 系统 Node.js:开发阶段使用(npm、构建、打包)
    • 内置 Node.js:应用运行时使用(用户电脑无需安装 Node.js)

示例:

# 系统 Node 版本
$ node -v
v20.10.0

# Electron 应用运行时在控制台输出
> console.log(process.versions.node)
18.17.1  # Electron 内置的 Node 版本(可能与系统不同)

安装方式:

  • 官网下载:nodejs.org/
  • nvm 管理多版本:
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
    nvm install 20
    nvm use 20
    

二、项目初始化

2.1 创建项目

mkdir my-electron-app
cd my-electron-app
npm init -y

2.2 安装核心依赖(必需)

# Electron 运行时(开发依赖)
npm install electron --save-dev

# 打包工具(开发依赖)
npm install electron-builder --save-dev

国内镜像加速(可选):

# 创建 .npmrc 文件
echo "electron_mirror=https://npmmirror.com/mirrors/electron/" >> .npmrc
echo "registry=https://registry.npmmirror.com" >> .npmrc

三、创建项目文件

3.1 项目结构

my-electron-app/
├── package.json       # 项目配置
├── main.js           # 主进程入口
├── preload.js        # 预加载脚本
└── index.html        # 渲染进程页面

3.2 主进程文件 main.js

const { app, BrowserWindow } = require('electron');
const path = require('path');

// 创建窗口
function createWindow() {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      nodeIntegration: false,    // 安全考虑,不直接暴露 Node.js
      contextIsolation: true,    // 启用上下文隔离
    }
  });

  mainWindow.loadFile('index.html');
  
  // 开发时打开调试工具(可选)
  // mainWindow.webContents.openDevTools();
}

// 应用就绪时创建窗口
app.whenReady().then(() => {
  createWindow();

  // macOS:点击 Dock 图标时重新创建窗口
  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow();
    }
  });
});

// 所有窗口关闭时退出(macOS 除外)
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

3.3 预加载脚本 preload.js

const { contextBridge, ipcRenderer } = require('electron');

// 向渲染进程暴露安全的 API
contextBridge.exposeInMainWorld('electronAPI', {
  // 示例:获取版本信息
  getVersion: () => process.versions.electron,
  
  // 进程间通信示例
  sendMessage: (channel, data) => {
    // 白名单验证
    const validChannels = ['message-to-main'];
    if (validChannels.includes(channel)) {
      ipcRenderer.send(channel, data);
    }
  },
  
  // 异步调用示例
  invokeAction: (channel, data) => {
    const validChannels = ['get-data', 'save-file'];
    if (validChannels.includes(channel)) {
      return ipcRenderer.invoke(channel, data);
    }
  }
});

3.4 页面文件 index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
  <title>你好世界</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      height: 100vh;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
    }
    h1 {
      font-size: 48px;
      margin-bottom: 20px;
    }
    .info {
      background: rgba(255, 255, 255, 0.1);
      padding: 20px 40px;
      border-radius: 10px;
      backdrop-filter: blur(10px);
    }
    .info p {
      margin: 10px 0;
    }
  </style>
</head>
<body>
  <h1>🎉 你好世界!</h1>
  <div class="info">
    <p>欢迎使用 Electron</p>
    <p>Electron: <span id="electron-version"></span></p>
    <p>Node.js: <span id="node-version"></span></p>
    <p>Chrome: <span id="chrome-version"></span></p>
  </div>

  <script>
    document.getElementById('electron-version').textContent = process.versions.electron;
    document.getElementById('node-version').textContent = process.versions.node;
    document.getElementById('chrome-version').textContent = process.versions.chrome;
  </script>
</body>
</html>

四、package.json 完整配置

{
  // ========== 基础信息 ==========
  "name": "my-electron-app",              // 应用名称(小写、无空格)
  "version": "1.0.0",                     // 版本号(semver 格式)
  "description": "我的 Electron 应用",     // 应用描述
  "main": "main.js",                      // 主进程入口文件(必需)
  "author": "你的名字 <your@email.com>",   // 作者信息
  "license": "MIT",                       // 开源协议
  "private": true,                        // 防止意外发布到 npm
  
  // ========== 运行脚本 ==========
  "scripts": {
    "start": "electron .",                          // 启动应用
    "dev": "electron . --enable-logging",           // 开发模式(显示日志)
    "pack": "electron-builder --dir",               // 打包但不生成安装包
    "dist": "electron-builder",                     // 打包当前平台
    "dist:mac": "electron-builder --mac",           // 打包 macOS
    "dist:win": "electron-builder --win",           // 打包 Windows
    "dist:linux": "electron-builder --linux",       // 打包 Linux
    "dist:all": "electron-builder -mwl"             // 打包所有平台
  },
  
  // ========== 开发依赖 ==========
  "devDependencies": {
    "electron": "^28.0.0",                // Electron 运行时
    "electron-builder": "^24.9.1"         // 打包工具
  },
  
  // ========== 打包配置 ==========
  "build": {
    // --- 通用配置 ---
    "appId": "com.example.myapp",                   // 应用唯一标识(反向域名)
    "productName": "我的应用",                       // 应用显示名称(可包含中文和空格)
    "copyright": "Copyright © 2024 Your Name",      // 版权信息
    "asar": true,                                   // 打包为 asar 归档(推荐)
    "compression": "normal",                        // 压缩级别:store/normal/maximum
    
    // --- 目录配置 ---
    "directories": {
      "output": "dist",                             // 输出目录
      "buildResources": "build"                     // 构建资源目录(图标等)
    },
    
    // --- 文件配置 ---
    "files": [
      "**/*",                                       // 包含所有文件
      "!**/*.ts",                                   // 排除 TypeScript 源文件
      "!*.code-workspace",                          // 排除 VS Code 工作区
      "!src/",                                      // 排除源码目录(如已编译)
      "!node_modules/**/*",                         // 排除所有 node_modules
      "node_modules/需要的包/**/*"                   // 只包含需要的依赖
    ],
    
    // --- 额外资源 ---
    "extraResources": [
      {
        "from": "assets/",                          // 源目录
        "to": "assets",                             // 目标目录
        "filter": ["**/*"]                          // 过滤规则
      }
    ],
    
    // ========== macOS 配置 ==========
    "mac": {
      "icon": "build/icon.icns",                    // 图标文件(512x512)
      "category": "public.app-category.utilities",  // 应用分类
      "target": [
        {
          "target": "dmg",                          // DMG 镜像
          "arch": ["x64", "arm64"]                  // 支持 Intel 和 Apple Silicon
        },
        {
          "target": "zip",                          // ZIP 压缩包
          "arch": ["universal"]                     // 通用二进制
        }
      ],
      "type": "distribution",                       // 分发类型:distribution/development
      "hardenedRuntime": true,                      // 强化运行时(公证需要)
      "gatekeeperAssess": false,                    // Gatekeeper 评估
      "entitlements": "build/entitlements.mac.plist",           // 权限配置
      "entitlementsInherit": "build/entitlements.mac.plist",    // 继承权限
      "provisioningProfile": "build/profile.provisionprofile",  // 描述文件
      "identity": "Developer ID Application: Your Name (TEAM)", // 签名身份
      "minimumSystemVersion": "10.13.0"             // 最低系统版本
    },
    
    // --- macOS DMG 配置 ---
    "dmg": {
      "title": "${productName} ${version}",         // DMG 标题
      "icon": "build/icon.icns",                    // DMG 图标
      "background": "build/background.png",         // 背景图片(540x380)
      "window": {
        "width": 540,                               // 窗口宽度
        "height": 380                               // 窗口高度
      },
      "contents": [
        {
          "x": 140,                                 // 应用图标 x 坐标
          "y": 180                                  // 应用图标 y 坐标
        },
        {
          "x": 400,                                 // Applications 快捷方式 x 坐标
          "y": 180,                                 // Applications 快捷方式 y 坐标
          "type": "link",
          "path": "/Applications"
        }
      ]
    },
    
    // --- macOS PKG 配置 ---
    "pkg": {
      "license": "LICENSE.txt",                     // 许可证文件
      "installLocation": "/Applications",           // 安装位置
      "allowAnywhere": false,                       // 允许安装到任意位置
      "allowCurrentUserHome": false,                // 允许安装到用户主目录
      "allowRootDirectory": false                   // 允许安装到根目录
    },
    
    // ========== Windows 配置 ==========
    "win": {
      "icon": "build/icon.ico",                     // 图标文件(256x256)
      "target": [
        {
          "target": "nsis",                         // NSIS 安装包
          "arch": ["x64", "ia32"]                   // 64位和32位
        },
        {
          "target": "portable",                     // 便携版
          "arch": ["x64"]
        },
        {
          "target": "zip",                          // ZIP 压缩包
          "arch": ["x64"]
        }
      ],
      "publisherName": "Your Company",              // 发布者名称
      "verifyUpdateCodeSignature": true,            // 验证更新签名
      "certificateFile": "path/to/cert.pfx",        // 证书文件
      "certificatePassword": "password",            // 证书密码(建议用环境变量)
      "signingHashAlgorithms": ["sha256"],          // 签名算法
      "signDlls": true,                             // 签名 DLL 文件
      "rfc3161TimeStampServer": "http://timestamp.digicert.com",  // 时间戳服务器
      "requestedExecutionLevel": "asInvoker"        // 执行级别:asInvoker/highestAvailable/requireAdministrator
    },
    
    // --- Windows NSIS 配置 ---
    "nsis": {
      "oneClick": false,                            // 一键安装(true=无选项)
      "allowToChangeInstallationDirectory": true,   // 允许选择安装目录
      "allowElevation": true,                       // 允许权限提升
      "installerIcon": "build/installer.ico",       // 安装程序图标
      "uninstallerIcon": "build/uninstaller.ico",   // 卸载程序图标
      "installerHeaderIcon": "build/icon.ico",      // 安装程序头部图标
      "createDesktopShortcut": true,                // 创建桌面快捷方式
      "createStartMenuShortcut": true,              // 创建开始菜单快捷方式
      "shortcutName": "我的应用",                    // 快捷方式名称
      "perMachine": false,                          // 为所有用户安装(需要管理员权限)
      "runAfterFinish": true,                       // 安装完成后运行
      "deleteAppDataOnUninstall": false,            // 卸载时删除应用数据
      "menuCategory": false,                        // 开始菜单分类(false=不创建子菜单)
      "artifactName": "${productName}-Setup-${version}.${ext}",  // 安装包文件名
      "uninstallDisplayName": "${productName}",     // 卸载程序显示名称
      "license": "LICENSE.txt",                     // 许可证文件
      "include": "build/installer.nsh",             // 自定义 NSIS 脚本
      "multiLanguageInstaller": true,               // 多语言安装程序
      "language": "2052",                           // 语言 ID(2052=简体中文)
      "warningsAsErrors": true                      // 警告视为错误
    },
    
    // --- Windows 便携版配置 ---
    "portable": {
      "artifactName": "${productName}-Portable-${version}.${ext}"
    },
    
    // ========== Linux 配置 ==========
    "linux": {
      "icon": "build/icon.png",                     // 图标文件(512x512)
      "category": "Utility",                        // 应用分类
      "target": [
        {
          "target": "AppImage",                     // AppImage 格式
          "arch": ["x64", "arm64"]
        },
        {
          "target": "deb",                          // Debian 包
          "arch": ["x64", "arm64"]
        },
        {
          "target": "rpm",                          // RedHat 包
          "arch": ["x64"]
        },
        {
          "target": "snap",                         // Snap 包
          "arch": ["x64"]
        }
      ],
      "executableName": "my-app",                   // 可执行文件名
      "synopsis": "简短描述",                        // 简短描述(一行)
      "description": "详细描述\n可以多行",            // 详细描述
      "maintainer": "maintainer@example.com",       // 维护者邮箱
      "vendor": "Your Company",                     // 供应商名称
      "desktop": {                                  // 桌面文件配置
        "Name": "我的应用",
        "GenericName": "应用通用名称",
        "Comment": "应用描述",
        "Type": "Application",
        "Terminal": false,
        "Categories": "Utility;Development;",
        "MimeType": "x-scheme-handler/myapp;",
        "StartupWMClass": "my-electron-app"
      }
    },
    
    // --- Linux AppImage 配置 ---
    "appImage": {
      "license": "LICENSE.txt",                     // 许可证文件
      "artifactName": "${productName}-${version}-${arch}.${ext}",
      "systemIntegration": "ask"                    // 系统集成:ask/doNotAsk
    },
    
    // --- Linux Snap 配置 ---
    "snap": {
      "grade": "stable",                            // 等级:stable/devel
      "confinement": "strict",                      // 限制:strict/devmode/classic
      "summary": "简短描述(最多78字符)",
      "plugs": [                                    // 插件权限
        "default",
        "home",
        "network",
        "removable-media"
      ]
    },
    
    // --- Linux Deb 配置 ---
    "deb": {
      "depends": [                                  // 依赖包
        "libgtk-3-0",
        "libnotify4",
        "libnss3",
        "libxss1"
      ],
      "recommends": [],                             // 推荐包
      "suggests": [],                               // 建议包
      "priority": "optional",                       // 优先级:required/important/standard/optional/extra
      "compression": "xz",                          // 压缩方式:gzip/xz/bzip2
      "afterInstall": "build/deb-postinst.sh",      // 安装后脚本
      "afterRemove": "build/deb-postrm.sh"          // 卸载后脚本
    },
    
    // ========== 发布配置 ==========
    "publish": [
      {
        "provider": "github",                       // 发布平台:github/s3/generic
        "owner": "your-username",                   // GitHub 用户名
        "repo": "your-repo",                        // 仓库名
        "token": "${GH_TOKEN}",                     // GitHub Token(环境变量)
        "private": false,                           // 私有仓库
        "releaseType": "release"                    // 发布类型:draft/prerelease/release
      },
      {
        "provider": "generic",                      // 通用服务器
        "url": "https://example.com/releases",      // 更新服务器地址
        "channel": "latest"                         // 更新通道
      }
    ],
    
    // ========== 自动更新配置 ==========
    "electronUpdater": {
      "autoDownload": true,                         // 自动下载更新
      "autoInstallOnAppQuit": true,                 // 退出时自动安装
      "allowDowngrade": false,                      // 允许降级
      "allowPrerelease": false                      // 允许预发布版本
    },
    
    // ========== 其他配置 ==========
    "protocols": [                                  // URL Scheme 配置
      {
        "name": "my-app-protocol",
        "schemes": ["myapp"],                       // myapp://
        "role": "Editor"                            // Editor/Viewer/Shell/None
      }
    ],
    
    "fileAssociations": [                           // 文件关联
      {
        "ext": "myext",                             // 文件扩展名
        "name": "My File Type",                     // 文件类型名称
        "description": "My File Description",       // 文件描述
        "icon": "build/fileIcon.icns",              // 文件图标
        "role": "Editor"                            // 角色
      }
    ],
    
    "artifactName": "${productName}-${version}-${os}-${arch}.${ext}",  // 输出文件名格式
    
    "detectUpdateChannel": true,                    // 自动检测更新通道
    
    "forceCodeSigning": false,                      // 强制代码签名
    
    "electronLanguages": [                          // Electron 语言包
      "zh-CN",
      "en-US"
    ],
    
    "extends": null                                 // 继承配置文件
  }
}

配置说明:

  • 注释以 // 开头的行在实际使用时需要删除(JSON 不支持注释)
  • 使用时可以只保留需要的字段,其他使用默认值
  • ${变量名} 可引用其他字段值或环境变量
  • 密码等敏感信息建议使用环境变量,如 process.env.CSC_KEY_PASSWORD

五、运行与调试

5.1 启动应用

npm start

5.2 开启调试

方式1:代码中打开

// main.js
mainWindow.webContents.openDevTools();

方式2:快捷键

  • macOS: Cmd + Option + I
  • Windows/Linux: Ctrl + Shift + I

方式3:右键菜单

  • 右键页面 → 检查元素

5.3 主进程调试

创建 .vscode/launch.json

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "调试主进程",
      "type": "node",
      "request": "launch",
      "cwd": "${workspaceFolder}",
      "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
      "windows": {
        "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
      },
      "args": ["."],
      "outputCapture": "std",
      "console": "integratedTerminal"
    }
  ]
}

六、打包应用

6.1 准备图标

平台格式尺寸路径
macOS.icns512x512build/icon.icns
Windows.ico256x256build/icon.ico
Linux.png512x512build/icon.png

图标生成工具:

6.2 执行打包

# 当前平台
npm run dist

# 指定平台
npm run dist:mac      # macOS
npm run dist:win      # Windows
npm run dist:linux    # Linux

6.3 输出目录

dist/
├── mac/
│   └── MyApp.app
├── MyApp-1.0.0.dmg
├── win-unpacked/
├── MyApp Setup 1.0.0.exe
├── linux-unpacked/
└── MyApp-1.0.0.AppImage

七、进程间通信 (IPC)

7.1 渲染进程 → 主进程(单向)

// preload.js
contextBridge.exposeInMainWorld('api', {
  send: (channel, data) => ipcRenderer.send(channel, data)
});

// main.js
ipcMain.on('message', (event, data) => {
  console.log(data);
});

// renderer.js
window.api.send('message', '你好');

7.2 主进程 → 渲染进程

// main.js
mainWindow.webContents.send('reply', '你好,渲染进程');

// preload.js
contextBridge.exposeInMainWorld('api', {
  onReply: (callback) => ipcRenderer.on('reply', callback)
});

// renderer.js
window.api.onReply((event, data) => {
  console.log(data);
});

7.3 双向通信(invoke/handle)

// main.js
ipcMain.handle('get-data', async (event, arg) => {
  return await fetchData(arg);
});

// preload.js
contextBridge.exposeInMainWorld('api', {
  getData: (arg) => ipcRenderer.invoke('get-data', arg)
});

// renderer.js
const data = await window.api.getData('参数');

八、常用功能

8.1 系统托盘

const { app, Tray, Menu } = require('electron');
const path = require('path');

let tray = null;

app.whenReady().then(() => {
  tray = new Tray(path.join(__dirname, 'tray-icon.png'));
  
  const contextMenu = Menu.buildFromTemplate([
    { label: '显示', click: () => mainWindow.show() },
    { label: '退出', click: () => app.quit() }
  ]);
  
  tray.setContextMenu(contextMenu);
  tray.setToolTip('我的应用');
});

8.2 系统通知

const { Notification } = require('electron');

new Notification({
  title: '标题',
  body: '内容',
  icon: path.join(__dirname, 'icon.png')
}).show();

8.3 文件对话框

const { dialog } = require('electron');

// 打开文件
const result = await dialog.showOpenDialog({
  properties: ['openFile', 'multiSelections'],
  filters: [
    { name: 'Images', extensions: ['jpg', 'png'] },
    { name: 'All Files', extensions: ['*'] }
  ]
});

// 保存文件
const saveResult = await dialog.showSaveDialog({
  defaultPath: 'untitled.txt'
});

8.4 自定义菜单

const { Menu } = require('electron');

const template = [
  {
    label: '文件',
    submenu: [
      { label: '新建', accelerator: 'CmdOrCtrl+N', click: () => {} },
      { type: 'separator' },
      { role: 'quit', label: '退出' }
    ]
  },
  {
    label: '编辑',
    submenu: [
      { role: 'undo', label: '撤销' },
      { role: 'redo', label: '重做' },
      { type: 'separator' },
      { role: 'cut', label: '剪切' },
      { role: 'copy', label: '复制' },
      { role: 'paste', label: '粘贴' }
    ]
  }
];

const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);

8.5 全局快捷键

const { globalShortcut } = require('electron');

app.whenReady().then(() => {
  globalShortcut.register('CommandOrControl+X', () => {
    console.log('快捷键触发');
  });
});

app.on('will-quit', () => {
  globalShortcut.unregisterAll();
});

九、自动更新

9.1 安装依赖

npm install electron-updater

9.2 主进程配置

const { autoUpdater } = require('electron-updater');

app.whenReady().then(() => {
  // 检查更新
  autoUpdater.checkForUpdatesAndNotify();
});

// 监听更新事件
autoUpdater.on('update-available', () => {
  console.log('有新版本');
});

autoUpdater.on('update-downloaded', () => {
  console.log('下载完成');
  // 退出并安装
  autoUpdater.quitAndInstall();
});

9.3 发布配置

package.jsonbuild 中配置:

{
  "build": {
    "publish": [
      {
        "provider": "github",
        "owner": "your-username",
        "repo": "your-repo"
      }
    ]
  }
}

十、安全最佳实践

10.1 安全检查清单

// main.js
const mainWindow = new BrowserWindow({
  webPreferences: {
    nodeIntegration: false,        // ✅ 必须:禁用 Node 集成
    contextIsolation: true,        // ✅ 必须:启用上下文隔离
    enableRemoteModule: false,     // ✅ 必须:禁用 remote 模块
    sandbox: true,                 // ✅ 推荐:启用沙箱
    webSecurity: true,             // ✅ 必须:启用 Web 安全
    allowRunningInsecureContent: false,  // ✅ 必须:禁止不安全内容
    preload: path.join(__dirname, 'preload.js')
  }
});

10.2 CSP 配置

<!-- index.html -->
<meta http-equiv="Content-Security-Policy" 
      content="default-src 'self'; 
               script-src 'self'; 
               style-src 'self' 'unsafe-inline';">

10.3 验证 IPC 消息

// preload.js
contextBridge.exposeInMainWorld('api', {
  sendMessage: (channel, data) => {
    // 白名单验证
    const validChannels = ['allowed-channel-1', 'allowed-channel-2'];
    if (validChannels.includes(channel)) {
      ipcRenderer.send(channel, data);
    }
  }
});

十一、常见问题

11.1 启动白屏

原因: 路径错误或资源加载失败

解决:

// 使用绝对路径
mainWindow.loadFile(path.join(__dirname, 'index.html'));

11.2 打包后无法运行

检查:

  1. package.jsonmain 字段是否正确
  2. files 配置是否包含所有必要文件
  3. 查看控制台错误信息

11.3 macOS "已损坏"提示

临时解决(仅测试):

sudo xattr -r -d com.apple.quarantine /path/to/YourApp.app

正确做法: 代码签名和公证


十二、项目结构建议

小型项目

my-electron-app/
├── main.js
├── preload.js
├── index.html
├── style.css
├── renderer.js
├── package.json
└── build/
    ├── icon.icns
    ├── icon.ico
    └── icon.png

中大型项目

my-electron-app/
├── src/
│   ├── main/              # 主进程
│   │   ├── index.js
│   │   ├── menu.js
│   │   ├── tray.js
│   │   └── ipc.js
│   ├── renderer/          # 渲染进程
│   │   ├── index.html
│   │   ├── index.js
│   │   └── styles/
│   └── preload/
│       └── index.js
├── assets/                # 资源文件
├── build/                 # 构建配置
├── dist/                  # 打包输出
└── package.json

十三、学习资源


十四、快速命令

# 初始化
npm init -y
npm install electron electron-builder --save-dev

# 运行
npm start

# 打包
npm run dist
npm run dist:mac
npm run dist:win
npm run dist:linux

# 清理
rm -rf node_modules dist
npm install