即使在 webpack.DefinePlugin 中设置了环境变量,也没有在电子中定义
Posted
技术标签:
【中文标题】即使在 webpack.DefinePlugin 中设置了环境变量,也没有在电子中定义【英文标题】:Environment variable is undefined in electron even it has been set inside webpack.DefinePlugin 【发布时间】:2018-07-17 14:18:19 【问题描述】:我有一个要求,我们需要根据它是在生产环境中还是在开发环境中执行来设置 dll 路径。所以我决定将该值放在环境变量中,并尝试使用 webpack.DefinePlugin() 来实现。
方法一:
webpack.config.json
plugins: [
new webpack.DefinePlugin(
'process.env.NODE_ENV' : JSON.stringify('production')
)
然后我尝试在电子的主进程中获取该值,在我的例子中是 elec.js
elec.js
const Electron = require("electron");
const app = require("electron");
var dllPath = "";
function createWindow()
let win = new BrowserWindow(
width: 800,
height: 600,
title: "Test",
icon: "Test.ico"
);
win.setMenu(null);
win.loadURL(
url.format(
pathname: path.join(__dirname, "../renderer/index.html"),
protocol: "file:",
slashes: true
)
);
if (process.env.NODE_ENV ==='production')
dllPath = path.join(
__dirname,
"./../../dll/test.dll"
);
else
dllPath = path.join(
__dirname,
"./../../../dll/test.dll"
);
app.on("ready", createWindow);
但问题是,当我尝试在 createWindow() 函数中访问该值时,它是未定义的,因此流程总是转到 else 块。
我有什么遗漏吗?
方法二:
我尝试使用跨环境节点包来实现相同的目标,但没有运气。请在下面找到我尝试使用 cross-env 的代码块。
package.json
"scripts":
"build": "cross-env process.env.NODE_ENV=production && rimraf ./dist/ && webpack --progress && node-sass
./src/renderer/scss/ -o ./dist/renderer/ && rimraf ./dist/renderer/includes/"
【问题讨论】:
【参考方案1】:在您的控制台中
对于 Windows 设置 MY_VARIABLE=true
对于 Linux $ export MY_VARIABLE=true
window.process.env.MY_VARIABLE
【讨论】:
【参考方案2】:iva2k 提供的洞察力使我能够为同样的问题找到解决方案。
使用dotenv 为我的配置创建一个.env
文件使我达到了我想要的一半(设置一些环境变量以用于生产设置)。然后问题就变成了 Electron 默认没有将那些从 Main 进程传递到 Renderer 进程。
解决方法很简单:使用 Electron 自己的 ipcMain
和 ipcRenderer
模块在两者之间传递 dotenv 对象。
在您的主文件(例如您的 elec.js 文件)中,在需要模块后放置一个 ipcMain
事件监听器:
const config = require('dotenv').config();
const electron = require('electron');
const app, BrowserWindow, ipcMain = electron;
...
ipcMain.on('get-env', (event) =>
event.sender.send('get-env-reply', config);
);
在应用程序的渲染端,将其放置在任何需要的地方:
async function getConfig()
const ipcRenderer = window.require('electron');
let config = null;
ipcRenderer.on('get-env-reply', (event, arg) =>
// The dotenv config object should return an object with
// another object inside caled "parsed". Change this if need be.
config = arg.parsed;
);
ipcRenderer.send('get-env');
return config;
这基本上允许我在主进程文件中声明一个事件,然后在我想要的任何进程端文件中重新使用它,从而允许我在与构建一起的文件中混淆配置变量,但不是在不打开开发工具的情况下,最终用户无法访问。
【讨论】:
【参考方案3】:也许晚了,但可以在 elec.js 中使用简单的 hack
const isProduction = process.env.NODE_ENV === 'production' || (!process || !process.env || !process.env.NODE_ENV);
【讨论】:
【参考方案4】:问题是多方面的。
首先,您的 elec.js 在应用加载之前由 Electron 执行。 Electron 运行 elec.js,它创建浏览器窗口 (let win = new BrowserWindow(...)
) 并将 HTML 文件 (win.loadURL(...)
) 加载到浏览器进程中,然后 HTML 加载您的 webpack 的 js。所以在 elec.js 中没有任何 webpacked js 代码可用。 webpack 的代码也在 elec.js 之外的另一个进程中运行。
另外需要注意的是,webpack 插件也不会为它所指向的变量创建任何赋值。它是通过简单的文本搜索和替换来完成的,在您的示例中,process.env.NODE_ENV 的所有实例都将替换为 webpack'ed 源代码中的“生产”字符串。这不是太明显,但会破坏预期的结果。
最后一件事 - webpack 插件不会更改 elec.js 文件中的任何代码,因为该文件不是 webpack'ed。
因此,所有这些都使得构建/webpack 时间的 process.env.NODE_ENV 在 elec.js 代码中不可用。
一旦机制明确,解决问题的方法很少,我将给出一般性的想法,因为每个都有很多讨论,并且根据情况和所需的用例,有些比其他更好:
在构建期间根据环境变量生成一个带有必要分配的 js 文件(例如,复制一个 env-prod.js / env-dev.js -> env.js),将其复制到 elec.js 旁边, 并在 elec.js 中引用它 (require(env.js)
)。
从命令行传递环境变量(例如NODE_ENV=1 electron .
) - 它将到达 elec.js。
根据环境变量将文件包含到 webpack 中(例如,复制 env-prod.js / env-dev.js -> env.js 之一)并从 elec.js 中查看 webpacked 文件,例如使用asar
命令。
根据构建在 package.json 中使用不同的版本(例如,版本:“1.0.0-DEBUG”用于调试),并通过调用 elec.js 中的 app.getVersion() 来读取和解析它。这很棘手,因为 package.json 应该是单个文件,但可以使用操作系统命令(例如在“脚本”中)在调用 npm 之前复制准备好的 package.json 文件之一。
这里有一些链接也可以提供帮助:
Electron issue #7714 - Electron 相关特性的讨论
electron-is-dev - 模块检查它是否在开发中
Electron boilerplate - 使用 config/env-prod/dev 文件的示例样板
【讨论】:
以上是关于即使在 webpack.DefinePlugin 中设置了环境变量,也没有在电子中定义的主要内容,如果未能解决你的问题,请参考以下文章
我在 webpack.DefinePlugin 中定义的变量未定义
使用来自 WebPack.DefinePlugin 的全局注入变量进行 NodeJS Mocha 单元测试
什么是用于 create-react-app 的 webpack.DefinePlugin 的良好替代品?