electron-vite-samples

repository·main·Indexed 43 days ago

https://github.com/caoxiemeihao/electron-vite-samples

A 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.

Tokens
27K
Snippets
75
Records
83
Agent score
53%

What's inside electron-vite-samples

  1. Use ESM packages in the Electron Renderer Process

    main

    The esmodule-renderer-process sample 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';
  2. Use the main-preload-only template for headless or preload-centric Electron apps

    main

    The main-preload-only sample 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.

  3. Use Node.js APIs and Native Addons in the Electron Main Process

    main

    This 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:

    1. Electron and Node.js APIs: Direct usage of electron and core Node.js modules like fs.
    2. Node.js Native Addons: Integration of C++ based native modules such as serialport and sqlite3.
    3. Node.js ESM Packages: Support for modern ECMAScript Module (ESM) packages like execa, got, and node-fetch.
  4. Build C/C++ native modules with Electron Forge and Vite

    main
    The electron-forge-vite-native sample 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.
  5. Implement Multiple BrowserWindows in Electron

    main

    This sample demonstrates how to manage multiple BrowserWindow instances within an Electron application. It covers two primary patterns:

    1. Multiple HTML (Renderer) pages: Loading different HTML files into different windows.
    2. Using multiple different BrowserWindow instances: Programmatically creating and managing distinct window objects, each potentially with its own configuration (size, position, webPreferences, etc.).
  6. Implement multiple renderer pages in a single BrowserWindow

    main
    This sample demonstrates how to manage multiple HTML renderer pages within a single BrowserWindow. 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.
  7. Compare Rollup and Webpack bundling mechanisms

    main

    Understanding 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)
  8. Handle C/C++ (.node) modules in bundlers

    main

    C/C++ modules (compiled as .node binary 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 .node files by copying them to the dist folder and injecting a require('./xxx.node') statement. For modules built with tools like node-gyp or node-pre-gyp (which typically use paths like build/Release/xxx.node), you should use loaders like @vercel/webpack-asset-relocator-loader to 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-natives to approximate Webpack's behavior by bypassing standard CommonJS plugin processing for require() calls to avoid parsing errors.

    General Rules for .node files:

    1. Direct Require: If a package uses require('./xx.node') directly, simply copy the .node file to your output directory.
    2. 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 .node path (like better-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 your package.json dependencies. Tools like electron-builder will then copy the entire dependency tree into the final node_modules folder, allowing Node.js to resolve the binaries at runtime.

    // Webpack external configuration for problematic native modules
    module.exports = {
      externals: {
        serialport: 'commonjs Serialport'
      }
    };
  9. How window switching works in the main process

    main

    The switchURL function handles the lifecycle of windows in this sample. It determines whether to instantiate a new BrowserWindow or focus an existing one based on the title property.

    Loading Logic:

    • Development: If process.env.VITE_DEV_SERVER_URL is 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 title equal to the requested HTML filename.
    • A preload.js script 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)
      }
    }
  10. Use Vite environment variables in the Main Process

    main

    When 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 during npm start or dev mode).
    • MAIN_WINDOW_VITE_NAME: The name of the renderer entry point, used to construct the file path in production builds.
  11. Communicate from Main Process to Renderer via IPC

    main

    The Main process communicates with the Renderer process using Electron's webContents.send method. In this implementation, two types of messages are sent:

    1. Lifecycle Message: Sent when the window finishes loading (did-finish-load) to provide a timestamp.
    2. 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);
    });