Electron 构建子进程(Node.js 进程)时的依赖处理与前端页面完全不同。
核心区别
| 维度 | 前端页面依赖 | 子进程(Node.js)依赖 |
|---|---|---|
| 处理方式 | 打包工具(Vite/Webpack)分析、tree-shaking、代码分割 | npm 模块直接安装,随应用一起打包 |
| 产物位置 | 打包到 bundle.js(浏览器可执行) | 保留在 node_modules,或打包为 asar 归档 |
| 安装时机 | 构建时解析并打包 | 开发时 npm install,构建时复制 |
| 运行时环境 | 浏览器沙箱(无 Node.js API) | 完整 Node.js 环境(可访问系统资源) |
| 原生模块 | 不支持(如 C++ 扩展) | 支持,但需要针对 Electron 重新编译 |
bundle.js 是前端开发中常见的打包文件,通常由模块打包工具(如 Webpack、Rollup 或 Parcel)生成。它将项目中分散的多个 JavaScript 文件、依赖库和资源合并为一个或多个优化后的文件,便于浏览器加载和执行。
详细对比
1. 前端页面依赖处理
javascript
// 前端代码 - 会被打包工具处理
import { createApp } from 'vue' // 从 node_modules 提取,打包到 bundle
import axios from 'axios' // 同上,tree-shaking 去除未使用代码
// 构建后:所有依赖变成单一/多个 JS 文件,无 node_modules
构建产物:
dist/
├── assets/
│ ├── index-xxx.js # 包含 vue、axios 等所有依赖代码
│ └── index-yyy.css
└── index.html
2. 子进程(主进程/Node 进程)依赖处理
javascript
// 主进程 main.js - Node.js 运行时直接执行
const { app, BrowserWindow } = require('electron')
const path = require('path')
const fs = require('fs-extra') // npm 依赖,Node 直接 require
const sqlite3 = require('better-sqlite3') // 原生 C++ 模块
// 这些依赖不会被"打包",而是随应用分发
构建产物:
app.asar (或 app 目录)
├── main.js
├── preload.js
├── node_modules/ # ← 依赖保留原样
│ ├── fs-extra/
│ ├── better-sqlite3/
│ └── electron/
└── package.json
Electron Builder 如何处理依赖
配置选项(electron-builder.yml)
yaml
# 默认行为:自动安装生产依赖
files:
- "**/*" # 包含所有文件
- "!node_modules/.bin/**/*" # 排除 .bin 目录
- "!**/*.map" # 排除 source map
# 依赖安装策略
npmRebuild: true # 重新编译原生模块(重要!)
nodeGypRebuild: false # 是否运行 node-gyp rebuild
# 指定额外依赖
extraResources:
- from: "node_modules/my-native-module"
to: "app/node_modules/my-native-module"
关键机制
| 机制 | 说明 |
|---|---|
asar 打包 |
默认将 node_modules 打包成 app.asar(类 tar 归档),减小体积并保护源码 |
| 原生模块重建 | npmRebuild: true 会自动使用 electron-rebuild 针对当前 Electron 版本重新编译 C++ 扩展 |
| 依赖过滤 | 自动排除 devDependencies,仅保留 dependencies |
| 平台特定 | Windows 构建时只打包 Windows 版原生模块,macOS 同理 |
实际示例
场景:使用 SQLite 数据库
1. 安装依赖
bash
npm install better-sqlite3 # 原生 C++ 模块
2. 主进程代码(main.js)
javascript
const Database = require('better-sqlite3')
const path = require('path')
const dbPath = path.join(app.getPath('userData'), 'app.db')
const db = new Database(dbPath)
// 直接使用 SQL,无需 HTTP API
db.exec('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)')
3. 构建配置
json
// package.json
{
"build": {
"appId": "com.example.app",
"npmRebuild": true, // 关键:重新编译原生模块
"asar": true, // 打包为 asar(可选)
"asarUnpack": [
"node_modules/better-sqlite3/**/*" // 原生模块通常需要解压(unpacked)
]
}
}
为什么 asarUnpack?
- 某些原生模块需要从文件系统直接加载 DLL/dylib,无法从 asar 归档内读取
asarUnpack指定这些文件保持独立,不打包进 asar
总结对比图
┌─────────────────────────────────────────────────────────────┐
│ Electron 应用结构 │
├─────────────────────────────────────────────────────────────┤
│ 渲染进程(前端页面) │
│ ├── 依赖:Vue/React 等 │
│ ├── 处理:Vite/Webpack 打包成静态 JS │
│ └── 结果:单个/多个 bundle 文件,无 node_modules │
├─────────────────────────────────────────────────────────────┤
│ 主进程/子进程(Node.js) │
│ ├── 依赖:fs-extra、sqlite3、axios 等 │
│ ├── 处理:npm install → 复制到输出目录 → 可选 asar 打包 │
│ ├── 原生模块:electron-rebuild 重新编译 │
│ └── 结果:保留 node_modules 结构,或 app.asar 归档 │
└─────────────────────────────────────────────────────────────┘
关键结论:
- 前端依赖:构建时打包,浏览器执行
- 子进程依赖:安装时下载 ,Node.js 运行时直接
require,Electron Builder 负责复制和原生模块重建