MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Vue生命周期钩子 onMounted与onUnmounted在Composition API中的应用

2022-10-226.8k 阅读

Vue 生命周期钩子概述

在深入探讨 onMountedonUnmounted 之前,先简单回顾一下 Vue 的生命周期概念。Vue 组件从创建到销毁的过程中,会经历一系列的阶段,例如初始化、挂载、更新、卸载等。每个阶段都提供了相应的钩子函数,允许开发者在特定的时间点执行自定义的逻辑。

传统的 Vue 选项式 API 中,我们使用 createdmountedupdatedbeforeDestroydestroyed 等钩子函数。例如:

<template>
  <div>
    {{ message }}
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello, Vue!'
    };
  },
  mounted() {
    console.log('Component mounted');
  },
  beforeDestroy() {
    console.log('Component will be destroyed');
  }
};
</script>

在上述代码中,mounted 钩子函数在组件挂载到 DOM 后被调用,beforeDestroy 钩子函数在组件销毁前被调用。

随着 Vue 3 的发布,Composition API 引入了一种新的方式来组织组件逻辑,其中 onMountedonUnmounted 就是与传统 mounteddestroyed 对应的 Composition API 钩子函数。

onMounted 钩子函数

基本概念

onMounted 钩子函数在组件挂载到 DOM 后立即被调用。这意味着此时组件的模板已经被渲染到 DOM 中,并且可以访问 DOM 元素。在 onMounted 中,你可以执行需要 DOM 元素存在才能运行的代码,例如初始化第三方库、绑定事件监听器等操作。

语法

onMounted 接受一个回调函数作为参数,该回调函数将在组件挂载后执行。

import { onMounted } from 'vue';

export default {
  setup() {
    onMounted(() => {
      // 在此处编写挂载后的逻辑
      console.log('Component mounted using onMounted');
    });
    return {};
  }
};

代码示例:初始化第三方库

假设我们要在组件挂载后初始化一个简单的图表库 Chart.js。首先安装 Chart.js:

npm install chart.js

然后在 Vue 组件中使用 onMounted 进行初始化:

<template>
  <canvas id="myChart"></canvas>
</template>

<script>
import { onMounted } from 'vue';
import { Chart } from 'chart.js';

export default {
  setup() {
    onMounted(() => {
      const ctx = document.getElementById('myChart').getContext('2d');
      new Chart(ctx, {
        type: 'bar',
        data: {
          labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
          datasets: [
            {
              label: '# of Votes',
              data: [12, 19, 3, 5, 2, 3],
              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
            }
          }
        }
      });
    });
    return {};
  }
};
</script>

在上述代码中,onMounted 确保在 canvas 元素挂载到 DOM 后才初始化 Chart.js,这样就可以正确获取到 canvas 的上下文并绘制图表。

代码示例:绑定事件监听器

有时候我们需要在组件挂载后为 DOM 元素绑定事件监听器。例如,为一个按钮添加点击事件:

<template>
  <button ref="myButton">Click me</button>
</template>

<script>
import { onMounted, ref } from 'vue';

export default {
  setup() {
    const myButton = ref(null);
    onMounted(() => {
      myButton.value.addEventListener('click', () => {
        console.log('Button clicked');
      });
    });
    return {
      myButton
    };
  }
};
</script>

在这个例子中,onMounted 使得我们可以在按钮挂载到 DOM 后为其添加点击事件监听器。通过 ref 来引用按钮元素,确保在 onMounted 回调中可以正确访问该元素。

onUnmounted 钩子函数

基本概念

onUnmounted 钩子函数在组件从 DOM 中卸载后被调用。这是一个清理资源的好时机,例如移除事件监听器、取消定时器、关闭网络连接等操作。如果在 onMounted 中进行了一些需要清理的操作,那么通常应该在 onUnmounted 中进行相应的清理。

语法

onUnmounted 同样接受一个回调函数作为参数,该回调函数将在组件卸载后执行。

import { onUnmounted } from 'vue';

export default {
  setup() {
    onUnmounted(() => {
      // 在此处编写卸载后的清理逻辑
      console.log('Component unmounted using onUnmounted');
    });
    return {};
  }
};

代码示例:移除事件监听器

继续上面为按钮添加点击事件的例子,当组件卸载时,我们需要移除之前添加的事件监听器,以避免内存泄漏:

<template>
  <button ref="myButton">Click me</button>
</template>

<script>
import { onMounted, onUnmounted, ref } from 'vue';

export default {
  setup() {
    const myButton = ref(null);
    let clickHandler;
    onMounted(() => {
      clickHandler = () => {
        console.log('Button clicked');
      };
      myButton.value.addEventListener('click', clickHandler);
    });
    onUnmounted(() => {
      if (myButton.value) {
        myButton.value.removeEventListener('click', clickHandler);
      }
    });
    return {
      myButton
    };
  }
};
</script>

在上述代码中,onUnmounted 回调函数中移除了在 onMounted 中添加的点击事件监听器。通过保存事件处理函数的引用,我们可以在卸载时准确地移除它。

代码示例:取消定时器

假设在组件挂载后启动了一个定时器,当组件卸载时需要取消该定时器:

<template>
  <div>
    <p>Timer: {{ timerValue }}</p>
  </div>
</template>

<script>
import { onMounted, onUnmounted, ref } from 'vue';

export default {
  setup() {
    const timerValue = ref(0);
    let timer;
    onMounted(() => {
      timer = setInterval(() => {
        timerValue.value++;
      }, 1000);
    });
    onUnmounted(() => {
      clearInterval(timer);
    });
    return {
      timerValue
    };
  }
};
</script>

