小怂吖 發表於 2025-3-16 22:39:00

Vue3生命周期钩子函数深度解析:从源码到实战的万字指南

<h2 id="一vue3生命周期革新特性">一、Vue3生命周期革新特性</h2>
<p>相较于Vue2,Vue3通过Composition API带来了更灵活的生命周期管理方式。通过<code>onBeforeMount</code>等函数注册钩子时,实际是通过<code>injectHook</code>方法将回调函数注册到组件实例的<code>hmr</code>属性中。</p>
<p>在 Vue3 应用开发中,生命周期钩子函数扮演着至关重要的角色。它们允许开发者在组件从创建到销毁的各个阶段执行自定义逻辑,为开发者提供了对组件生命周期的精细控制能力。理解和熟练运用这些钩子函数,是开发高效、健壮 Vue3 应用的关键。</p>
<pre><code class="language-javascript">// Vue3源码片段(packages/runtime-core/src/apiLifecycle.ts)
export function injectHook(
type: LifecycleHook,
hook: Function &amp; { __weh?: Function },
target: ComponentInternalInstance | null
) {
const hooks = target || (target = [])
hooks.push(hook)
}
</code></pre>
<h2 id="二创建阶段钩子函数">二、创建阶段钩子函数</h2>
<h3 id="21-beforecreate">2.1 beforeCreate</h3>
<ul>
<li><strong>触发时机</strong>:在组件实例被创建之前,此时组件的 <code>data</code>、<code>computed</code>、<code>methods</code> 等属性尚未初始化,无法访问 <code>this</code>。</li>
<li><strong>使用场景</strong>:虽然在实际开发中 <code>beforeCreate</code> 使用较少,但在一些需要在组件创建前执行通用逻辑的场景下,例如记录组件创建日志等操作,可在此处编写代码。</li>
</ul>
<h3 id="22-created">2.2 created</h3>
<ul>
<li><strong>触发时机</strong>:组件实例创建完成后调用,此时 <code>data</code>、<code>computed</code>、<code>methods</code> 等已初始化,可通过 <code>this</code> 访问组件的属性和方法,但 DOM 尚未挂载。</li>
<li><strong>使用场景</strong>:常用于进行数据的初始化操作,例如从本地存储中读取数据并赋值给组件的响应式数据,或进行一些简单的计算属性初始化。也适合在此处发起一些不需要依赖 DOM 的异步数据请求。</li>
</ul>
<pre><code class="language-html">&lt;template&gt;
&lt;div&gt;{{ message }}&lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref } from 'vue';

const message = ref('');

const fetchData = async () =&gt; {
const response = await fetch('/api/data');
const data = await response.json();
message.value = data.text;
};

fetchData();
&lt;/script&gt;
</code></pre>
<h2 id="三挂载阶段钩子函数">三、挂载阶段钩子函数</h2>
<h3 id="31-beforemount">3.1 beforeMount</h3>
<ul>
<li><strong>触发时机</strong>:在组件挂载到 DOM 之前调用,此时模板已经编译完成,但尚未将其挂载到实际的 DOM 节点上。</li>
<li><strong>使用场景</strong>:可用于在挂载前对模板或数据进行最后的调整,例如修改即将挂载的 DOM 的一些属性。不过由于 DOM 尚未挂载,直接操作 DOM 的意义不大。</li>
</ul>
<h3 id="32-mounted">3.2 mounted</h3>
<ul>
<li><strong>触发时机</strong>:组件成功挂载到 DOM 后调用,此时可以通过 <code>$el</code> 访问真实的 DOM 元素,或使用 <code>refs</code> 访问子组件及元素。</li>
<li><strong>使用场景</strong>:常用于需要操作 DOM 的场景,比如初始化第三方插件(如图表库、富文本编辑器等),这些插件通常需要在真实 DOM 存在的情况下进行初始化。也可以在此时进行一些依赖于 DOM 结构的计算或数据处理。</li>
</ul>
<h3 id="33-第三方库集成规范">3.3 第三方库集成规范</h3>
<p>以ECharts集成示例:</p>
<pre><code class="language-html">&lt;template&gt;
&lt;div ref="chartContainer"&gt;&lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref, onMounted } from 'vue';
import Chart from 'chart.js';

const chartContainer = ref(null);

