Webpack 插件

使用 webpack 轉換和打包您的 Electron Forge 應用程式程式碼。

此插件可讓您輕鬆設定標準的 webpack 工具,以編譯您的主進程程式碼和渲染器進程程式碼,並內建支援渲染器進程中的熱模組替換 (HMR),以及支援多個渲染器。

安裝

npm install --save-dev @electron-forge/plugin-webpack

使用方式

插件設定

您必須提供兩個 webpack 設定檔:一個用於主進程,位於 mainConfig 中,另一個用於渲染器進程,位於 renderer.config 中。完整的設定選項可在 API 文件中 WebpackPluginConfig 底下找到。

例如,以下是從 Forge 的 webpack 範本取得的 設定

module.exports = {
  // ...
  plugins: [
    {
      name: '@electron-forge/plugin-webpack',
      config: {
        mainConfig: './webpack.main.config.js',
        renderer: {
          config: './webpack.renderer.config.js',
          entryPoints: [{
            name: 'main_window',
            html: './src/renderer/index.html',
            js: './src/renderer/index.js',
            preload: {
              js: './src/preload.js'
            }
          }]
        }
      }
    }
  ]
  // ...
};

專案檔案

此插件會為主進程以及每個渲染器進程和預載腳本產生單獨的入口點。

您需要在專案檔案中執行兩項操作才能使此插件正常運作。

package.json

首先,您的 package.json 檔案中的 main 條目需要指向 "./.webpack/main",如下所示

package.json
{
  "name": "my-app",
  "main": "./.webpack/main",
  // ...
}

主進程程式碼

其次,所有 loadURLpreload 路徑需要引用此插件將為您定義的魔術全域變數。

每個入口點都有兩個全域變數,根據指派給您入口點的名稱定義

  • 渲染器的入口點將附加 _WEBPACK_ENTRY

  • 渲染器的預載腳本將附加 _PRELOAD_WEBPACK_ENTRY

在先前範例中的 main_window 入口點的情況下,全域變數將命名為 MAIN_WINDOW_WEBPACK_ENTRYMAIN_WINDOW_PRELOAD_WEBPACK_ENTRY。以下提供如何使用它們的範例

main.js
const mainWindow = new BrowserWindow({
  webPreferences: {
    preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY
  }
});

mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);

這些變數僅在主進程中定義。如果您需要在渲染器中使用這些路徑之一(例如,將預載腳本傳遞至 <webview> 標籤),您可以使用同步 IPC 來回傳遞魔術變數值。

main.js
// make sure this listener is set before your renderer.js code is called
ipcMain.on('get-preload-path', (e) => {
  e.returnValue = WINDOW_PRELOAD_WEBPACK_ENTRY;
});

與 TypeScript 搭配使用

如果您正在將 webpack 插件與 TypeScript 搭配使用,您將需要手動宣告這些魔術變數,以避免編譯器錯誤。

main.js(主進程)
declare const MAIN_WINDOW_WEBPACK_ENTRY: string;
declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string;

進階設定

webpack-dev-server

Forge 的 webpack 插件使用 webpack-dev-server 來協助您在開發模式下快速迭代渲染器進程程式碼。使用啟用 webpack 插件執行 electron-forge start 將啟動一個開發伺服器,該伺服器可透過插件設定進行配置。

devServer

在開發模式下,您可以透過在您的 Forge Webpack 插件設定中設定 devServer 來變更大部分的 webpack-dev-server 選項。

插件設定
{
  name: '@electron-forge/plugin-webpack',
  config: {
    // other Webpack plugin config...
    devServer: {
      stats: 'verbose'
    }
    // ...
  }
}

devContentSecurityPolicy

在開發模式下,您可以透過在您的 Forge Webpack 插件設定中設定 devContentSecurityPolicy 來設定內容安全策略 (CSP)

{
  name: '@electron-forge/plugin-webpack',
  config: {
    // other Webpack plugin config...
    devContentSecurityPolicy: 'default-src \'self\' \'unsafe-inline\' data:; script-src \'self\' \'unsafe-eval\' \'unsafe-inline\' data:',
    // other Webpack plugin config...
    mainConfig: './webpack.main.config.js',
    renderer: {
      /* renderer config here, see above section */
    }
  }
}

如果您希望在開發中使用原始碼對應,您需要為 script-src 指令設定 'unsafe-eval'。使用 'unsafe-eval' 會導致 Electron 本身在開發人員工具主控台中觸發關於啟用該值的警告,這通常沒有問題,只要您不在生產環境中設定該值即可。

原生 Node 模組

如果您使用 WebpackTypeScript + Webpack 範本來建立您的應用程式,則原生模組大致上會直接正常運作。

如果您正在手動設定外掛程式,您可以透過將以下兩個 loader 新增到 Webpack 設定中的 module.rules 設定中,來使原生模組正常運作。請確保您已安裝 node-loader@vercel/webpack-asset-relocator-loader 作為開發相依性。

npm install --save-dev node-loader @vercel/webpack-asset-relocator-loader@1.7.3

Electron Forge 會對 asset relocator loader 進行 monkeypatch,以使其能與 Electron 正常運作,因此版本已固定以確保相容性。如果您升級該版本,您將自行承擔風險。

