如何获取预加载的 AppData 目录?
背景.js
[...]
async function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__static, "preload.js"),
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION
},
})
}
[...]
preload.js
const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld(
'configManager',
require("../src/utils/config-manager")
)
config-manager.js
const app = require("electron").app
const fs = require("fs")
const resourcePath = app.getPath('appData').replaceAll("\", "/") + "my-custom-path" // <---
const configPath = resourcePath + "config.json"
const defaultConfig = [ ... ]
let config;
function createFilesIfNotExists(){
if (!fs.existsSync(resourcePath))
fs.mkdirSync(resourcePath)
if (!fs.existsSync(configPath)){
fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 4))
return true
}
return false
}
module.exports = {
loadConfig() {
createFilesIfNotExists()
[...]
return config
}
}
如果我运行这个,我会收到此错误。
TypeError: Cannot read property 'getPath' of undefined
at Object.<anonymous> (VM77 config-manager.js:3)
at Object.<anonymous> (VM77 config-manager.js:65)
at Module._compile (VM43 loader.js:1078)
at Object.Module._extensions..js (VM43 loader.js:1108)
at Module.load (VM43 loader.js:935)
at Module._load (VM43 loader.js:776)
at Function.f._load (VM70 asar_bundle.js:5)
at Function.o._load (VM75 renderer_init.js:33)
at Module.require (VM43 loader.js:959)
at require (VM50 helpers.js:88)
(anonymous) @ VM75 renderer_init.js:93
我认为发生这种情况是因为“app”稍后初始化。
我的最终目标是从 AppData 目录中读取 json 配置。
如果有更好的方法来做到这一点,请随时告诉我。
用户不必能够在运行时更改配置。但我必须能够将 defaultConfig 中的默认值写入配置文件中。
app.getPath()方法仅在应用程序“就绪”后才可用。使用app.on('ready' () => { ... });检测 'ready' 事件。有关更多信息,请参阅 Electron 的事件:'ready' 事件。关于您的
preload.js脚本,直接在其中包含函数有时会使内容难以阅读和理解(即使它仅由require实现)。目前,该文件没有关注点分离。 IE:您的“配置”功能混合在preload脚本中。如果您希望分离问题,那么您应该从preload.js文件中重构您的“配置”代码,并将其放在自己的文件中。这样,您的preload.js文件仅用于配置 IPC 通道和传输关联数据(如果有)。好吧,让我们看看如何解决
app.getPath('appData')问题。在您的
main.js文件中,检测您的应用程序何时“就绪”,然后通过您的config-manager.js文件获取appData目录。main.js(主线程)const electronApp = require('electron').app; const electronBrowserWindow = require('electron').BrowserWindow; let appConfig = require('config-manager'); let appMainWindow = require('mainWindow'); let mainWindow; app.whenReady().then(() => { // Load the config. let configStatus = appConfig.loadConfig(); console.log(configStatus); let config = appConfig.getConfig(); console.log(config); // Create your main window. mainWindow = appMainWindow.create() ... }) })在您的
config-manager.js文件中,我已将您的“路径”变量移至loadConfig()函数范围,因为它们仅由该函数使用。如果您需要将它们公开以供文件中其他位置使用,则需要将它们移回loadConfig()函数作用域之外。我将对
ElectronApp.getPath('appData')的引用移至loadConfig()函数中,因为在应用程序“就绪”后从main.js调用此函数。我添加了辅助函数
pathExists()因为它的实现被多次使用。最后,我添加了
getConfig()函数,以便在需要时从应用程序主线程中的任何位置轻松获取配置对象(只要将其包含在需要使用它的文件中即可)。IE:let appConfig = require('config-manager')。config-manager.js(主线程)const electronApp = require("electron").app const nodeFs = require("fs") const defaultConfig = [ ... ]; let config; function loadConfig() { let resourcePath = app.getPath('appData').replaceAll("\", "/") + "my-custom-path"; let configPath = resourcePath + "config.json"; if (! pathexists(resourcePath)) { nodeFs.mkdirSync(resourcePath); } if (! pathexists(configPath)) { nodeFs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 4)); config = defaultConfig; } else { config = JSON.parse(nodeFs.readFileSync(configPath , 'utf8')); }; } function getConfig() { return config; } function pathExists(path) { return (fs.existsSync(path)) ? true : false; } module.exports = {loadConfig, getConfig}典型的
preload.js脚本看起来像这样。const contextBridge = require('electron').contextBridge; const ipcRenderer = require('electron').ipcRenderer; // White-listed channels. const ipc = { 'render': { // From render to main. 'send': [ 'config:updateConfig' // Example only ], // From main to render. 'receive': [ 'config:showConfig' // Exmaple only ], // From render to main and back again. 'sendReceive': [] } }; contextBridge.exposeInMainWorld( // Allowed 'ipcRenderer' methods. 'ipcRender', { // From render to main. send: (channel, args) => { let validChannels = ipc.render.send; if (validChannels.includes(channel)) { ipcRenderer.send(channel, args); } }, // From main to render. receive: (channel, listener) => { let validChannels = ipc.render.receive; if (validChannels.includes(channel)) { // Deliberately strip event as it includes `sender` ipcRenderer.on(channel, (event, ...args) => listener(...args)); } }, // From render to main and back again. invoke: (channel, args) => { let validChannels = ipc.render.sendReceive; if (validChannels.includes(channel)) { return ipcRenderer.invoke(channel, args); } } } );如果您需要帮助来了解 IPC 通道的实现以及如何在主线程或渲染线程中发送/接收它们,那么只需提出一个新问题即可。