
本教程详细讲解如何在vue 2应用中动态重构dom结构,将一系列`h1`标题及其后续的段落(`p`标签)分组,并封装到html的`
理解需求与挑战
在处理来自内容管理系统(如Sanity.io)的数据时,我们常常会遇到需要将扁平化的内容结构转换为更具语义和交互性的HTML结构。例如,一个常见的场景是,内容以一系列
标题,后面紧跟着多个
段落的形式呈现,如下所示:
<h1>标题一</h1>
<p>段落内容1.1</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/ai/2019" title="飞书多维表格"><img
src="https://img.php.cn/upload/ai_manual/000/000/000/175679978185950.png" alt="飞书多维表格" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/ai/2019" title="飞书多维表格">飞书多维表格</a>
<p>表格形态的AI工作流搭建工具,支持批量化的AI创作与分析任务,接入DeepSeek R1满血版</p>
</div>
<a href="/ai/2019" title="飞书多维表格" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div>
<p>段落内容1.2</p>
<h1>标题二</h1>
<p>段落内容2.1</p><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/cb6835dc7db1" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">前端免费学习笔记(深入)</a>”;</p>
<p>段落内容2.2</p>
<p>段落内容2.3</p>我们的目标是将其重构为可折叠的 段落作为可折叠的详细内容,直到遇到下一个结构,使得每个
标题作为可折叠部分的摘要,其后的所有相关
标题。期望的输出结构如下:
<details> <summary><h1>标题一</h1></summary> <p>段落内容1.1</p> <p>段落内容1.2</p> </details> <details> <summary><h1>标题二</h1></summary> <p>段落内容2.1</p><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/cb6835dc7db1" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">前端免费学习笔记(深入)</a>”;</p> <p>段落内容2.2</p> <p>段落内容2.3</p> </details>
这种转换无法通过简单的Vue模板绑定或计算属性直接实现,因为它涉及到对DOM元素的查找、创建、移动和替换,属于典型的原生DOM操作范畴。特别是在使用portable-text-to-vue这类库将富文本内容转换为Vue组件块时,这些块通常会渲染成一系列的HTML标签,此时进行DOM二次处理就显得尤为必要。
核心实现思路
为了实现上述DOM结构的重构,我们需要在Vue组件的特定生命周期钩子中执行JavaScript代码来操作DOM。
- 选择合适的时机: 由于我们需要在组件渲染后才能访问和操作其真实的DOM元素,因此Vue的mounted()生命周期钩子是执行此类操作的理想选择。
-
获取目标元素: 首先,需要获取所有作为分组起点的
元素。
-
构建和移动: 遍历每个
元素,为它创建一个新的
和结构,将
移动到
中。然后,识别该
之后直到下一个
(或文档末尾)的所有兄弟元素,并将它们移动到新的
元素中。 -
清理: 移除原始的
元素,因为其内容已经转移到新的结构中。
分步实现详解
以下是在Vue 2组件中实现此DOM重构的详细步骤和示例代码。
步骤一:获取所有目标H1元素
在mounted()钩子中,使用document.getElementsByTagName('h1')来获取页面中所有的
元素。由于getElementsByTagName返回的是一个实时的HTMLCollection,在对其进行循环并修改DOM时可能会导致索引问题,因此建议将其转换为数组进行处理。mounted() {
// 获取当前组件的根元素,或者特定容器元素
const container = this.$el;
// 将HTMLCollection转换为数组,以便安全地遍历和修改DOM
const h1Elements = Array.from(container.getElementsByTagName('h1'));
// ... 后续操作
}步骤二:为每个H1及其后续内容构建details结构
遍历h1Elements数组。对于每个h1元素,我们需要:
- 创建新的和
元素。
- 将当前h1元素移动到新创建的
中。 - 将
添加到 中。 -
将新创建的元素插入到原始h1元素之前。这样,新的结构就占据了h1原来的位置。









