Appearance
Webpack 插件
使用 webpack 转换和捆绑 Electron Forge 应用程序的代码。
此插件可以轻松设置标准 webpack 工具来编译主进程代码和渲染器进程代码,并在渲染器进程中内置对热模块替换 (HMR) 的支持,并支持多个渲染器。
安装
bash
npm install --save-dev @electron-forge/plugin-webpack
用法
插件配置
您必须提供两个 webpack 配置文件:一个用于mainConfig
中的主进程,另一个用于renderer.config
中的渲染器进程。完整的配置选项可在WebpackPluginConfig
查看.
例如,这是从 Forge 的 webpack 模板中获取的配置:
js
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'
}
}]
}
}
}
]
// ...
};
json
{
// ...
"config": {
"forge": {
"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
json
{
"name": "my-app",
"main": "./.webpack/main",
// ...
}
主流程代码
其次,所有loadURL
和preload
路径都需要引用该插件将为您定义的神奇全局变量。
每个入口点都有两个基于分配给入口点的名称定义的全局变量:
- 渲染器的入口点将带有后缀
_WEBPACK_ENTRY
- 渲染器的预加载脚本将以
_PRELOAD_WEBPACK_ENTRY
对于前面示例中的main_window
入口点,全局变量将命名为MAIN_WINDOW_WEBPACK_ENTRY
和MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY
。下面给出了如何使用它们的示例:
main.js
js
const mainWindow = new BrowserWindow({
webPreferences: {
preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY
}
});
mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);
这些变量仅在主流程中定义。如果需要在渲染器中使用这些路径之一(例如,将预加载脚本传递给<webview>
标记),则可以通过同步IPC往返传递魔术变量值。
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;
});
js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electron', {
getPreloadPath: () => ipcRenderer.sendSync('get-preload-path')
});
js
const preloadPath = window.electron.getPreloadPath();
TIP
与 TypeScript 一起使用
如果你在 TypeScript 中使用 webpack 插件,则需要手动声明这些魔术变量以避免编译器错误
main.js 主进程
js
declare const MAIN_WINDOW_WEBPACK_ENTRY: string;
declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string;
高级配置
webpack-dev-server
Forge 的 webpack 插件使用webpack-dev-server
帮助您在 Development 模式下快速迭代 Renderer 进程代码。在 webpack 插件处于活动状态的情况下运行electron-forge start
将启动一个可通过插件配置配置的开发服务器。
devServer
在开发模式下,您可以通过在 Forge Webpack 插件配置中设置devServer
来更改大多数webpack-dev-server
选项。
插件配置
js
{
name: '@electron-forge/plugin-webpack',
config: {
// other Webpack plugin config...
devServer: {
stats: 'verbose'
}
// ...
}
}
devContentSecurityPolicy (开发内容安全策略)
在开发模式下,您可以通过在 Forge Webpack 插件配置中设置devContentSecurityPolicy
来设置内容安全策略 (CSP)。
js
{
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 */
}
}
}
TIP
如果希望在开发中使用源映射,则需要为script-src
指令设置unsafe eval
。使用unsafe-eval
将导致Electron本身在DevTools控制台中触发关于启用该值的警告,只要您不在生产中设置该值,这通常是正常的。
原生 Node 模块
如果您使用 Webpack 或 TypeScript + Webpack 模板来创建应用程序,则原生模块大多是开箱即用的。
如果你手动设置插件,你可以通过将以下两个加载器添加到 Webpack 配置中的配置中来使原生模块工作。确保同时安装module.rulesnode-loader
和@vercel/webpack-asset-relocator-loader
作为开发依赖项。
bash
npm install --save-dev node-loader @vercel/webpack-asset-relocator-loader@1.7.3
TIP
Electron Forge 对资产重定向器加载器进行 monkeypatch,以便它与 Electron 正确工作,因此版本已被固定以确保兼容性。如果您升级该版本,则风险自负。
webpack.main.config.js
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 中,Node.js你可以使用BrowserWindow
构造函数选项。启用了以下选项的渲染器将具有一个类似于浏览器的 Web 环境,可以访问 Node.js的require
函数及其所有核心 API:
main.js(主进程)
js
const win = new BrowserWindow({
webPreferences: {
contextIsolation: false,
nodeIntegration: true
}
});
这将创建一个需要额外 webpack 配置的独特环境。
在插件配置中设置正确的 webpack 目标
Webpack 目标对各种 Electron 环境具有一流的支持。Forge 的 webpack 插件将根据配置中的nodeIntegration
选项为渲染器设置编译目标。
- 当
nodeIntegration
为true
时,target
为electron-renderer
。 - 当
nodeIntegration
为false
时,target
为web
。
默认情况下,此选项为false
。您可以通过renderer.nodeIntegration
选项为所有渲染器设置此选项,并且可以在数组entryPoints
中创建的每个渲染器中覆盖其值。
在下面的配置示例中,webpack将编译为所有入口点的electron-renderer
目标,media_player
除外,它将编译为web
目标。
插件配置
js
{
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
}
]
}
}
}
TIP
在主流程代码和 webpack 插件配置中启用nodeIntegration
非常重要。此选项复制是必要的,因为 webpack 目标在编译时是固定的,但 BrowserWindow 的 Web 首选项是在运行时确定的。
热模块更换
在开发模式下,由于webpack-dev-server
,所有正在开发的渲染器进程都将默认启用热模块替换 (HMR)。
但是,HMR 不可能在预加载脚本中工作。但是,webpack 会不断监视和重新编译这些文件,因此请重新加载渲染器以获取预加载脚本的更新。
对于主进程,键入rs
您从中启动的控制台,Forge 将使用新的electron-forge
主进程代码为您重新启动应用程序。
热重载缓存
使用 Webpack 5 缓存时,需要通过自己的缓存来维护资源权限,并且需要将公共路径注入到构建中。
要确保这些情况有效,请确保在构建中运行initAssetCache
,options.outputAssetBase
参数为:
js
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 组件树中最顶层的组件:
js
import { hot } from "react-hot-loader";
const App: FunctionComponent = () => (
<div>
...
</div>
);
export default hot(module)(App)
您可以在任何其他组件中使用此模式,具体取决于您要重新加载的内容。例如,如果对AppBar
组件使用hot()
HOC并更改AppBar
的子项,则会重新加载整个AppBar
组件,但更高级别的App
布局将保持不变。从本质上讲,更改将传播到组件树中找到的第一个hot()
HOC。
生产中会发生什么?
理论上,您不需要关心。在开发过程中,我们会启动webpack-dev-server
实例来支持您的渲染器进程。在生产环境中,我们只构建静态文件。
假设你使用我们在上一节中解释的已定义全局变量,那么当你的应用程序被打包时,一切都应该可以正常工作。
如何进行虚拟路由?
如果你想使用类似react-router
要在应用程序中执行虚拟路由,您需要确保使用不基于浏览器历史记录 API 的 history 方法。浏览器历史记录将在开发中有效,但在生产中无效,因为您的代码将从文件系统加载,而不是从 Web 服务器加载。在这种情况下,您应该使用react-router``MemoryRouter
让一切正常。