有没有办法在Electron中的渲染器之间传递一个持久的全局变量?

mu0hgdu0  于 2022-05-15  发布在  Electron
关注(0)|答案(1)|浏览(321)

我正在尝试为我的MathsTutor应用程序设置一个持久变量,该变量允许我存储HTML页面中生成的数值,即使在用户离开和返回之后也是如此。我的完整脚本可以在我的github(https://github.com/proshanto-c/MathsTutor)上找到,如果它能帮助阅读它。通常发生的是,当一个人进入其中一个子页面时,比如说添加,他们点击生成按钮,它会生成两个随机数字,数字会从屏幕上方下拉。现在,当用户返回到主菜单,然后回到页面时,页面完全重置,并且没有办法找回他们之前的数字。我希望数字保持持久性,我一直在尝试使用一个预加载脚本,该脚本使用contextBridge公开全局变量,这些变量长期保存值,但一直不起作用。
我的项目被分成了几个渲染文件,所以我会试着只保留相关的部分:
main.js:

const {app, BrowserWindow, ipcMain, Menu} = require('electron');
const url = require('url');
const path = require('path');

let mainWindow;

const createWindow = () => {
    // Create a window
    mainWindow = new BrowserWindow({
        show: false,
        autoHideMenuBar: true,
        webPreferences: ({
            preload: path.join(__dirname, 'scripts', 'preload.js'),
            nodeIntegration: true,
            contextIsolation: false,
        }),
        minWidth: 700,
        minHeight: 400,
    });
    mainWindow.maximize();
    mainWindow.show();

    // Load HTML into window
    mainWindow.loadFile('index.html');
}
...

scripts/preload.js:

const {contextBridge} = require('electron');

contextBridge.exposeInMainWorld('integers', {
    m1: 0,
    m2: 0,
    a1: 0,
    a2: 0,
})

scripts/generator.js:

const {ipcRenderer} = require('electron');

// Initialising document variables

// Randomiser section
const digitSelector = document.getElementById('digitSelector');
const randButton = document.getElementById('randomise');
const digitSlider = document.getElementById('digits');
const sliderValue = document.getElementById('sliderValue');
// Problem section
const problem = document.getElementById('problemBox');
const number1 = document.getElementById('number1');
const number2 = document.getElementById('number2');
const answer = document.getElementById('answer');
const submit = document.getElementById('submit');
const ci = document.getElementById('ci');
// Exit app button
const closeApp = document.getElementById('closeApp');
// Pull the name of the page
const operation = document.title;

let v1;
let v2;
// Total value for checking answers
let total;

// Generate a random number with a set number of digits
const generateRandomInteger = (noOfDigits) => {
...
} 

const makeAnswerable = () => {
    number1.innerText = v1;
    number2.innerText = v2;
    problem.style.visibility = "visible";
    problem.style.opacity = "1";
    problem.style.transform = "translateX(0)";
    digitSelector.style.left = "100%";
    digitSelector.style.transform = "translateX(-100%)";
    ci.innerText = '';
    answer.value = '';
}

// Does five main things:
// Randomise the two numbers in the problem seen on screen
// Reset the answer message and clear the answer box
// Sets the total to the actual expected answer to the problem on screen
// Bring the problem box down from behind the top two banners with the numbers generated
// Move the randomiser section to the right of the screen
const randomiseNumbers = (noOfDigits) => {
    v1 = generateRandomInteger(noOfDigits);
    v2 = generateRandomInteger(noOfDigits);
    makeAnswerable();
    if (operation === "Addition") {
        total = v1+v2;
        window.integers.a1 = v1;
        window.integers.a2 = v2;
    }
    else if (operation === "Multiplication") {
        total = v1*v2;
        window.integers.m1 = v1;
        window.integers.m2 = v2;
    }
}

// Run randomiseNumbers when the generate button is clicked
randButton.addEventListener('click', () => {
    const digits = digitSlider.value;
    randomiseNumbers(digits);
});

...

if (operation === "Addition" && window.integers.a1 !== 0) {
    v1 = window.integers.a1;
    v2 = window.integers.a2;
    total = v1+v2;
    makeAnswerable();
}
else if (operation === "Multiplication" && window.integers.m1 !== 0) {
    v1 = window.integers.m1;
    v2 = window.integers.m2;
    total = v1*v2;
    makeAnswerable();
}

pages/addition.html:multiplication.html相同,index.html只是一个主菜单)

<!DOCTYPE html>
<html lang="en">
    <head>
...
        <title>Addition</title>
    </head>

    <body>
...
        <div class="mainContent">
            <!-- Randomiser box -->
            <div class="digitSelector" id="digitSelector">
                <h3>How many digits would you like the integers to have?</h3>
                <div class="sliderInfo">
                    <input type="range" min="1" max="5" value="3" class="digitSlider" id="digits">
                    <h3 id="sliderValue">3</h3>
                    <button class="randomiser" id="randomise">Generate</button>
                </div>
            </div>
            <!-- Problem box (starts off screen to the top) -->
            <div class="problemBox" id="problemBox">
                <h2>Try adding these numbers together:</h2>
                <div>
                    <h3 id="number1"></h3>
                    <h3 id="number2"></h3>
                    <input type="number" class="number" min="0" step="1" max="1000000" id="answer" name="answer" placeholder="Answer here">
                    <input type="submit" id="submit" value="Check answer">
                    <h3 id="ci"></h3>
                </div>
            </div>
        </div>
    </body>

    <script src="../scripts/generator.js"></script>
</html>
q3aa0525

q3aa05251#

