\n\n\n```\n\n### Частая ошибка: забыли про стрелочные функции\n\n```javascript\n// ❌ Плохо: стрелочная функция в addEventListener\nmounted() {\n // Каждый раз новая функция — removeEventListener не сработает!\n window.addEventListener(\"resize\", () => {\n this.handleResize();\n });\n},\n\nunmounted() {\n // Эта функция не будет удалена, т.к. это другой объект\n window.removeEventListener(\"resize\", () => {\n this.handleResize();\n });\n},\n\n// ✅ Правильно: сохраняем ссылку на метод\nmounted() {\n window.addEventListener(\"resize\", this.handleResize);\n},\n\nunmounted() {\n window.removeEventListener(\"resize\", this.handleResize);\n},\n\nmethods: {\n handleResize() {\n // ...\n },\n},\n```\n\n### Чеклист очистки ресурсов\n\nКогда создаёшь что-то в **mounted**, помни про **unmounted**:\n\n- **setInterval** -> **clearInterval**\n- **setTimeout** -> **clearTimeout**\n- **addEventListener** -> **removeEventListener**\n- **WebSocket** -> **ws.close()**\n- **Observable.subscribe** -> **subscription.unsubscribe()**\n- **Promise** -> отмени, если возможно\n- **requestAnimationFrame** -> **cancelAnimationFrame**\n\n### Заключение\n\nОбъекты, созданные в **mounted**:\n1. **Остаются в памяти** после unmount, если не очищены\n2. **Могут вызвать ошибки**, если пытаются получить доступ к компоненту\n3. **Приводят к утечкам памяти** и деградации производительности\n4. **Должны быть очищены** в **unmounted**\n\n**Всегда помни про cleanup!**","dateCreated":"2026-04-02T21:51:14.921369","upvoteCount":0,"author":{"@type":"Person","name":"claude-haiku-4.5"}}}}
← Назад к вопросам

Будет ли что-либо происходить с объектом созданным в mounted во Vue?

2.0 Middle🔥 121 комментариев
#Vue.js

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Жизненный цикл объектов в Vue mounted: утечки памяти и очистка

Когда ты создаёшь объект в хуке mounted во Vue, нужно понимать, что с ним происходит после размонтирования компонента. Это критично для предотвращения утечек памяти.

Что происходит с объектом после unmount?

// ❌ Опасно: утечка памяти
export default {
  mounted() {
    // Создаём объект
    this.heavyObject = {
      data: new Array(1000000).fill("data"),
      timerId: null,
    };

    // Запускаем интервал (НЕ очищаем!)
    this.timerId = setInterval(() => {
      console.log("Doing something...");
    }, 1000);
  },
};

// Когда компонент размонтируется:
// 1. this.heavyObject всё ещё в памяти
// 2. Интервал продолжает работать (утечка!)
// 3. Если интервал обращается к компоненту — ОШИБКА

Правильный способ: очистка в unmounted

// ✅ Правильно: очищаем ресурсы
export default {
  data() {
    return {
      timerId: null,
      heavyObject: null,
    };
  },

  mounted() {
    // Создаём объект
    this.heavyObject = {
      data: new Array(1000000).fill("data"),
    };

    // Запускаем интервал
    this.timerId = setInterval(() => {
      console.log("Doing something...");
    }, 1000);
  },

  unmounted() {
    // Очищаем интервал
    if (this.timerId) {
      clearInterval(this.timerId);
    }

    // Очищаем тяжёлый объект
    this.heavyObject = null;
  },
};

Практический пример: WebSocket

// ❌ Плохо: WebSocket остаётся открытым
export default {
  mounted() {
    this.ws = new WebSocket("ws://api.example.com");

    this.ws.onmessage = (event) => {
      this.data = event.data;
    };
  },
  // WebSocket остаётся открытым, даже после размонтирования!
};

// ✅ Хорошо: закрываем WebSocket
export default {
  data() {
    return {
      ws: null,
      data: null,
    };
  },

  mounted() {
    this.ws = new WebSocket("ws://api.example.com");

    this.ws.onmessage = (event) => {
      this.data = event.data;
    };
  },

  unmounted() {
    if (this.ws) {
      this.ws.close();
      this.ws = null;
    }
  },
};

Практический пример: EventListener

// ❌ Плохо: слушатель остаётся
export default {
  mounted() {
    window.addEventListener("resize", this.handleResize);
  },

  methods: {
    handleResize() {
      this.windowWidth = window.innerWidth;
    },
  },
  // Слушатель остаётся активным!
};

// ✅ Хорошо: удаляем слушатель
export default {
  data() {
    return {
      windowWidth: window.innerWidth,
    };
  },

  mounted() {
    window.addEventListener("resize", this.handleResize);
  },

  unmounted() {
    window.removeEventListener("resize", this.handleResize);
  },

  methods: {
    handleResize() {
      this.windowWidth = window.innerWidth;
    },
  },
};

Обработчик mousemove

// ❌ Утечка памяти
export default {
  mounted() {
    // На каждое движение мыши создаётся новая функция!
    document.addEventListener("mousemove", () => {
      this.mouseX = event.clientX;
      this.mouseY = event.clientY;
    });
  },
};

// ✅ Правильно: используем метод компонента
export default {
  data() {
    return {
      mouseX: 0,
      mouseY: 0,
    };
  },

  mounted() {
    document.addEventListener("mousemove", this.handleMouseMove);
  },

  unmounted() {
    document.removeEventListener("mousemove", this.handleMouseMove);
  },

  methods: {
    handleMouseMove(event) {
      this.mouseX = event.clientX;
      this.mouseY = event.clientY;
    },
  },
};

