Electron中如何在渲染进程间传递持久化全局变量?
MathsTutor 页面数值持久化问题
问题描述
我正在为MathsTutor应用配置持久化变量,用来存储HTML页面生成的数值,确保用户离开页面再返回时能保留这些值。当前用户进入加法等子页面后,点击生成按钮会生成两个随机数,但返回主菜单再回到该页面时,页面会完全重置,无法恢复之前的数字。我尝试用preload脚本通过contextBridge暴露全局变量来长期存储数值,但这个方案没生效。
核心代码片段
main.js
const {app, BrowserWindow, ipcMain, Menu} = require('electron'); const url = require('url'); const path = require('path'); let mainWindow; const createWindow = () => { // 创建窗口 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(); // 加载HTML到窗口 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'); // 初始化文档变量 // 随机数生成器区域 const digitSelector = document.getElementById('digitSelector'); const randButton = document.getElementById('randomise'); const digitSlider = document.getElementById('digits'); const sliderValue = document.getElementById('sliderValue'); // 题目区域 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'); // 关闭应用按钮 const closeApp = document.getElementById('closeApp'); // 获取页面标题 const operation = document.title; let v1; let v2; // 用于验证答案的正确结果 let total; // 生成指定位数的随机整数 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 = ''; } // 主要完成五项操作: // 随机生成页面显示的题目中的两个数字 // 重置答案提示信息并清空答题框 // 设置total为当前题目的正确答案 // 将题目框从顶部横幅后移至可见区域 // 将随机数生成器区域移至屏幕右侧 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; } } // 点击生成按钮时执行randomiseNumbers 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"> <!-- 随机数生成器盒子 --> <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> <!-- 题目盒子(初始位于屏幕外上方) --> <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>
问题原因及解决方案
原因分析
你用contextBridge暴露的integers对象是页面级全局变量,当切换页面(从加法页返回主菜单再回来)时,整个页面会重新加载,preload脚本也会重新执行,integers对象会被重置为初始值(m1、m2、a1、a2都回到0),所以之前存储的值会丢失。
解决方案
要实现跨页面的持久化,需要把数据存在主进程或者本地存储中,以下是两种可行方案:
方案1:用Electron的IPC在主进程存储数据
主进程的变量不会随页面刷新而重置,通过IPC通信让渲染进程读写主进程的存储:
- 修改main.js,添加存储变量和IPC监听:
// 在main.js顶部定义存储变量 let persistedNumbers = { a1: 0, a2: 0, m1: 0, m2: 0 }; // 监听渲染进程的存储请求 ipcMain.on('save-numbers', (event, data) => { Object.assign(persistedNumbers, data); }); // 监听渲染进程的读取请求 ipcMain.handle('get-numbers', () => { return persistedNumbers; });
- 修改preload.js,暴露IPC方法给渲染进程:
const {contextBridge, ipcRenderer} = require('electron'); contextBridge.exposeInMainWorld('numberStorage', { save: (data) => ipcRenderer.send('save-numbers', data), get: () => ipcRenderer.invoke('get-numbers') });
- 修改generator.js中的存储和读取逻辑:
- 存储部分:
// 替换原来的window.integers赋值 if (operation === "Addition") { total = v1+v2; window.numberStorage.save({a1: v1, a2: v2}); } else if (operation === "Multiplication") { total = v1*v2; window.numberStorage.save({m1: v1, m2: v2}); }
- 读取部分:
// 替换原来的初始化判断 window.numberStorage.get().then(stored => { if (operation === "Addition" && stored.a1 !== 0) { v1 = stored.a1; v2 = stored.a2; total = v1+v2; makeAnswerable(); } else if (operation === "Multiplication" && stored.m1 !== 0) { v1 = stored.m1; v2 = stored.m2; total = v1*v2; makeAnswerable(); } });
方案2:用浏览器本地存储(localStorage)
这种方案更简单,直接用浏览器的localStorage存储数据,不需要主进程参与:
- 修改generator.js中的存储和读取逻辑:
- 存储部分:
if (operation === "Addition") { total = v1+v2; localStorage.setItem('additionNumbers', JSON.stringify({a1: v1, a2: v2})); } else if (operation === "Multiplication") { total = v1*v2; localStorage.setItem('multiplicationNumbers', JSON.stringify({m1: v1, m2: v2})); }
- 读取部分:
if (operation === "Addition") { const stored = JSON.parse(localStorage.getItem('additionNumbers')); if (stored && stored.a1 !== 0) { v1 = stored.a1; v2 = stored.a2; total = v1+v2; makeAnswerable(); } } else if (operation === "Multiplication") { const stored = JSON.parse(localStorage.getItem('multiplicationNumbers')); if (stored && stored.m1 !== 0) { v1 = stored.m1; v2 = stored.m2; total = v1*v2; makeAnswerable(); } }
两种方案都能实现跨页面持久化:主进程存储的优势是数据完全由应用控制,不会被浏览器缓存清理影响;localStorage的优势是实现简单,默认支持跨会话持久化(关闭应用再打开也保留数据)。
内容的提问来源于stack exchange,提问作者proshanto-c
相关产品推荐
相关产品推荐

