electron 网页TodoList应用打包win桌面软件数据持久化
参考:
electron 网页TodoList工具打包成win桌面应用exe
https://blog.csdn.net/weixin_42357472/article/details/140648621
electron直接打包exe应用,打开网页上面添加的task在重启后为空,历史没有被保存,需要持久化工具保存之前的操作记录
这里测试用electron-store或electron-conf两个持久化工具运行都ESM报错:
https://zhuanlan.zhihu.com/p/706281785

解决方法:
直接使用 Node.js 的 fs 模块直接读写 JSON 文件。这样可以避免 ES modules 的问题。
1)electron配置文件
main.js
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')
const fs = require('fs')const STORAGE_PATH = path.join(app.getPath('userData'), 'todos.json')function saveTodos(todos) {fs.writeFileSync(STORAGE_PATH, JSON.stringify(todos))
}function loadTodos() {try {return JSON.parse(fs.readFileSync(STORAGE_PATH, 'utf8'))} catch (error) {return []}
}function createWindow () {const win = new BrowserWindow({width: 800,height: 600,webPreferences: {nodeIntegration: true,contextIsolation: false}})win.loadFile('index.html')
}app.whenReady().then(() => {createWindow()app.on('activate', () => {if (BrowserWindow.getAllWindows().length === 0) {createWindow()}})
})app.on('window-all-closed', () => {if (process.platform !== 'darwin') {app.quit()}
})// 修改这些 IPC 处理器
ipcMain.on('save-todos', (event, todos) => {saveTodos(todos)
})ipcMain.handle('load-todos', () => {return loadTodos()
})
package.json
{"name": "todolist-app","version": "1.0.0","main": "main.js","build": {"appId": "com.yourcompany.todolist","mac": {"category": "public.app-category.productivity"},"win": {"icon": "icons/icon.png","target": ["nsis"]},"linux": {"target": ["AppImage","deb"]}},"scripts": {"start": "electron .","build": "electron-builder --win"},"keywords": [],"author": "","license": "ISC","description": "","devDependencies": {"electron": "^31.2.1","electron-builder": "^24.13.3"},"dependencies": {}
}
2)网页应用
index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>TodoList</title><link rel="stylesheet" href="styles.css">
</head>
<body><h1>TodoList</h1><form id="todo-form"><input type="text" id="todo-input" placeholder="Enter a new task" required><button type="submit" id="add-button">Add</button></form><ul id="todo-list"></ul><script src="script.js"></script>
</body>
</html>
styles.css
body {font-family: Arial, sans-serif;max-width: 500px;margin: 0 auto;padding: 20px;
}
h1 {text-align: center;
}
#todo-form {display: flex;margin-bottom: 20px;
}
#todo-input {flex-grow: 1;padding: 10px;font-size: 16px;border: 1px solid #ddd;border-radius: 4px 0 0 4px;
}
#add-button {padding: 10px 20px;font-size: 16px;background-color: #4CAF50;color: white;border: none;border-radius: 0 4px 4px 0;cursor: pointer;
}
#todo-list {list-style-type: none;padding: 0;
}
.todo-item {display: flex;align-items: center;padding: 10px;background-color: #f9f9f9;border: 1px solid #ddd;margin-bottom: 10px;border-radius: 4px;
}
.todo-item.completed {text-decoration: line-through;opacity: 0.6;
}
.todo-item input[type="checkbox"] {margin-right: 10px;
}
.delete-button {margin-left: auto;background-color: #f44336;color: white;border: none;padding: 5px 10px;border-radius: 4px;cursor: pointer;
}
script.js
const { ipcRenderer } = require('electron')const todoForm = document.getElementById('todo-form')
const todoInput = document.getElementById('todo-input')
const todoList = document.getElementById('todo-list')async function loadTodos() {const todos = await ipcRenderer.invoke('load-todos')todos.forEach(todo => {addTodoToDOM(todo.text, todo.completed)})
}function saveTodos() {const todos = Array.from(todoList.children).map(li => ({text: li.querySelector('span').textContent,completed: li.classList.contains('completed')}))ipcRenderer.send('save-todos', todos)
}function addTodoToDOM(text, completed = false) {const li = document.createElement('li')li.className = 'todo-item' + (completed ? ' completed' : '')li.innerHTML = `<input type="checkbox" ${completed ? 'checked' : ''}><span>${text}</span><button class="delete-button">Delete</button>`li.querySelector('input[type="checkbox"]').addEventListener('change', function() {li.classList.toggle('completed')if (li.classList.contains('completed')) {todoList.appendChild(li)} else {todoList.insertBefore(li, todoList.firstChild)}saveTodos()})li.querySelector('.delete-button').addEventListener('click', function() {li.remove()saveTodos()})if (completed) {todoList.appendChild(li)} else {todoList.insertBefore(li, todoList.firstChild)}
}todoForm.addEventListener('submit', function(e) {e.preventDefault()if (todoInput.value.trim() === '') returnaddTodoToDOM(todoInput.value)saveTodos()todoInput.value = ''
})loadTodos()
打包:
npm run build