Таймеры и задержки

// ❌ Плохо: setTimeout продолжит работать
export default {
  mounted() {
    setTimeout(() => {
      this.doSomething(); // ОШИБКА: this уже не существует
    }, 5000);
  },

  methods: {
    doSomething() {
      console.log("Done");
    },
  },
};

// ✅ Хорошо: отменяем timeout
export default {
  data() {
    return {
      timeoutId: null,
    };
  },

  mounted() {
    this.timeoutId = setTimeout(() => {
      this.doSomething();
    }, 5000);
  },

  unmounted() {
    if (this.timeoutId) {
      clearTimeout(this.timeoutId);
    }
  },

  methods: {
    doSomething() {
      console.log("Done");
    },
  },
};

Подписки на внешние источники (Observable)

// ❌ Плохо: подписка не отменяется
import { observable$ } from "some-library";

export default {
  data() {
    return {
      data: null,
      subscription: null,
    };
  },

  mounted() {
    this.subscription = observable$.subscribe(data => {
      this.data = data;
    });
  },
  // Подписка продолжит работать!
};

// ✅ Хорошо: отменяем подписку
export default {
  data() {
    return {
      data: null,
      subscription: null,
    };
  },

  mounted() {
    this.subscription = observable$.subscribe(data => {
      this.data = data;
    });
  },

  unmounted() {
    if (this.subscription) {
      this.subscription.unsubscribe();
    }
  },
};

Полный пример: сложный компонент с несколькими ресурсами

export default {
  data() {
    return {
      // Таймеры
      intervalId: null,
      timeoutId: null,

      // Соединения
      ws: null,
      subscription: null,

      // Данные
      data: null,
      mouseX: 0,
      mouseY: 0,
    };
  },

  mounted() {
    // Интервал для обновления данных
    this.intervalId = setInterval(() => {
      this.fetchData();
    }, 5000);

    // WebSocket для реалтайм данных
    this.ws = new WebSocket("ws://api.example.com");
    this.ws.onmessage = (event) => {
      this.data = event.data;
    };

    // Слушатель движения мыши
    document.addEventListener("mousemove", this.handleMouseMove);

    // Подписка на Observable
    this.subscription = dataStream$.subscribe(data => {
      this.data = data;
    });

    // Отложенная операция
    this.timeoutId = setTimeout(() => {
      this.loadMoreData();
    }, 10000);
  },

  unmounted() {
    // Очищаем интервал
    if (this.intervalId) {
      clearInterval(this.intervalId);
    }

    // Очищаем timeout
    if (this.timeoutId) {
      clearTimeout(this.timeoutId);
    }

    // Закрываем WebSocket
    if (this.ws) {
      this.ws.close();
      this.ws = null;
    }

    // Отменяем подписку
    if (this.subscription) {
      this.subscription.unsubscribe();
    }

    // Удаляем слушатель
    document.removeEventListener("mousemove", this.handleMouseMove);

    // Очищаем данные
    this.data = null;
  },

  methods: {
    handleMouseMove(event) {
      this.mouseX = event.clientX;
      this.mouseY = event.clientY;
    },

    async fetchData() {
      try {
        const response = await fetch("/api/data");
        this.data = await response.json();
      } catch (error) {
        console.error("Failed to fetch data:", error);
      }
    },

    loadMoreData() {
      // Загружаем дополнительные данные
    },
  },
};

Использование composables для чистоты кода

// hooks/useMouseTracker.js
import { ref, onMounted, onUnmounted } from "vue";

export function useMouseTracker() {
  const x = ref(0);
  const y = ref(0);

  const handleMouseMove = (event) => {
    x.value = event.clientX;
    y.value = event.clientY;
  };

  onMounted(() => {
    document.addEventListener("mousemove", handleMouseMove);
  });

  onUnmounted(() => {
    document.removeEventListener("mousemove", handleMouseMove);
  });

  return { x, y };
}

// Component.vue
<script setup>
import { useMouseTracker } from "@/hooks/useMouseTracker";

const { x, y } = useMouseTracker();
</script>

<template>
  <div>
    Mouse: {{ x }}, {{ y }}
  </div>
</template>

Частая ошибка: забыли про стрелочные функции

// ❌ Плохо: стрелочная функция в addEventListener
mounted() {
  // Каждый раз новая функция — removeEventListener не сработает!
  window.addEventListener("resize", () => {
    this.handleResize();
  });
},

unmounted() {
  // Эта функция не будет удалена, т.к. это другой объект
  window.removeEventListener("resize", () => {
    this.handleResize();
  });
},

// ✅ Правильно: сохраняем ссылку на метод
mounted() {
  window.addEventListener("resize", this.handleResize);
},

unmounted() {
  window.removeEventListener("resize", this.handleResize);
},

methods: {
  handleResize() {
    // ...
  },
},

Чеклист очистки ресурсов

Когда создаёшь что-то в mounted, помни про unmounted:

  • setInterval -> clearInterval
  • setTimeout -> clearTimeout
  • addEventListener -> removeEventListener
  • WebSocket -> ws.close()
  • Observable.subscribe -> subscription.unsubscribe()
  • Promise -> отмени, если возможно
  • requestAnimationFrame -> cancelAnimationFrame

Заключение

Объекты, созданные в mounted:

  1. Остаются в памяти после unmount, если не очищены
  2. Могут вызвать ошибки, если пытаются получить доступ к компоненту
  3. Приводят к утечкам памяти и деградации производительности
  4. Должны быть очищены в unmounted

Всегда помни про cleanup!