因为您没有创建您的Electron窗口与URL的(例如:win.loadURL()),则不能使用浏览器本地存储来存储您的最新号码。
相反,在生成随机数时,您可以通过IPC将随机数传递给主进程。您的preload.js脚本将有助于此过程。
例如,在本例中,当addition窗口关闭时,这些数字将保留在主进程中。
addition窗口重新打开时(即:重新创建),它可以通过IPC将数字(如果已定义)发送回addition窗口以显示在UI中。
注意:当主窗口关闭时,此示例不会持续。为了持久化,可以在每次更新时将数字保存到json文件中。然后,在应用程序启动时,在创建主窗口之前,将数字加载回主进程局部变量。
在下面的最小可重复示例中,出于测试目的,我在addition.html文件中创建了一个基本的generateNumber()函数。
main.js(主进程)
main.js文件负责创建两个窗口(mainaddition),当从addition窗口接收到持久编号时更新持久编号,并在重新创建addition窗口时将其发送回addition窗口。

// Import required electron modules
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronIpcMain = require('electron').ipcMain;

// Import required Node modules
const nodePath = require('path');

// Prevent garbage collection
let number1;
let number2;

let mainWindow;
let additionWindow;

function createMainWindow() {
    const mainWindow = new electronBrowserWindow({
        x: 0,
        y: 0,
        width: 800,
        height: 600,
        show: false,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            sandbox: true,
            preload: nodePath.join(__dirname, 'preload.js')
        }
    });

    mainWindow.loadFile(nodePath.join(__dirname, 'main-window.html'))
        .then(() => { mainWindow.show(); });

    return mainWindow;
}

function createAdditionWindow() {
    const additionWindow = new electronBrowserWindow({
        x: 150,
        y: 150,
        width: 800,
        height: 600,
        parent: mainWindow, // Make it a child of the main window
        modal: true,        // Make it a child of the main window
        show: false,
        webPreferences: {
            nodeIntegration: false,
            contextIsolation: true,
            sandbox: true,
            preload: nodePath.join(__dirname, 'preload.js')
        }
    });

    additionWindow.loadFile(nodePath.join(__dirname, 'addition-window.html'))
        .then(() => {
            if (number1 !== undefined && number2 !== undefined) {
                additionWindow.webContents.send('previous-numbers', {'number1': number1, 'number2': number2});
            }
        })
        .then(() => { additionWindow.show(); });

    return additionWindow;
}

electronApp.on('ready', () => {
    mainWindow = createMainWindow();
});

electronApp.on('window-all-closed', () => {
    if (process.platform !== 'darwin') {
        electronApp.quit();
    }
});

electronApp.on('activate', () => {
    if (electronBrowserWindow.getAllWindows().length === 0) {
        createMainWindow();
    }
});

// ---

electronIpcMain.on('open-addition-window', () => {
    additionWindow = createAdditionWindow();
})

electronIpcMain.on('update-numbers', (event, data) => {
    number1 = data.number1;
    number2 = data.number2;
})

preload.js(主进程)
preload.js脚本包含主进程和渲染进程之间通信所需的IPC方法。

// Import the necessary Electron modules
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;

// Exposed protected methods in the render process
contextBridge.exposeInMainWorld(
    // Allowed 'ipcRenderer' methods
    'ipcRenderer', {
        // From render to main
        openAdditionWindow: () => {
            ipcRenderer.send('open-addition-window');
        },
        updateNumbers: (data) => {
            ipcRenderer.send('update-numbers', data);
        },
        // From main to render
        previousNumbers: (listener) => {
            ipcRenderer.on('previous-numbers', listener)
        }
    }
);

main-window.html(渲染过程)
包含通过IPC打开addition窗口的按钮的基本窗口。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Main Window</title>
        <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"/>
    </head>

    <body>
        <h1>Main Window</h1>
        <hr>
        <input type="button" id="openAdditionWindow" value="Open Addition Window">
    </body>

    <script>
        // On button click, open the addition window
        document.getElementById('openAdditionWindow').addEventListener('click', () => {
            window.ipcRenderer.openAdditionWindow();
        });
    </script>
</html>

addition-window.html(渲染过程)
当窗口被创建时,如果主进程中有任何持久的数字,它将通过IPC发送它们。一旦收到,本地变量将被更新,然后更新UI中的数字。
当点击Generate Numbers按钮时,会随机生成两个数字,在UI中更新,然后通过IPC发送到主进程进行持久化。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Addition Window</title>
        <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"/>
    </head>

    <body>
        <h1>Addition Window</h1>
        <hr>
        <input type="button" id="generateNumbers" value="Generate Numbers">
        <hr>
        <div>
            <span id="number1"></span>
            <span>&nbsp;</span>
            <span id="number2"></span>
        </div>
    </body>

    <script>
        let number1;
        let number2;

        let numberOneSpan = document.getElementById('number1');
        let numberTwoSpan = document.getElementById('number2');

        // Listen for IPC message from main process to render process
        window.ipcRenderer.previousNumbers((event, data) => {
            number1 = data.number1;
            number2 = data.number2;
            updateNumbers(number1, number2);
        });

        // Listen for click on Generate Numbers button
        document.getElementById('generateNumbers').addEventListener('click', () => {
            number1 = generateNumber();
            number2 = generateNumber();

            updateNumbers(number1, number2);

            window.ipcRenderer.updateNumbers({'number1': number1, 'number2': number2});
        })

        // Generate a random number
        function generateNumber() {
            return Math.floor(Math.random() * 100);
        }

        // Update numbers in UI
        function updateNumbers(number1, number2) {
            numberOneSpan.innerText = number1;
            numberTwoSpan.innerText = number2;
        }
    </script>
</html>

相关问题