人人都会AI编程

18.2 Webpack + Electron 构建方案

更新时间:2026-07-11

Webpack 是 Electron 项目中最常用的构建工具之一。虽然 Vite 等新工具逐渐流行,但 Webpack 凭借成熟的生态、丰富的插件和庞大的社区支持,依然是大量现有项目的基础设施。本节将提供一个可直接落地的 Webpack + Electron 配置方案,覆盖渲染进程与主进程的构建需求。

18.2.1 为什么还需要 Webpack

Electron 本身可以加载任意 HTML 文件,理论上你用原生的 <script> 标签也能写应用。但实际项目中,你需要:

  • 使用 TypeScript、Sass、Less 等预编译语言
  • 支持 JSX 或 Vue 单文件组件
  • 按需加载、代码分割以优化启动速度
  • 开发时热更新(HMR),提高迭代效率
  • 生产环境压缩代码、分配缓存、减少体积

Webpack 恰好能在这些环节提供完整支持,而且它与 Electron 的进程模型可以很好地分工:渲染进程配置像普通前端项目,主进程配置则针对 Node.js 环境

18.2.2 基础项目结构

假设我们的 Electron 项目结构如下:

my-app/
├── src/
│   ├── main/           # 主进程代码
│   │   └── index.ts
│   ├── preload/        # 预加载脚本
│   │   └── index.ts
│   └── renderer/       # 渲染进程(前端代码)
│       ├── index.html
│       ├── index.tsx   # React 入口
│       └── App.tsx
├── package.json
├── webpack.main.config.js   # 主进程 Webpack 配置
├── webpack.renderer.config.js # 渲染进程 Webpack 配置
└── tsconfig.json

我们将为主进程渲染进程分别编写 Webpack 配置,因为这俩环境差异极大:主进程运行在 Node.js 中,目标为 CommonJS 模块;渲染进程运行在 Chromium 中,目标为浏览器环境。

18.2.3 渲染进程配置

渲染进程本质上就是一个现代前端应用,可以使用熟悉的 loader 和 plugin。

安装依赖:

npm install --save-dev webpack webpack-cli webpack-dev-server
npm install --save-dev html-webpack-plugin ts-loader css-loader style-loader
npm install --save-dev react react-dom @types/react @types/react-dom

webpack.renderer.config.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
  entry: './src/renderer/index.tsx',
  target: 'web',  // 渲染进程运行在浏览器上下文
  output: {
    path: path.resolve(__dirname, 'dist/renderer'),
    filename: '[name].[contenthash].js',
    clean: true,
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx'],
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(png|jpg|gif|svg|woff2?)$/,
        type: 'asset/resource',
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/renderer/index.html',
    }),
  ],
  devServer: {
    port: 3000,
    hot: true,
    static: {
      directory: path.join(__dirname, 'dist/renderer'),
    },
  },
};

src/renderer/index.html:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>My App</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>

在开发阶段,渲染进程可以独立启动 webpack-dev-server,方便前端工程师快速调试界面;而在 Electron 环境中,我们会让 Electron 窗口加载 http://localhost:3000

18.2.4 主进程配置

主进程需要在 Node.js 环境中运行,因此 target 应该设为 'electron-main'(或 'node',但 'electron-main' 专为 Electron 做了优化)。同时,我们需要保留原生模块的 require 方式,不打包 Electron 内建模块和 node_modules。

webpack.main.config.js:

const path = require('path');

