You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QuillJS多独立保存区域实现方案:Blots/容器或Delta解析?

在QuillJS中实现多独立保存区域的两种方案

这其实是Quill开发里挺常见的需求——看起来是一个完整文档,但内部拆成多个可单独保存的单元(比如每个标题对应一块内容)。我分享两种实用思路,你可以根据自己的场景选:

方案一:用自定义Blot包裹独立保存区域(更推荐)

这种方法不需要手动解析整个Delta,而是通过自定义Blot把每个需要独立保存的区域封装成一个带标识的独立对象,后续提取内容会非常直观。

具体步骤:

  1. 注册自定义Blot
    创建一个继承自Block的Blot,用来包裹每个标题+内容的组合,给它加一个唯一标识属性(比如data-section-id):

    class SaveableSection extends Block {
      static blotName = 'saveable-section';
      static tagName = 'div';
      static className = 'saveable-section';
    
      static create(id) {
        const node = super.create();
        node.dataset.sectionId = id;
        return node;
      }
    
      static formats(node) {
        return node.dataset.sectionId;
      }
    }
    
    Quill.register(SaveableSection, true);
    
  2. 构建编辑器内容结构
    在插入标题和对应内容时,把它们放在这个自定义Blot里面。比如通过Quill的API插入:

    // 插入一个带ID的保存区域
    const sectionId = `section-${Date.now()}`;
    quill.insertEmbed(0, 'saveable-section', sectionId);
    // 然后在这个区域里插入标题和内容
    quill.insertText(1, '标题1', { header: 1 });
    quill.insertText(quill.getLength(), '\n这是标题1对应的内容...');
    
  3. 提取每个区域的Delta
    保存时,直接遍历所有自定义Blot,提取每个区域的Delta:

    function saveAllSections() {
      const sections = quill.root.querySelectorAll('.saveable-section');
      const savedData = [];
    
      sections.forEach(section => {
        const sectionId = section.dataset.sectionId;
        // 获取当前区域对应的Quill范围
        const range = quill.getIndex(section);
        const length = quill.getLength(section);
        // 提取该区域的Delta
        const delta = quill.getContents(range, length);
        
        savedData.push({
          sectionId,
          delta,
          title: extractSectionTitle(delta) // 可选:从Delta里提取标题
        });
      });
    
      // 这里调用你的保存接口,把savedData发出去
      console.log('要保存的各区域数据:', savedData);
    }
    

这种方法的好处是结构清晰,每个保存单元和DOM/Blot直接绑定,后续维护、修改区域都很方便,不容易出错。

方案二:解析全局Delta拆分内容

如果不想自定义Blot,也可以直接解析整个编辑器的Delta,通过识别标题块(比如header格式)来拆分出每个独立保存单元。

具体步骤:

  1. 遍历Delta的ops,拆分单元
    遍历整个Delta的操作数组,每当遇到header格式的内容,就标记一个新的保存单元开始,把后续的ops归到这个单元里,直到遇到下一个标题:
    function splitDeltaIntoSections(fullDelta) {
      const sections = [];
      let currentSection = { title: '', delta: new Quill.Contents() };
    
      fullDelta.ops.forEach(op => {
        // 判断当前op是否是标题
        if (op.attributes?.header) {
          // 如果已有当前单元,先存入数组
          if (currentSection.delta.ops.length > 0) {
            sections.push(currentSection);
          }
          // 新建一个单元,把标题内容存进去
          currentSection = {
            title: op.insert,
            delta: new Quill.Contents({ ops: [op] })
          };
        } else {
          // 非标题内容,加入当前单元的Delta
          currentSection.delta.ops.push(op);
        }
      });
    
      // 把最后一个单元加进去
      if (currentSection.delta.ops.length > 0) {
        sections.push(currentSection);
      }
    
      return sections;
    }
    
    // 使用示例
    const fullDelta = quill.getContents();
    const sections = splitDeltaIntoSections(fullDelta);
    // 遍历sections保存每个单元
    sections.forEach((section, index) => {
      console.log(`保存第${index+1}个区域:`, section.title, section.delta);
    });
    

这种方法的优势是不需要修改编辑器的Blot结构,适合快速实现简单场景,但如果文档里有复杂格式(比如嵌套、多类型标题),解析逻辑会变得复杂,容易出现边界问题。

总结

  • 如果你需要长期维护、场景较复杂,优先选自定义Blot方案,结构清晰,扩展性强;
  • 如果只是简单的标题拆分,临时需求,可以用解析Delta方案,快速上手。

内容的提问来源于stack exchange,提问作者JHamel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:41:01