搜尋

首頁  >  問答  >  主體

Electron 在預先載入時取得 AppData

如何取得預先載入的 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 中的預設值寫入設定檔中。

P粉351138462P粉351138462225 天前555

全部回覆(1)我來回復

  • P粉569205478

    P粉5692054782024-04-07 00:44:04

    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 通道的實作以及如何在主執行緒或渲染執行緒中發送/接收它們,那麼只需提出一個新問題即可。

    回覆
    0
  • 取消回覆