vitejs / vite

Next generation frontend tooling. It's fast!
http://vite.dev
MIT License
67.84k stars 6.11k forks source link

HMR Dispose Issues with JS dependencies #4824

Closed aphex closed 2 years ago

aphex commented 3 years ago

Describe the bug

I am not sure if this is vite or the vue-plugin. I have tried to look more into how all this is wired together and hopefully this break down explains the issue well enough for someone to point me in the right direction.

Imagine a normal vite project with a vue3, setup is pretty standard. I have a vue SFC component, Main.vue which is importing a js file, interval.js, that calls setInterval upon loading.

If I then go and edit this file I have no way to use import.meta.hot.dispose to clean up the interval I created. I think thinking this comes back how HMR reload is working, maybe here https://github.com/vitejs/vite/blob/main/packages/vite/src/node/server/hmr.ts#L77 in how it queries the module graph, but I am not sure.

Either way when the HMR socket message hits the client all I see there is "main.vue" as being updated. So my interval.js never hits this loop https://github.com/vitejs/vite/blob/main/packages/vite/src/client/client.ts#L326 so it will never have a chance to dispose any side effects it has.

Is there a reason only .vue files are brought through here? It also seems that this is just going to fall short regardless as it seems to detect the change to interval.js find it was up to the top most component, main.vue and just reload main.vue. However main.vue is then going to reimport all its dependencies with new timestamps. So we will end up with duplicate imports of files without having any chance to cleanup side effects from them.

I guess maybe the answer here is just don't have side effects in your JS files, wrap everything in tryOnMounted/tryOnUnMounted from vueUse. This doesn't seem right though, and still doesn't fix the issue when something is called outside of a component lifecycle (imported from another utility or hook). It does seem like files should be able to create whatever side effects they need knowing they will only be loaded once by the browser regardless of how many times it is used in the application.

Issues with timers/watchers/side effects doubling and tripling up will only surface when using HMR. Hopefully I am just missing something simple but this makes it difficult to have any global side effects in an application.

Reproduction

To reproduce just create a vite + vue3 project, import any JS file into a vue component with the following code in it

if (import.meta.hot) {
  import.meta.hot.dispose((data) => {
    console.log('cleanup', data)
  })
}

setInterval(() => console.log('tick'), 1000)

Make changes to that JS file and see the cleanup is not done.

System Info

N/A

Used Package Manager

npm

Logs

No response

Validations

sapphi-red commented 2 years ago

@bluwy Do you think this is the same with https://github.com/sveltejs/kit/issues/2903#issuecomment-982901659? In fact, it works. https://stackblitz.com/edit/vitejs-vite-qrrf7a?file=src%2Fint.js

bluwy commented 2 years ago

Hmm yeah it seems to be the same, and stackblitz work for me too. @aphex if you can provide a repro (github repo or stackblitz( that would help as we can't reproduce this.

github-actions[bot] commented 2 years ago

Hello @aphex. Please provide a minimal reproduction using a GitHub repository or StackBlitz. Issues marked with need reproduction will be closed if they have no activity within 3 days.