QuillJS多独立保存区域实现方案:Blots/容器或Delta解析?
在QuillJS中实现多独立保存区域的两种方案
这其实是Quill开发里挺常见的需求——看起来是一个完整文档,但内部拆成多个可单独保存的单元(比如每个标题对应一块内容)。我分享两种实用思路,你可以根据自己的场景选:
方案一:用自定义Blot包裹独立保存区域(更推荐)
这种方法不需要手动解析整个Delta,而是通过自定义Blot把每个需要独立保存的区域封装成一个带标识的独立对象,后续提取内容会非常直观。
具体步骤:
注册自定义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);构建编辑器内容结构
在插入标题和对应内容时,把它们放在这个自定义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对应的内容...');提取每个区域的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格式)来拆分出每个独立保存单元。
具体步骤:
- 遍历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
相关产品推荐
相关产品推荐