module.exports = {
  mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
  entry: './src/main/index.ts',
  target: 'electron-main',
  output: {
    path: path.resolve(__dirname, 'dist/main'),
    filename: 'main.js',
    clean: true,
  },
  resolve: {
    extensions: ['.ts', '.js'],
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  externals: {
    // 告诉 Webpack 不要打包 Electron 原生模块,保持外部引用
    electron: 'commonjs2 electron',
  },
  node: {
    __dirname: false,  // 保证 __dirname 指向原始目录,而非 webpack 拼接的虚拟路径
    __filename: false,
  },
};

关键点说明:

  • externalselectron 标记为外部依赖,这样 Webpack 不会把它打进 bundle,而是由 Electron 运行时提供。
  • node.dirname: false 避免 Webpack 对 dirname 的替换,保证文件路径的正确性(尤其在读取本地资源时很重要)。

18.2.5 预加载脚本配置

预加载脚本运行在一个介于 Node.js 和渲染进程之间的受限环境,通常使用 CommonJS 模块,且应该被单独编译。

可以新建一个配置 webpack.preload.config.js

const path = require('path');

module.exports = {
  mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
  entry: './src/preload/index.ts',
  target: 'electron-preload',
  output: {
    path: path.resolve(__dirname, 'dist/preload'),
    filename: 'preload.js',
  },
  resolve: {
    extensions: ['.ts', '.js'],
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
};

然后在主进程中设置:

const win = new BrowserWindow({
  webPreferences: {
    preload: path.join(__dirname, '../preload/preload.js'),
    contextIsolation: true,
  },
});

18.2.6 开发环境集成

理想状态下,开发者启动一个命令就能同时执行 Webpack 对主进程的编译、渲染进程的 dev server,并启动 Electron。

可以使用 concurrentlynpm-run-all 来编排:

package.json 脚本:

"scripts": {
  "start:renderer": "webpack serve --config webpack.renderer.config.js",
  "build:main": "webpack --config webpack.main.config.js",
  "build:preload": "webpack --config webpack.preload.config.js",
  "dev:electron": "electron .",   // 从 dist/main/main.js 启动
  "dev": "concurrently \"npm run build:main\" \"npm run build:preload\" \"npm run start:renderer\" \"wait-on http://localhost:3000 && npm run dev:electron\""
}

这样,运行 npm run dev 会:

  1. 编译主进程和预加载脚本到 dist/
  2. 启动 dev server 在端口 3000
  3. 等待 dev server 可用后,启动 Electron 并加载 http://localhost:3000

为了让 Electron 加载 dev server,主进程代码中可以判断环境:

// src/main/index.ts
const isDev = process.env.NODE_ENV === 'development';
const win = new BrowserWindow({
  // ...
});

if (isDev) {
  win.loadURL('http://localhost:3000');
  win.webContents.openDevTools();
} else {
  win.loadFile(path.join(__dirname, '../renderer/index.html'));
}

18.2.7 生产构建

生产构建需要同时构建渲染进程、主进程和预加载脚本,并处理好资源路径。可以为 package.json 添加:

"scripts": {
  "build:renderer": "webpack --mode production --config webpack.renderer.config.js",
  "build:all": "npm run build:main && npm run build:preload && npm run build:renderer",
  "package": "npm run build:all && electron-builder"
}

electron-builder 打包时通常会读取 dist/ 目录下的文件。注意将 electron-builderfiles 配置指向正确的构建输出。

Electron Builder 配置片段:

"build": {
  "files": [
    "dist/**/*"
  ],
  "directories": {
    "output": "release"
  }
}

18.2.8 实用建议

  1. 区分环境变量

使用 cross-env 设置 NODE_ENV,并在 Webpack 配置中通过 DefinePlugin 注入全局常量,方便在代码中判断开发/生产模式。

  1. 主进程代码分割

主进程通常不需要 Code Splitting,因为 Node.js 环境支持动态 require,但 Webpack 的 Tree Shaking 可以移除未使用的代码,保持主进程启动速度。

  1. 原生模块处理

如果你的依赖中包含 C++ 原生模块(如 sqlite3),不要在主进程 Webpack 中包含它,将其标记为 externals 或使用 node-loader 动态加载。打包时记住在 electron-builder 中重建原生模块。

  1. TypeScript 支持

主进程、预加载脚本、渲染进程分别使用独立的 tsconfig.json,确保每个环境的 targetlib 设置正确(例如渲染进程可以设为 ES2020,主进程设为 ES2019 并包含 Node 类型定义)。

  1. 使用 webpack-merge

如果配置中存在大量公共部分,可以用 webpack-merge 抽取共用的 rulesresolve,避免重复代码。

  1. 调试主进程

开发时可使用 VS Code 的 “Attach to Process” 功能,配合 --inspect 参数调试主进程 Webpack 编译后的代码。可以在启动脚本中加入:

   electron --inspect=5858 .
   

通过这套 Webpack 配置方案,你可以用最熟悉的工具链来构建一个模块化、可维护的 Electron 应用。无论是主进程还是渲染进程,Webpack 都能提供一致的开发体验,并保证最终产物的优化质量。下一节我们会探讨 Vite 等更轻量的构建方式,看看它们如何进一步简化整个流程。