onMounted(() =&gt; {
if (chartContainer.value) {
    new Chart(chartContainer.value, {
      type: 'bar',
      data: {
      labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
      datasets: [
          {
            label: '# of Votes',
            data: ,
            backgroundColor: [
            'rgba(255, 99, 132, 0.2)',
            'rgba(54, 162, 235, 0.2)',
            'rgba(255, 206, 86, 0.2)',
            'rgba(75, 192, 192, 0.2)',
            'rgba(153, 102, 255, 0.2)',
            'rgba(255, 159, 64, 0.2)'
            ],
            borderColor: [
            'rgba(255, 99, 132, 1)',
            'rgba(54, 162, 235, 1)',
            'rgba(255, 206, 86, 1)',
            'rgba(75, 192, 192, 1)',
            'rgba(153, 102, 255, 1)',
            'rgba(255, 159, 64, 1)'
            ],
            borderWidth: 1
          }
      ]
      },
      options: {
      scales: {
          y: {
            beginAtZero: true
          }
      }
      }
    });
}
});
&lt;/script&gt;
</code></pre>
<h2 id="四更新阶段钩子函数">四、更新阶段钩子函数</h2>
<h3 id="41-beforeupdate">4.1 beforeUpdate</h3>
<ul>
<li><strong>触发时机</strong>:在组件数据更新之前调用,此时组件的 <code>data</code> 已经发生变化,但 DOM 尚未更新。</li>
<li><strong>使用场景</strong>:可用于在数据更新前进行一些准备工作,例如保存当前 DOM 的状态,以便在更新后进行对比或恢复。</li>
</ul>
<h3 id="42-updated">4.2 updated</h3>
<ul>
<li><strong>触发时机</strong>:组件数据更新且 DOM 重新渲染完成后调用。</li>
<li><strong>使用场景</strong>:适合进行一些依赖于更新后 DOM 状态的操作,例如重新计算一些基于新 DOM 结构的布局信息,或者对更新后的 DOM 进行进一步的样式调整。但需要注意,在 <code>updated</code> 钩子函数中操作 DOM 时要避免陷入死循环,因为再次修改数据可能会再次触发更新。</li>
</ul>
<pre><code class="language-html">&lt;template&gt;
&lt;div @click="updateData"&gt;
    &lt;p&gt;{{ message }}&lt;/p&gt;
&lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref, onBeforeUpdate, onUpdated } from 'vue';

const message = ref('初始消息');

const updateData = () =&gt; {
message.value = '更新后的消息';
};

onBeforeUpdate(() =&gt; {
console.log('数据即将更新,当前消息:', message.value);
});

onUpdated(() =&gt; {
console.log('数据已更新,DOM 已重新渲染');
});
&lt;/script&gt;
</code></pre>
<h2 id="五卸载阶段钩子函数">五、卸载阶段钩子函数</h2>
<h3 id="51-beforeunmount">5.1 beforeUnmount</h3>
<ul>
<li><strong>触发时机</strong>:在组件实例被卸载之前调用,此时组件仍然存在,可以访问组件的属性和方法。</li>
<li><strong>使用场景</strong>:常用于清理组件在运行过程中创建的一些副作用,例如清除定时器、解绑事件监听器等,以避免内存泄漏。</li>
</ul>
<h3 id="52-unmounted">5.2 unmounted</h3>
<ul>
<li><strong>触发时机</strong>:组件实例被成功卸载后调用,此时组件及其所有子组件都已从 DOM 中移除,组件相关的事件监听器、定时器等都已被清理。</li>
<li><strong>使用场景</strong>:理论上在此处已无需进行复杂操作,不过可以用于记录组件卸载相关的日志信息。</li>
</ul>
<pre><code class="language-html">&lt;template&gt;
&lt;div&gt;
    &lt;p&gt;{{ message }}&lt;/p&gt;
&lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref, onBeforeUnmount, onUnmounted } from 'vue';

const message = ref('组件内容');
let timer;

const startTimer = () =&gt; {
timer = setInterval(() =&gt; {
    console.log('定时器运行中');
}, 1000);
};

startTimer();

onBeforeUnmount(() =&gt; {
clearInterval(timer);
console.log('组件即将卸载,清除定时器');
});

onUnmounted(() =&gt; {
console.log('组件已卸载');
});
&lt;/script&gt;
</code></pre>
<h2 id="六常见误区与解决方案">六、常见误区与解决方案</h2>
<table>
<thead>
<tr>
<th>问题现象</th>
<th>原因分析</th>
<th>解决方案</th>
</tr>
</thead>
<tbody>
<tr>
<td>mounted中获取DOM为null</td>
<td>异步渲染延迟</td>
<td>使用nextTick()</td>
</tr>
<tr>
<td>内存泄漏</td>
<td>未清理事件监听</td>
<td>建立清理函数注册表</td>
</tr>
<tr>
<td>重复请求</td>
<td>未取消前次请求</td>
<td>使用AbortController</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>写在最后</strong><br>
哈喽!大家好呀,我是 Code_Cracke,一名热爱编程的小伙伴。在这里,我将分享一些实用的开发技巧和经验心得。如果你也对编程充满热情,欢迎关注并一起交流学习!</p>
<p>如果你对这篇文章有任何疑问、建议或者独特的见解,欢迎在评论区留言。无论是探讨技术细节,还是分享项目经验,都能让我们共同进步。</p>
</blockquote>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:Code_Cracke,转载请注明原文链接:https://www.cnblogs.com/proer-blog/p/18775864</p><br><br>
来源:https://www.cnblogs.com/proer-blog/p/18775864
頁: [1]
查看完整版本: Vue3生命周期钩子函数深度解析:从源码到实战的万字指南