electron-vite-samples
repository·main·Indexed 43 days ago
https://github.com/caoxiemeihao/electron-vite-samplesA collection of best practice samples and implementation patterns for using electron-vite. Includes configurations for ESM packages in renderer processes, Electron Forge integration with Svelte, building C/C++ native modules, multi-window and multi-renderer architectures, and integrating better-sqlite3 in both main and renderer processes.
What's inside electron-vite-samples
- This sample demonstrates how to integrate Electron Forge, Vite, and Svelte within an Electron application. It provides a boilerplate for developers looking to use Svelte as the UI framework while leveraging Vite's build tooling and Electron Forge's packaging capabilities.
Use ESM packages in the Electron Renderer Process
mainThe
esmodule-renderer-processsample demonstrates how to use Node.js ECMAScript Modules (ESM) directly within the Electron renderer process. This setup allows you to import and use modern Node.js ESM packages that might otherwise struggle in a traditional CommonJS Electron environment.Key capabilities demonstrated include:
- Accessing Electron and Node.js APIs (e.g.,
electron,fs). - Using Node.js native addons (e.g.,
serialport,sqlite3). - Importing Node.js ESM packages (e.g.,
execa,got,node-fetch).
// Example of what can be used in this setup: import { ipcRenderer } from 'electron'; import fs from 'fs'; import { execa } from 'execa'; import got from 'got';- Accessing Electron and Node.js APIs (e.g.,
Use the main-preload-only template for headless or preload-centric Electron apps
mainThe
main-preload-onlysample is designed for Electron applications that do not require a standard renderer process (e.g., apps that primarily run logic in the Main process or rely heavily on Preload scripts for system integration without a complex UI).A key feature of this configuration is support for preload scripts hot-reload, allowing you to iterate on your preload logic without manually restarting the entire application.
Use Node.js APIs and Native Addons in the Electron Main Process
mainThis sample demonstrates how to utilize the full power of the Node.js environment within the Electron Main Process. Unlike the Renderer process, the Main process has direct access to:
- Electron and Node.js APIs: Direct usage of
electronand core Node.js modules likefs. - Node.js Native Addons: Integration of C++ based native modules such as
serialportandsqlite3. - Node.js ESM Packages: Support for modern ECMAScript Module (ESM) packages like
execa,got, andnode-fetch.
- Electron and Node.js APIs: Direct usage of
Build C/C++ native modules with Electron Forge and Vite
mainTheelectron-forge-vite-nativesample demonstrates how to integrate and build C/C++ native modules within an Electron project that uses the Vite plugin via Electron Forge. This is useful when your Electron application requires high-performance native code or access to system-level APIs not available in standard JavaScript/TypeScript.Implement Multiple BrowserWindows in Electron
mainThis sample demonstrates how to manage multiple
BrowserWindowinstances within an Electron application. It covers two primary patterns:- Multiple HTML (Renderer) pages: Loading different HTML files into different windows.
- Using multiple different BrowserWindow instances: Programmatically creating and managing distinct window objects, each potentially with its own configuration (size, position, webPreferences, etc.).
Implement multiple renderer pages in a single BrowserWindow
mainThis sample demonstrates how to manage multiple HTML renderer pages within a singleBrowserWindow. This pattern is useful when you want to switch between different views or application states without opening new windows, effectively treating the single window as a container for different UI contexts.Compare Rollup and Webpack bundling mechanisms
mainUnderstanding the difference between Rollup and Webpack is crucial when deciding which bundler to use for your project, especially regarding module interop and output structure.
Webpack
Webpack organizes code following the CommonJS (CJS) specification. It uses a module-centric approach involving:
- Modules: A central registry of all modules.
module.exports: The mechanism for exporting members.require: The function used for loading modules.
Webpack wraps modules in a function shell to manage these dependencies.
Rollup
Rollup produces output based on the standard ESModule (ESM) specification. It does not use an implicit CommonJS wrapper, resulting in "what you see is what you get" code that closely matches your source. This makes Rollup highly efficient for tree-shaking but can lead to side effects when handling dynamic
import()calls if not managed carefully.// Webpack Bundle Example var __webpack_modules__ = { 0: (module, exports, __webpack_require__) => { const { add } = __webpack_require__(1) const sum = add(1 + 2) }, 1: (module, exports, __webpack_require__) => { function add(a, b) { return a + b } module.exports = { add }; } } module.exports = __webpack_require__(0) // Rollup Bundle Example function add(a, b) { return a + b } const sum = add(1 + 2)Handle C/C++ (.node) modules in bundlers
mainC/C++ modules (compiled as
.nodebinary files) cannot be bundled into a single JavaScript file like standard JS because they require CommonJS bindings to be loaded by Node.js.Webpack Strategy
Webpack handles
.nodefiles by copying them to thedistfolder and injecting arequire('./xxx.node')statement. For modules built with tools likenode-gypornode-pre-gyp(which typically use paths likebuild/Release/xxx.node), you should use loaders like@vercel/webpack-asset-relocator-loaderto ensure the files are correctly relocated and the runtime paths are injected into the bundle.Rollup Strategy
Rollup does not natively support C/C++ modules. You can use
rollup-plugin-nativesto approximate Webpack's behavior by bypassing standard CommonJS plugin processing forrequire()calls to avoid parsing errors.General Rules for .node files:
- Direct Require: If a package uses
require('./xx.node')directly, simply copy the.nodefile to your output directory. - Tool-built (node-gyp/node-pre-gyp): Locate the file following the standard build patterns (e.g.,
build/Release/) and copy it to the output directory.
Handling Unpredictable Modules (e.g.,
serialport)If a module does not follow standard patterns or allows dynamic configuration of its
.nodepath (likebetter-sqlite3), it may fail to bundle correctly. The recommended solution for Electron developers is to mark the problematic package as external in your Webpack configuration and include it in yourpackage.jsondependencies. Tools likeelectron-builderwill then copy the entire dependency tree into the finalnode_modulesfolder, allowing Node.js to resolve the binaries at runtime.// Webpack external configuration for problematic native modules module.exports = { externals: { serialport: 'commonjs Serialport' } };- Direct Require: If a package uses
How window switching works in the main process
mainThe
switchURLfunction handles the lifecycle of windows in this sample. It determines whether to instantiate a newBrowserWindowor focus an existing one based on thetitleproperty.Loading Logic:
- Development: If
process.env.VITE_DEV_SERVER_URLis present, the window loads the URL from the Vite dev server:[VITE_DEV_SERVER_URL]/html/[html_filename]. - Production: The window loads the file from the local filesystem:
../dist/html/[html_filename].
Window Configuration:
- Each window is assigned a
titleequal to the requested HTML filename. - A
preload.jsscript is automatically attached to every window created via this function.
// Internal logic used by the 'to-window' IPC handler function switchURL(html = 'index.html') { const existWin = windows.find(w => w.title === html) if (existWin) { existWin.focus() } else { const win = new BrowserWindow({ title: html, webPreferences: { preload: path.join(__dirname, 'preload.js'), }, }) if (process.env.VITE_DEV_SERVER_URL) { win.loadURL(path.posix.join(process.env.VITE_DEV_SERVER_URL, 'html', html)) } else { win.loadFile(path.join(__dirname, `../dist/html/${html}`)) } windows.push(win) } }- Development: If
Use Vite environment variables in the Main Process
mainWhen using Electron Forge with Vite, specific global variables are injected into the main process to facilitate loading the renderer content. You must use these to switch between the development server and production assets:
MAIN_WINDOW_VITE_DEV_SERVER_URL: The URL of the Vite development server (available duringnpm startordevmode).MAIN_WINDOW_VITE_NAME: The name of the renderer entry point, used to construct the file path in production builds.
Communicate from Main Process to Renderer via IPC
mainThe Main process communicates with the Renderer process using Electron's
webContents.sendmethod. In this implementation, two types of messages are sent:- Lifecycle Message: Sent when the window finishes loading (
did-finish-load) to provide a timestamp. - Database Status Message: Sent after the SQLite3 database has been successfully initialized.
The Renderer process should listen for the
'main-process-message'channel to receive these updates.// In the Main process: win.webContents.send('main-process-message', 'message content'); // In the Renderer process (via preload/contextBridge): ipcRenderer.on('main-process-message', (event, message) => { console.log(message); });- Lifecycle Message: Sent when the window finishes loading (