在这个例子中,onMounted 启动了一个每秒增加 timerValue 的定时器,而 onUnmounted 则在组件卸载时取消了该定时器,防止定时器在组件卸载后继续运行,避免潜在的问题。

onMounted 与 onUnmounted 的嵌套组件应用

在 Vue 应用中,组件通常是嵌套的。理解 onMountedonUnmounted 在嵌套组件中的执行顺序和应用场景非常重要。

执行顺序

当父组件挂载时,它的 onMounted 钩子函数会在其所有子组件挂载完成后被调用。类似地,当父组件卸载时,它的 onUnmounted 钩子函数会在其所有子组件卸载完成后被调用。

例如,有一个父组件 Parent.vue 和一个子组件 Child.vue

<!-- Parent.vue -->
<template>
  <div>
    <Child />
  </div>
</template>

<script>
import Child from './Child.vue';
import { onMounted, onUnmounted } from 'vue';

export default {
  components: {
    Child
  },
  setup() {
    onMounted(() => {
      console.log('Parent component mounted');
    });
    onUnmounted(() => {
      console.log('Parent component unmounted');
    });
    return {};
  }
};
</script>
<!-- Child.vue -->
<template>
  <div>
    Child component
  </div>
</template>

<script>
import { onMounted, onUnmounted } from 'vue';

export default {
  setup() {
    onMounted(() => {
      console.log('Child component mounted');
    });
    onUnmounted(() => {
      console.log('Child component unmounted');
    });
    return {};
  }
};
</script>

Parent.vue 被挂载时,控制台会先输出 Child component mounted,然后输出 Parent component mounted。当 Parent.vue 被卸载时,控制台会先输出 Child component unmounted,然后输出 Parent component unmounted

应用场景

在嵌套组件中,onMountedonUnmounted 可以用于处理组件间的通信和资源管理。例如,父组件可以在 onMounted 中初始化一些共享资源,并在 onUnmounted 中清理这些资源。子组件可以在 onMounted 中订阅父组件提供的事件或数据,在 onUnmounted 中取消订阅。

假设父组件提供一个数据更新的事件,子组件在挂载时订阅该事件,在卸载时取消订阅:

<!-- Parent.vue -->
<template>
  <div>
    <button @click="updateData">Update Data</button>
    <Child />
  </div>
</template>

<script>
import Child from './Child.vue';
import { onMounted, onUnmounted, ref } from 'vue';

export default {
  components: {
    Child
  },
  setup() {
    const data = ref('Initial data');
    const eventListeners = [];
    const updateData = () => {
      data.value = 'Updated data';
      eventListeners.forEach(listener => listener());
    };
    onMounted(() => {
      console.log('Parent component mounted');
    });
    onUnmounted(() => {
      console.log('Parent component unmounted');
      eventListeners.length = 0;
    });
    return {
      data,
      updateData,
      eventListeners
    };
  }
};
</script>
<!-- Child.vue -->
<template>
  <div>
    <p>{{ parentData }}</p>
  </div>
</template>

<script>
import { onMounted, onUnmounted } from 'vue';

export default {
  setup(props, { parent }) {
    const parentData = parent.data;
    let unsubscribe;
    onMounted(() => {
      unsubscribe = () => {
        console.log('Child component unsubscribed');
      };
      parent.eventListeners.push(unsubscribe);
      console.log('Child component mounted and subscribed');
    });
    onUnmounted(() => {
      const index = parent.eventListeners.indexOf(unsubscribe);
      if (index > -1) {
        parent.eventListeners.splice(index, 1);
      }
      console.log('Child component unmounted and unsubscribed');
    });
    return {
      parentData
    };
  }
};
</script>

在这个例子中,子组件在 onMounted 中订阅父组件的事件,在 onUnmounted 中取消订阅,确保资源的正确管理。

注意事项

  1. 避免内存泄漏:在 onMounted 中添加的事件监听器、定时器等资源,一定要在 onUnmounted 中进行清理。否则,当组件卸载后,这些资源可能仍然存在,导致内存泄漏,影响应用的性能和稳定性。
  2. 访问组件实例:在 Composition API 中,setup 函数中没有传统的 this 指向组件实例。如果需要访问组件实例,可以使用 getCurrentInstance 函数(在某些场景下),但要注意它返回的实例对象的使用限制。在 onMountedonUnmounted 中,通常不需要直接访问组件实例,因为可以通过 setup 函数返回的数据和方法来处理逻辑。
  3. 与异步操作的结合:在 onMounted 中执行异步操作时,要注意处理异步操作的结果。例如,如果在 onMounted 中发起一个 API 请求,可能需要在组件卸载时取消该请求,以避免在组件卸载后还处理不必要的响应数据。可以使用 AbortController 来实现请求的取消。

总结

onMountedonUnmounted 是 Vue Composition API 中非常重要的生命周期钩子函数。onMounted 用于在组件挂载到 DOM 后执行初始化逻辑,如初始化第三方库、绑定事件监听器等;onUnmounted 则用于在组件从 DOM 中卸载后进行资源清理,如移除事件监听器、取消定时器等。在嵌套组件中,理解它们的执行顺序和应用场景对于构建复杂的 Vue 应用至关重要。通过合理使用这两个钩子函数,并注意避免常见的问题,开发者可以更好地管理组件的生命周期,提高应用的性能和稳定性。

以上内容详细介绍了 onMountedonUnmounted 在 Composition API 中的应用,希望能帮助你在 Vue 前端开发中更熟练地运用这些知识。