Vì inject, provide, lifecycle hook (onMounted, onUnmounted) và việc gắn effect vào scope đều dựa vào instance component đang active.
- Vue set instance này ngay trước khi chạy
setup()và gỡ ngay sau khisetup()trả về. - Sau một
awaittrongsetup()thường, code chạy ở microtask khác — instance không còn active.
// sai: trong async setup() thường, sau await là mất context instance
async setup() {
const data = await fetchData()
onMounted(() => { /* warning: called when there is no active instance */ })
const api = inject(ApiKey) // trả về undefined
}
// <script setup> là ngoại lệ: compiler bọc await bằng withAsyncContext để khôi phục instanceHệ quả cụ thể:
- onMounted/onUnmounted không đăng ký → cleanup không chạy → rò rỉ listener/timer.
- inject trả undefined dù cha có provide.
- watch/computed tạo sau await không thuộc scope component → không tự dừng khi unmount.
Cách xử lý: gọi mọi composable ở đầu setup, đưa phần async vào trong composable (trả về ref loading/data và tự fetch trong onMounted). Trong async setup() thường mà phải await, hãy đăng ký hook/inject trước dòng await đầu tiên; top-level await trong <script setup> thì an toàn vì compiler tự khôi phục instance. Với code chạy ngoài component (vd callback), bọc bằng effectScope() để tự quản lý vòng đời effect.