网页笔记插件(chrome)开发记录

起因

用过一款浏览器笔记插件,鼠标选中文字后,会弹出按钮,点击按钮可以在对应位置记笔记,保存笔记后,选中的文字会高亮,再次打开该网页仍可以查看高亮文字,以及对应的笔记。

但是使用过程中发现,当笔记太多,页面渲染会变得很慢,插件经常出现崩溃的情况,应该是所有笔记数据都存储在localstorage所致。

因此我准备参照该插件的功能,对存储性能进行优化,开发一个改进版的网页笔记插件。

失败的开发过程

我设想的方案是,当为选中的文字添加笔记以后,将选中文字用<span></span>包裹起来,然后设置文字的背景颜色,从而达到高亮的效果,但是这样存在两个问题:

  1. 会导致原本的文本排版变化
  2. 有些跨dom选中文字的情况无法用该方法

开源项目学习

因为被卡在"选中区域高亮"这一点上,于是在找了一些开源项目,发现context-note这一插件功能比较完备,并且代码公开,遂结合AI对代码进行学习。

选中文本高亮

首先代码注册了一个监听器,用于监听鼠标抬起事件,判断是否有文本被选中:

js 复制代码
// 监听`mouseup`事件判断是否有文本被选中
document.addEventListener("mouseup", (e) => {
  clearLogoIcon();
  // 如果有文本被选中,解析其`rects`和`texts`
  const { rects, texts } = parseRectsAndTextFromSelection();
  const text = getFormattedTextFromTextList(texts);
}

其中parseRectsAndTextFromSelection()先通过递归的方式获取选中元素最内层的文本,然后获取选中文字所占区域,在该区域创建一个div,设置颜色,透明度,遮罩在文本上方,实现高亮的效果。

代码中是通过range.getClientRects()获取选中内容所占区域,自己用下面这个例子测试了一下:

途中带颜色的是选中的文字,该函数返回的区域被圆角矩形框住,可见range.getClientRects()不仅返回了选中文本所在的最内层dom,还返回了其外层dom,因此需要排除外层dom。

js 复制代码
function filterDuplicateRects(rects: Rect[]) {
  const filterRectMap = new Map<string, Rect>();
  for (const rect of rects) {
    const key = `x:${rect.x},y:${rect.y}`;
    if (!filterRectMap.has(key)) {
      filterRectMap.set(key, rect);
    } else {
      const oriRect = filterRectMap.get(key) as Rect;
      if (
        (oriRect.width > rect.width && rect.width > 0) ||
        oriRect.width === 0
      ) {
        filterRectMap.set(key, rect);
      }
    }
  }
  return Array.from(filterRectMap.values());
}

该函数逻辑是:优先保留有宽度且较小的矩形;特殊处理宽度为0的矩形(可能是无效矩形):

  • 初始化Map:创建一个以坐标为键的Map来存储唯一的矩形
  • 遍历矩形数组:
    • 为每个矩形生成键:"x:{x},y:{y}"
    • 如果该坐标不存在,直接添加
    • 如果坐标已存在,进行比较:
      • 如果原矩形宽度 > 新矩形宽度 且 新矩形宽度 > 0,用新矩形替换
      • 或者如果原矩形宽度为0,也用新矩形替换
  • 返回结果:将Map的值转换为数组返回

另外还有一个函数用于排除包含关系,将外部的元素排除,保留内部元素。

保存笔记

该部分有这样一段代码:

javascript 复制代码
 storage.notes = await addItemToArr(StorageKeys.notes, note);

 // make sure the note dom is created
 setTimeout(() => {
   // 2. scroll to the note item
   const divNote = noteDivs.value[noteDivs.value.length - 1]?.$el;
   if (divNote) {
     divNote.scrollIntoView({ block: "center" });
   }

   // 3. focus the content editor of this note
   // make sure above `scrollIntoView` is finished
   setTimeout(() => {
     mitt.emit("focus-editor", note.id);
   });
 });

不太理解此处setTimeout()的作用,而且不设置暂停时间的意义是什么。结合AI的解答,有了以下理解:

setTimeout(callback, delay)中的delay默认是0,回调函数不会立即执行,而是被放入「宏任务队列」,等待当前同步代码执行完毕、调用栈清空后,才会执行。可以确保前面的 DOM 操作、渲染行为先完成,再执行回调函数。

添加笔记使用时,使用的是chrome浏览器本地存储chrome.storage.local.set(),和普通网页localStorage固定5M不同,插件在使用浏览器本地存储时可以用 chrome.storage.local.requestQuota()申请扩容,但是该程序里面并没有这个功能,笔记存储空间为固定的5M。

后续考虑升级扩容申请机制,或者使用存储空间更大的indexDB(100M)。

相关推荐
做cv的小昊6 小时前
计算机图形学:【Games101】学习笔记05——着色(插值、高级纹理映射)与几何(基本表示方法)
笔记·opencv·学习·计算机视觉·图形渲染·几何学
iconball10 小时前
个人用云计算学习笔记 --24 虚拟化、KVM 基础使用与热迁移实验、VMware ESXi笔记
运维·笔记·学习·云计算
齐鲁大虾11 小时前
UOS(统信操作系统)如何更新CUPS(通用Unix打印系统)
linux·服务器·chrome·unix
是小菜呀!11 小时前
基于深度学习的图像检索系统项目实践
笔记
奕辰杰12 小时前
Netty私人学习笔记
笔记·学习·netty·网络通信·nio
De-Alf13 小时前
Megatron-LM学习笔记(6)Megatron Model Attention注意力与MLA
笔记·学习·算法·ai
polarislove021413 小时前
9.2 自制延迟函数-嵌入式铁头山羊STM32笔记
笔记·stm32·嵌入式硬件
愈努力俞幸运13 小时前
chrome 扩展(插件)开发入门教程
前端·chrome
智嵌电子13 小时前
【笔记篇】【硬件基础篇】模拟电子技术基础 (童诗白) 第7章 波形的发生和信号的转换
笔记·嵌入式硬件