网页笔记插件(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)。

相关推荐
左左右右左右摇晃8 小时前
计算机网络笔记整理
笔记·计算机网络
不吃西红柿的858 小时前
[职场] 内容运营求职简历范文 #笔记#职场发展
笔记·职场和发展·内容运营
似水明俊德9 小时前
02-C#.Net-反射-学习笔记
开发语言·笔记·学习·c#·.net
智者知已应修善业9 小时前
【51单片机独立按键控制数码管移动反向,2片74CH573/74CH273段和位,按键按下保持原状态】2023-3-25
经验分享·笔记·单片机·嵌入式硬件·算法·51单片机
C羊驼10 小时前
C语言:两天打鱼,三天晒网
c语言·经验分享·笔记·算法·青少年编程
sheeta199810 小时前
苍穹外卖Day04笔记
笔记
闲猫13 小时前
Linux 历史命令(history)
linux·运维·chrome
今儿敲了吗14 小时前
46| FBI树
数据结构·c++·笔记·学习·算法
苦瓜小生14 小时前
【黑马点评学习笔记 | 实战篇 】| 6-Redis消息队列
redis·笔记·后端
Chengbei1115 小时前
Chrome浏览器渗透利器支持原生扫描!JS 端点 + 敏感目录 + 原型污染自动化检测|VulnRadar
javascript·chrome·安全·web安全·网络安全·自动化·系统安全