web架构师编辑器内容-添加自动保存的功能
对于频繁改动的应用,自动保存的功能是一个非常有用的功能,可以避免用户在没有保存的情况下丢失自己保存过的数据。
对于自动保存,一般有两种实现,参考语雀和石墨:
- 语雀采用的是定时保存的方式,大约在3分半后自动发送一次请求
- 石墨文档采用的是实时保存:对于文本区域,直接输入字符的时候,会发送请求,就是在文本区域onchange的时候,绑定一个事件,有新内容介入的时候发送请求。
这里我们可以采用第一种方法,定时发送请求。采用这种方式之前我们可以进行一个优化:为了避免一股脑的发送请求,比如说如果用户没有做任何操作,到时候还是会发送请求,所以呢,我们要给他添加一个特殊字段isDirty,这个字段可以标记一次数据是否有修改,加入有修改,才进行保存,这里采用的方式和做历史数据功能的方式类似,不会采用对比新旧数据比较耗时的方法,而是触发特定的mutations才会标记字段,比如说addComponent、deleteComponent、updateComponent、updatPage。
// 特殊字段
isDirty// 在一些 mutation 的时候同时修改该字段
addComponent
deleteComponent
updateComponent
updatePagestate.isDirty = true// 在 save 之后将 isDirty 置为 false
state.isDirty = false
代码实现:添加isDirty字段
// store/editor.ts
export interface EditorProps {// 供中间编辑器渲染的数组components: ComponentData[];// 当前编辑的是哪个元素,uuidcurrentElement: string;// 当然最后保存的时候还有有一些项目信息,这里并没有写出,等做到的时候再补充page: PageData;// 当前被复制的组件copiedComponent?: ComponentData;// 当前操作的历史记录histories: HistoryProps[];// 当前历史记录的操作位置historyIndex: number;// 开始更新时的缓存值cachedOldValues: any;// 保存最多历史条目记录数maxHistoryNumber: number;// 数据是否有修改isDirty: boolean;
}
然后在一系列mutations中给isDirty设置为true,这都是一系列重复的功能,这时候就想到之前使用过的高阶函数处理过浏览器的默认行为:web架构师编辑器内容-快捷键操作的实现,这里我们也可以封装成高阶函数来完成一系列操作,比较方便:
// store/editor.ts
const setDirtyWrapper = (callback: Mutation<EditorProps>) => {return (state: EditorProps, payload: any) => {state.isDirty = true;callback(state, payload);};
};// mutations
// 添加元素
addComponent: setDirtyWrapper((state, component: ComponentData) => {component.layerName = "图层" + (state.components.length + 1);state.components.push(component);pushHistory(state, {id: uuidv4(),componentId: component.id,type: "add",data: cloneDeep(component),});
})// 粘贴元素
pasteCopiedComponent: setDirtyWrapper((state) => {if (state.copiedComponent) {const clone = cloneDeep(state.copiedComponent);clone.id = uuidv4();clone.layerName = clone.layerName + "副本";state.components.push(clone);message.success("已黏贴当前图层", 1);pushHistory(state, {id: uuidv4(),componentId: clone.id,type: "add",data: cloneDeep(clone),});}
})// 删除元素
deleteComponent: setDirtyWrapper((state, id) => {const currentComponent = state.components.find((component) => component.id === id);if (currentComponent) {const currentIndex = state.components.findIndex((component) => component.id === id);state.components = state.components.filter((component) => component.id !== id);pushHistory(state, {id: uuidv4(),componentId: currentComponent.id,type: "delete",data: currentComponent,index: currentIndex,});message.success("删除当前图层成功", 1);}
})//更新元素
updateComponent: setDirtyWrapper((state, { key, value, id, isRoot }: UpdateComponentData) => {const updatedComponent = state.components.find((component) => component.id === (id || state.currentElement));if (updatedComponent) {if (isRoot) {(updatedComponent as any)[key as string] = value;} else {const oldValue = Array.isArray(key)? key.map((key) => updatedComponent.props[key]): updatedComponent.props[key];if (!state.cachedOldValues) {state.cachedOldValues = oldValue;}pushHistoryDebounce(state, { key, value, id });if (Array.isArray(key) && Array.isArray(value)) {key.forEach((keyName, index) => {updatedComponent.props[keyName] = value[index];});} else if (typeof key === "string" && typeof value === "string") {updatedComponent.props[key] = value;}}}}
)
发送完请求后,将isDirty置为false
// store/editor.ts
saveWork(state) {state.isDirty = false;
},
在onMounted生命周期里面增加对于的逻辑:
onMounted(() => {timer = setInterval(() => {if (isDirty.value) {saveWork();}}, 1000 * 50);
});
onUnmounted(() => {clearInterval(timer);
});
相关文章:
web架构师编辑器内容-添加自动保存的功能
对于频繁改动的应用,自动保存的功能是一个非常有用的功能,可以避免用户在没有保存的情况下丢失自己保存过的数据。 对于自动保存,一般有两种实现,参考语雀和石墨: 语雀采用的是定时保存的方式,大约在3分半…...
【Redis】关于它为什么快?使用场景?以及使用方式?为何引入多线程?
目录 1.既然redis那么快,为什么不用它做主数据库,只用它做缓存? 2.Redis 一般在什么场合下使用? 3.redis为什么这么快? 4.Redis为什么要引入了多线程? 1.既然redis那么快,为什么不用它做主数据…...
SpringBoot之JWT登录
JWT JSON Web Token(JSON Web令牌) 是一个开放标准(rfc7519),它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON对象安全地传输信息。此信息可以验证和信任,因为它是数字签名的。jwt可以使用秘密〈使用HNAC算法…...
【备战蓝桥杯】——循环结构
🌈个人主页: Aileen_0v0 🔥热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 💫个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-bFHV3Dz5xMe6d3NB {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…...
【数据分享】1929-2023年全球站点的逐年平均气温数据(Shp\Excel\免费获取)
气象数据是在各项研究中都经常使用的数据,气象指标包括气温、风速、降水、湿度等指标,其中又以气温指标最为常用!说到气温数据,最详细的气温数据是具体到气象监测站点的气温数据!本次我们为大家带来的就是具体到气象监…...
探索Pyecharts关系图绘制技巧:炫酷效果与创意呈现【第42篇—python:Pyecharts水球图】
文章目录 Pyecharts绘制多种炫酷关系网图引言准备工作代码实战1. 基本关系网图2. 自定义节点样式和边样式3. 关系网图的层级结构4. 添加标签和工具提示5. 动态关系网图6. 高级关系网图 - Les Miserables 示例7. 自定义关系网图布局8. 添加背景图9. 3D 关系网图10. 热力关系网图…...
蓝桥杯-循环节长度
两个整数做除法,有时会产生循环小数,其循环部分称为: 循环节。比如,11/136>0.8461553846153..... 其循环节为[846153] 共有 6 位。下面的方法,可以求出循环节的长度。请仔细阅读代码,并填写划线部分缺少的代码。 注…...
Jython调用openwire库连接activemq转发topic订阅消息到另一个activemq 服务器上 完整代码
以下是一个示例代码,演示如何在Jython中使用OpenWire库连接ActiveMQ,将一个主题(topic)上的订阅消息转发到另一个ActiveMQ服务器上: from org.apache.activemq import * from org.apache.activemq.transport import *…...
面试经典题---30.串联所有单词的子串
30.串联所有单词的子串 我的解法: 滑动窗口: 解法中用到了两个哈希表map1和map2,分别用于记录words中各个单词的出现频数和当前滑动窗口[left, right)中单词的出现频数;外部for循环i从0到len - 1,内部while循环每次会…...
字符串随机生成工具(开源)-Kimen(奇门)
由于最近笔者在开发数据脱敏相关功能,其中一类脱敏需求为能够按照指定的格式随机生成一个字符串来代替原有信息,数据看起来格式需要与原数据相同,如:电话号码,身份证号以及邮箱等。在网上搜索了下,发现没有…...
UE4 CustomDepthMobile流程小记
原生UE opaque材质中获取CustomDepth/CustomStencil会报错 在其Compile中调用的函数中没有看到报错逻辑 材质节点的逻辑都没有什么问题,所以看一下报错 在HLSLMaterialTranslator::Translate中 修改之后 mobile流程的不透明材质可以直接获取SceneTexture::customd…...
Docker 基础篇
目录 一、Docker 简介 1. Docker 2. Linux 容器 3. 传统虚拟机和容器的对比 4. Docker 的作用 5. Docker 的基本组成(Docker 三要素) 6. Docker 工作原理 7. Docker 架构 8. Docker 下载 二、Docker 安装 1. CentOS Docker 安装 2. CentOS8 …...
Idea上操作Git回退本地版本,怎么样保留已修改的文件,回退本地版本的四种方式代表什么?
Git的基本概念:Git是一个版本控制系统,用于管理代码的变更历史记录。核心概念包括仓库、分支、提交和合并。 1、可以帮助开发者合并开发的代码 2、如果出现冲突代码的合并,会提示后提交合并代码的开发者,让其解决冲突 3、代码文件版本管理 问题描述 当我们使用git提交代码…...
vue3封装el-pagination分页组件
1、效果如图: 2、分页组件代码: <template><div class"paging"><el-config-provider :locale"zhCn"><el-paginationv-model:current-page"page.currentPage"v-model:page-size"page.pageSize…...
负载均衡下Webshell连接思路及难点
君衍. 一、应用场景二、环境搭建三、思路以及难点1、查看内部结构2、查看webshell3、使用蚁剑进行连接4、难点1 shell文件上传问题5、难点2 命令执行时飘逸6、难点3 大工具上传失败7、难点4 脚本失效 四、解决方式1、关闭对方节点服务器2、基于IP地址判断是否执行3、脚本实现流…...
基于链表实现贪吃蛇游戏
本文中,我们将使用链表和一些Win32 API的知识来实现贪吃蛇小游戏 一、功能 (1)游戏载入界面 (2)地图的绘制 (3)蛇身的移动和变长 (4)食物的生成 (5&…...
Python网络爬虫实战——实验6:Python实现js逆向与加解密
【实验内容】 本实验主要介绍在数据采集过程中对js代码进行分析从而对加密字段进行解密。 【实验目的】 1、理解js逆向工程的概念 2、学会逆向工程中的加解密分析 【实验步骤】 步骤1 理解js逆向工程的概念 步骤2 学会逆向工程中的加解密分析 步骤3 采集广东政府采购网 步…...
【python】使用aiohttp库编写一个简单的异步服务器
1. aiohttp介绍 aiohttp 是一个用于编写异步 HTTP 客户端和服务器的 Python 库。它建立在 Python 的 asyncio 库之上,提供了一种方便的方式来处理异步请求和响应。 官网地址:Welcome to AIOHTTP — aiohttp 3.9.1 documentation 以下是 aiohttp 的一些…...
新手使用代理IP接入代码教程
“实现匿名访问与数据保护在当今互联网高速发展的时代,网络安全和隐私保护成为了越来越重要的议题。代理IP可以隐藏用户的真实IP地址,从而实现匿名访问。为了保护用户的隐私和数据安全,许多网站和应用程序都采用了代理IP技术。” 一、代理IP的…...
JVM问题排查手册
三万字长文:JVM内存问题排查Cookbook 一、Heap快照 # jmap命令保存整个Java堆(在你dump的时间不是事故发生点的时候尤其推荐) jmap -dump:formatb,fileheap.bin <pid> # jmap命令只保存Java堆中的存活对象, 包含live选项,…...
Unity 2021/2019 项目里用 NModbus4.dll 搞定 Modbus TCP 通信(附测试工具和避坑指南)
Unity工业通信实战:用NModbus4实现Modbus TCP全流程开发指南 当游戏引擎遇上工业协议,会碰撞出怎样的火花?三年前接手一个智能制造培训项目时,我首次尝试在Unity中集成Modbus通信。原以为简单的协议对接,却因线程冲突导…...
动态对抗Zygisk-IL2CppDumper:Unity游戏安全新策略
1. 认识Zygisk-IL2CppDumper的攻击原理 如果你开发过Unity游戏,一定对IL2CPP不陌生。这是Unity官方推荐的脚本后端,它把C#代码转换成C代码再编译为本地机器码,相比Mono模式确实安全不少。但最近一年,一个叫Zygisk-IL2CppDumper的工…...
PyTorch 2.5镜像体验:预装全套工具,让AI项目开发效率翻倍
PyTorch 2.5镜像体验:预装全套工具,让AI项目开发效率翻倍 1. 为什么选择预装环境的PyTorch镜像? 深度学习项目开发中,最令人头疼的往往不是算法设计或模型调优,而是环境配置这个看似简单却暗藏玄机的工作。想象一下这…...
单细胞分析进阶:手把手教你用hdWGCNA挖掘Treg细胞关键基因模块(附完整代码)
单细胞分析进阶:手把手教你用hdWGCNA挖掘Treg细胞关键基因模块(附完整代码) 在免疫微环境中,调节性T细胞(Treg)扮演着维持免疫平衡的关键角色。理解这些细胞的基因共表达网络对于揭示其功能机制至关重要。本…...
Hive与MySQL集成配置全流程解析
1. Hive与MySQL集成的核心价值 在企业级大数据环境中,Hive作为数据仓库工具经常需要处理PB级数据。但默认的Derby元数据库存在单会话限制和性能瓶颈,这正是MySQL大显身手的地方。我经历过多次生产环境迁移,将元数据从Derby切换到MySQL后&…...
终极免费开源图像修复工具:ComfyUI-BrushNet完整使用指南
终极免费开源图像修复工具:ComfyUI-BrushNet完整使用指南 【免费下载链接】ComfyUI-BrushNet ComfyUI BrushNet nodes 项目地址: https://gitcode.com/gh_mirrors/co/ComfyUI-BrushNet 想要快速掌握AI图像编辑的核心技术?ComfyUI-BrushNet作为一款…...
微信群消息监控系统进阶:如何用dataclass优化配置管理并实现热更新
微信群消息监控系统进阶:如何用dataclass优化配置管理并实现热更新 在开发长期运行的微信消息监控系统时,配置管理往往是后期维护的痛点。许多开发者初期会选择简单的字典或JSON文件存储配置,但随着功能迭代,硬编码的配置项、散落…...
Vue 过滤器详解及 Vue 3 中的替代方案
Vue 过滤器详解及 Vue 3 中的替代方案 一、Vue 过滤器的核心概念与特性 Vue 过滤器(Filter)是 Vue 2.x 提供的用于数据格式化转换的机制,其核心设计理念是不修改原始数据,仅对显示层进行格式化处理。过滤器本质上是纯函数…...
STM32实战指南_基于STM32F103的智能交通灯系统设计与实现(硬件+软件+调试)
1. 项目背景与需求分析 十字路口的交通拥堵是城市治理的经典难题。传统定时切换的交通灯就像个固执的老头子,不管车多车少都按固定节奏工作,经常出现一边排长龙、另一边空荡荡的尴尬场景。这次我们要用STM32F103这颗"最强大脑"给交通灯装上&qu…...
告别996!用Google Antigravity的Agent-First模式,5分钟搞定React Native与Android原生桥接模块
告别996!用Google Antigravity的Agent-First模式,5分钟搞定React Native与Android原生桥接模块 如果你是一位长期奋战在Android与React Native混合开发一线的工程师,一定对"桥接模块"这个词汇又爱又恨。每当产品经理提出"我们…...
