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

相关推荐
嵌入式知行合一19 小时前
时间管理方法论
笔记
儒雅的晴天19 小时前
git笔记
笔记·git
半夏知半秋19 小时前
kcp学习-通用的kcp lua绑定
服务器·开发语言·笔记·后端·学习
我想发发发19 小时前
已经安装了ROS环境却还是报错`ModuleNotFoundError: No module named ‘rclpy‘`
前端·人工智能·chrome·机器人
中屹指纹浏览器20 小时前
指纹浏览器底层沙箱隔离技术实现原理与架构优化
经验分享·笔记
小裕哥略帅21 小时前
PMP知识--五大过程组
笔记·学习
Aliex_git21 小时前
提示词工程学习笔记
人工智能·笔记·学习
0和1的舞者1 天前
力扣hot100-链表专题-刷题笔记(二)
笔记·算法·leetcode·链表·职场和发展
航Hang*1 天前
Photoshop 图形与图像处理技术——第9章:实践训练6——滤镜特效
图像处理·笔记·学习·ui·photoshop
燃犀知不可乎骤得1 天前
MFC学习记录
c++·学习·mfc