webpack.main.config.js
module.exports = {
  module: {
    rules: [
      {
        // We're specifying native_modules in the test because the asset
        // relocator loader generates a "fake" .node file which is really
        // a cjs file.
        test: /native_modules\/.+\.node$/,
        use: 'node-loader'
      },
      {
        test: /\.(m?js|node)$/,
        parser: { amd: false },
        use: {
          loader: '@vercel/webpack-asset-relocator-loader',
          options: {
            outputAssetBase: 'native_modules'
          }
        }
      }
    ]
  }
};

如果 asset relocator loader 無法適用於您的原生模組,您可能需要考慮使用 webpack 的 externals 設定

Node 整合

在您的應用程式碼中啟用 Node 整合

在 Electron 中,您可以使用 BrowserWindow 建構函式選項,在渲染器進程中啟用 Node.js。啟用以下選項的渲染器將擁有類似瀏覽器的 Web 環境,並可存取 Node.js require 和其所有核心 API。

main.js(主進程)
const win = new BrowserWindow({
  webPreferences: {
    contextIsolation: false,
    nodeIntegration: true
  }
});

這會建立一個獨特的環境,需要額外的 webpack 設定。

在您的外掛程式設定中設定正確的 webpack 目標

Webpack targets 對各種 Electron 環境提供一流的支援。Forge 的 webpack 外掛程式會根據設定中的 nodeIntegration 選項,設定渲染器的編譯目標。

  • nodeIntegrationtrue 時,targetelectron-renderer

  • nodeIntegrationfalse 時,targetweb

此選項預設為 false。您可以透過 renderer.nodeIntegration 選項,為所有渲染器設定此選項,並且您可以在您於 entryPoints 陣列中建立的每個渲染器中覆寫其值。

在下面的設定範例中,webpack 將針對除 media_player 之外的所有進入點,編譯成 electron-renderer 目標,而 media_player 將編譯成 web 目標。

插件設定
{
  name: '@electron-forge/plugin-webpack',
  config: {
    mainConfig: './webpack.main.config.js',
    renderer: {
      config: './webpack.renderer.config.js',
      nodeIntegration: true, // Implies `target: 'electron-renderer'` for all entry points
      entryPoints: [
        {
          html: './src/app/app.html',
          js: './src/app/app.tsx',
          name: 'app'
        },
        {
          html: './src/mediaPlayer/index.html',
          js: './src/mediaPlayer/index.tsx',
          name: 'media_player',
          nodeIntegration: false // Overrides the default nodeIntegration set above
        }
      ]
    }
  }
}

重要的是,您需要在主進程程式碼和 webpack 外掛程式設定中同時啟用 nodeIntegration。此選項重複是必要的,因為 webpack 目標在編譯時是固定的,但 BrowserWindow 的 Web 偏好設定是在執行時確定的。

熱模組替換

在開發模式下,由於 webpack-dev-server,您在開發中的所有渲染器進程預設都會啟用 熱模組替換 (HMR)

但是,HMR 無法在預載腳本內運作。不過,webpack 會不斷監看和重新編譯這些檔案,因此請重新載入渲染器以取得預載腳本的更新。

對於主進程,請在您啟動 electron-forge 的主控台中輸入 rs,Forge 會使用新的主進程程式碼為您重新啟動應用程式。

熱重新載入快取

當使用 Webpack 5 快取時,資產權限需要透過它們自己的快取來維護,並且需要將公開路徑注入到建置中。

為了確保這些情況能順利運作,請務必使用 options.outputAssetBase 引數在建置中執行 initAssetCache

const relocateLoader = require('@vercel/webpack-asset-relocator-loader');
webpack({
  // ...
  plugins: [
    {
      apply (compiler) {
        compiler.hooks.compilation.tap('webpack-asset-relocator-loader', compilation => {
          relocateLoader.initAssetCache(compilation, outputAssetBase);
        });
      }
    }
  ]
});

React 的熱重新載入

如果您正在使用 React 元件,您可能希望 HMR 自動擷取變更並重新載入元件,而無需手動重新整理頁面。可以透過安裝 react-hot-loader 來定義應熱重新載入的模組,即可達成此目的。

以下是在 TypeScript 中使用 App 作為 React 元件樹中最頂層元件的用法範例

import { hot } from "react-hot-loader";

const App: FunctionComponent = () => (
  <div>
    ...
  </div>
);

export default hot(module)(App)

您可以根據您想要重新載入的內容,在任何其他元件中使用此模式。例如,如果您將 hot() HOC 用於 AppBar 元件,並變更 AppBar 的子項,則會重新載入整個 AppBar,但較高層級的 App 版面配置則維持不變。本質上,變更將向上傳播到元件樹中找到的第一個 hot() HOC。

在生產環境中會發生什麼事?

理論上,你不需要擔心這個問題。在開發階段,我們會啟動 webpack-dev-server 實例來驅動你的渲染器進程。在生產環境中,我們只會建置靜態檔案。

假設你使用了我們在上一節中解釋的已定義全域變數,當你的應用程式打包後,一切都應該可以正常運作。

我該如何進行虛擬路由?

如果你想使用像是 react-router 這樣的套件在你的應用程式中進行虛擬路由,你需要確保你使用的是不是基於瀏覽器歷史 API 的歷史方法。瀏覽器歷史記錄在開發階段可以運作,但在生產環境中則不行,因為你的程式碼會從檔案系統載入,而不是從網頁伺服器載入。在 react-router 的情況下,你應該使用 MemoryRouter 來使一切正常運作。

最後更新時間