完成在dist下生产安装文件,及win-unpacked 无需安装的解压文件


点击exe运行即可

相关文章:
electron 网页TodoList应用打包win桌面软件数据持久化
参考: electron 网页TodoList工具打包成win桌面应用exe https://blog.csdn.net/weixin_42357472/article/details/140648621 electron直接打包exe应用,打开网页上面添加的task在重启后为空,历史没有被保存,需要持久化工具保存之前…...
软件缺陷(Bug)、禅道
目录 软件缺陷的判定标准 软件缺陷的核心内容 构成缺陷的基本要素 缺陷报告 缺陷管理 缺陷的跟踪流程 项目管理工具--禅道 软件在使用过程中存在的任何问题(如:错误、异常等),都叫软件的缺陷,简称bug。 软件缺…...
MySQL客户端命令一节将.sql文件导入MySQL
MySql客户端命令 直接输入SQL语句 使用MySQL客户端连接到服务器之后,可以发送SQL语句到服务器执行,并且以;和\g, \G作为结束不同的结束方式显示内容有所不同** TIPS: ;和\g结尾以表格的形式显示结果\G以行的形式显示结果 在连接到服务器之后…...
[论文笔记] DCA(Dual Chunk Attention)
DCA(Dual Chunk Attention)是一种在自然语言处理模型中用来处理长文本的技术。传统的注意力机制(Attention)在处理长文本时可能会遇到效率和性能瓶颈,因为计算每个单词与其他所有单词之间的关系会随着文本长度的增加而…...
构建查询洞察 UI
本文字数:2631;估计阅读时间:7 分钟 作者:Bucky Schwarz 本文在公众号【ClickHouseInc】首发 我们最近发布了 Query Insights 的初步实现,为 ClickHouse Cloud 用户提供了一种便捷的方法来查看和解释查询日志。该功能对…...
【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十九章 等待队列
i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…...
35.【C语言】详解函数递归
目录: 定义 作用 例子1~3 拓展学习 趣味练习 1.定义:函数自己调用自己(递推回归) int main() {main()return 0; } 这样容易死循环,导致爆栈(Stack Overflow) 所以需要设立限制条件,使执行时越来越接近条…...
【机器学习】智驭未来:机器学习如何重塑制造业的转型与升级
📝个人主页🌹:Eternity._ 🌹🌹期待您的关注 🌹🌹 ❀目录 🔍1. 引言📒2. 机器学习重塑制造业生产流程🌸预测性维护:减少停机时间,提高设…...
Python爬虫(5) --爬取网页视频
文章目录 爬虫爬取视频指定url发送请求UA伪装请求页面 获取想要的数据解析定位定位音视频位置 存放视频完整代码实现总结 爬虫 Python 爬虫是一种自动化工具,用于从互联网上抓取网页数据并提取有用的信息。Python 因其简洁的语法和丰富的库支持(如 requ…...
【Unity】关于Luban的简单使用
最近看了下Luban导出Excel数据的方式,来记录下 【Unity】关于Luban的简单使用 安装Luban开始使用UnityLubanC# 扩展 安装Luban Luban文档:https://luban.doc.code-philosophy.com/docs/beginner/quickstart 1.安装dotnet sdk 8.0或更高版本sdk 2.githu…...
企业公户验证API如何使用JAVA、Python、PHP语言进行应用
在纷繁复杂的金融与商业领域,确保每笔交易的安全与合规是至关重要的。而企业公户验证API,正是这样一位默默守护的数字卫士,它通过智能化的手段,简化了企业对公账户验证流程,让繁琐的审核变得快捷且可靠。 什么是企业公…...
杰发科技Bootloader(2)—— 基于7840的Keil配置地址
序 在7840的sample代码里面有一个简单的Boot跳转APP的示例 PFlash地址从0开始 DFlash的地址从1000000开始 Boot解析 他的boot地址配置为0 Boot的代码主要是这几行,主要作用就是Flash的跳转 int main(void) {SystemClock_Config();InitDebug();printf("demo…...
cmd常用命令
在Windows操作系统中,CMD(Command Prompt)是一个强大的命令行工具,允许用户通过键入命令来执行各种系统级操作。以下是一些常用的CMD命令及其功能: 文件与目录管理 dir:显示当前目录下的文件和子目录列表。…...
PCIe 以太网芯片 RTL8125B 的 spec 和 Linux driver 分析备忘
1,下载 RTL8125B driver 下载页: https://www.realtek.com/Download/List?cate_id584 2,RTL8125B datasheet下载 下载页: https://file.elecfans.com/web2/M00/44/D8/poYBAGKHVriAHnfWADAT6T6hjVk715.pdf3, 编译driver 解压: $ tar xj…...
Python tkinter Menu菜单组件详解
好久没有更新了,今天我来领大家熟悉一下Menu组件 1.认识、了解Menu 什么是Menu menu组件是tkinter中的菜单组件,通过该组件,开发者可以为窗口设计菜单和工具栏等。(ttk还提供了treeview树形菜单,python遍历目录的两种…...
谷粒商城实战笔记-46-商品服务-API-三级分类-配置网关路由与路径重写
文章目录 一,准备工作1,新增一级菜单2,新增二级菜单 二,前端树形界面开发1,开发分类展示组件 三,远程调用接口获取商品分类数据1,远程调用2,路由配置 错误记录 本节的主要内容&#…...
简要了解sql注入
sql注入安全测试中危害 数据库中的数据,对数据库数据进行操作(查询、删除等);网站的权限,找到注入点后可后门写入; sql注入产生原理详细分析 可控变量,带入数据库查询,变量未存在…...
Java 扫雷游戏
程序分析 使用Java编写的扫雷游戏界面程序,主要内容总结如下: Frame类继承自JFrame,构建了扫雷游戏的界面。 包含文本框text、标签nowBomb和setBomb、按钮start、面板MenuPamel和bombPanel等组件。通过jbInit方法进行初始化设置,…...
vue3 命令运行窗口暴露网络地址,以及修改端口号
一般情况下这里的地址是隐藏的 这里加上 --host 可以暴露网络地址,再加上--port --8080 就可以将端口号修改为8080(修改后边的数字就可以修改为你想要的端口号)...
由CANoe自带协议栈在TCP断开连接时同时发送两条FIN报文引起的注意事项
在我写这篇文章CAPL如何在底层模拟TCP Server端断开TCP连接时,我发现了一个奇怪的现象。我为了使用CAPL组装报文的方式实现TCP Server断开连接的过程,插入一个网络节点作为Client端。为了让Client能够发起连接和发起断开连接,给网络节点配置了独立的TCP/IP Stack,也就是CAN…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
2025年#高考 将在近日拉开帷幕,#AI 监考一度冲上热搜。当AI深度融入高考,#时间同步 不再是辅助功能,而是决定AI监考系统成败的“生命线”。 AI亮相2025高考,40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕,江西、…...
基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
