字节笔记本

2026年2月23日

wxread-export 核心源码解析:微信读书笔记导出实现

本文介绍 wxread-export 项目中的核心工具函数 utils.js,该文件实现了微信读书划线笔记和评论的 Markdown 格式导出功能。

项目背景

wxread-export 是一款浏览器扩展工具,用于一键导出微信读书(WeRead)的读书笔记,支持将划线和评论导出为 Markdown 格式,方便用户整理和归档阅读笔记。

核心代码解析

Book 类设计

utils.js 中定义了 Book 类,负责处理书籍笔记数据的解析和格式化:

javascript
class Book {
  constructor(inMarkData, inReviewData) {
    this.book = inMarkData.book;
    this.chapters = inMarkData.chapters;
    this.markByChapterUid = {};
    this.generateMark(inMarkData.updated);
    this.generateReview(inReviewData.reviews);
  }
}

Markdown 导出功能

getText() 方法是核心导出逻辑,将笔记数据格式化为 Markdown:

javascript
getText(type = "markdown") {
  let result = "";
  Object.keys(this.markByChapterUid).forEach((chapterUid) => {
    if (!this.markByChapterUid[chapterUid].marks.length) return;
    if (this.markByChapterUid[chapterUid].title) {
      result += `## ${this.markByChapterUid[chapterUid].title}\n`;
    }
    this.markByChapterUid[chapterUid].marks.forEach((markItem) => {
      if (markItem.reviewText) {
        result += `${markItem.reviewText}\n`;
      }
      if (markItem.markText) {
        result += `>${markItem.markText}\n\n<hr>\n\n`;
      }
    });
  });
  return result;
}

输出格式特点:

  • 章节标题使用 ## 二级标题
  • 评论内容直接输出
  • 划线内容使用 > 引用格式
  • 每条笔记后用 <hr> 分隔线区分

划线笔记处理

generateMark() 方法按章节整理划线笔记,并按位置排序:

javascript
generateMark(marks) {
  for (var i = this.chapters.length - 1; i >= 0; i--) {
    let chapterItem = this.chapters[i];
    this.markByChapterUid[chapterItem.chapterUid] = Object.assign(
      { marks: [] },
      chapterItem
    );
    this.markByChapterUid[chapterItem.chapterUid].marks = marks
      .filter((mark) => mark.chapterUid === chapterItem.chapterUid)
      .sort((a, b) =>
        Number(a.range.split("-")[0]) - Number(b.range.split("-")[0])
      );
  }
}

排序逻辑: 根据 range 字段的起始位置进行数值排序,确保笔记按书中出现顺序排列。

评论数据合并

generateReview() 方法将评论数据与划线笔记关联:

javascript
generateReview(reviews) {
  for (var i = reviews.length - 1; i >= 0; i--) {
    let reviewItem = reviews[i].review;
    // 查找相同位置的划线笔记
    let sameMarksItem = this.markByChapterUid[reviewItem.chapterUid]
      .marks.find((marksItem) => reviewItem.range === marksItem.range);
    if (sameMarksItem) {
      // 合并到现有笔记
      sameMarksItem.reviewText = reviewItem.content;
    } else {
      // 创建新笔记项
      let tmpMarkItem = {
        range: reviewItem.range,
        markText: reviewItem.abstract,
        reviewText: reviewItem.content,
      };
      // ...
    }
  }
}

关联策略:

  • 通过 range 字段匹配评论与划线
  • 若找到匹配的划线,将评论内容合并到同一笔记
  • 若无匹配划线,创建独立评论项

导出入口函数

javascript
export const generateBookMark = (
  inMarkData,
  inReviewData = { reviews: [] }
) => {
  let book = new Book(inMarkData, inReviewData);
  return book.getText();
};

数据结构说明

输入数据格式

划线数据 (markData):

javascript
{
  book: { bookId, title, author },
  chapters: [{ chapterUid, title }],
  updated: [{ chapterUid, range, markText }]
}

评论数据 (reviewData):

javascript
{
  reviews: [{
    review: {
      chapterUid,
      chapterTitle,
      range,
      abstract,  // 引用的原文
      content    // 评论内容
    }
  }]
}

技术亮点

  1. 模块化设计:使用 ES6 Class 封装书籍数据处理逻辑
  2. 数据关联:通过 range 字段实现划线与评论的精准匹配
  3. 排序处理:按书中位置排序,保持阅读顺序
  4. 格式规范:生成标准 Markdown,便于后续编辑和分享

项目链接

分享: