vite
vite copied to clipboard
Avoid unnecessary reload after HMR WebSocket connection lost
Clear and concise description of the problem
Currently Vite reloads the page after a successful ping after connection lost.
In a hybrid app, once the app launches another app, the connection lost. When the app switches back, the app reloads. To properly debug routines involve switching to another app, we have to either use production build or comment out location.reload() in vite/dist/client/client.mjs.
Suggested solution
Reconnect WebSocket to check HMR status instead of reload unconditionally.
Alternative
No response
Additional context
No response
Validations
- [X] Follow our Code of Conduct
- [X] Read the Contributing Guidelines.
- [x] Read the docs.
- [x] Check that there isn't already an issue that request the same feature to avoid creating a duplicate.
I have same problem #6089
I want to flag an important reason I'd like to be able to deactivate the websocket.
An active websocket blocks the bfcache on the browser. So the bfcaching caching behavior locally is different than the caching behavior in prod, where there is no websocket, at least for me. I therefore couldn't reproduce a certain cache-related bug locally until I realized the Vite websocket was affecting it. Because there was no way to shut off websockets completely, I had to strip Vite out to be able to reproduce the bug.
Just leaving this comment here since I didn't see any reference to this caching issue in any of the websocket-related Vite issues, and I'm sure someone will have this problem someday.
@niksy
How this happens is:
When the web socket connection was disconnected, Vite's HMR client starts to ping to http://yourserver/websocket-path.
https://github.com/vitejs/vite/blob/29abeeab9cc147c6d3383c672ded68311304bbaf/packages/vite/src/client/client.ts#L311-L333
When the Vite server starts, Vite will respond to that ping request.
Then, Vite's HMR client reload the page.
https://github.com/vitejs/vite/blob/29abeeab9cc147c6d3383c672ded68311304bbaf/packages/vite/src/client/client.ts#L103-L105
But this has some edge cases. For example, when WebSocket server starts after the HTTP Server (can happen in middleware mode).
Where we need to change is:
- Change the ping to use WebSocket instead of HTTP: https://github.com/vitejs/vite/blob/29abeeab9cc147c6d3383c672ded68311304bbaf/packages/vite/src/client/client.ts#L311
- Handle that WebSocket connection: https://github.com/vitejs/vite/blob/29abeeab9cc147c6d3383c672ded68311304bbaf/packages/vite/src/node/server/ws.ts#L80
I guess we need to use a different protocol(sec-websocket-protocol) to differentiate the normal connection and the ping connection.
@sapphi-red
Change the ping to use WebSocket instead of HTTP:
Does this mean we need to create our own WebSocket server and reference it in Vite server.hmr.server property?
Does this mean we need to create our own WebSocket server and reference it in Vite
server.hmr.serverproperty?
No, Vite already has a WebSocket server and we can reuse that.
Could somebody give me any suggestions on how I can prevent the reload?
I use the latest vite 4.5.0 with SSR, createViteServer in middlewareMode and Express.js. Most of my config is based on the very nice repo vite-typescript-ssr-react.
Honestly, I have not had any issues until we stuck with a security check with one of the 3rd party services. They use Burp Suite to intercept all of the network requests. The app running I believe reverse proxy.
The first thing I noticed is the connection via the app probably because the proxy is always insecure.
This is why, I think, the WebSocket connection fails, and in an attempt to reestablish the connection Vite reloads the web app.
Also to be honest I don't really understand why we need the HMR WebSocket on prod.
@IvanKalinin Vite isn't intended to be run in prod and you shouldn't be doing that. https://vitejs.dev/guide/ssr.html#:~:text=Then%2C%20in%20server,a%20working%20setup
(That is not related to this issue)
Would love to see this be configurable.
For my situation (a very simple vite server setup w/ some proxies for socketio and our api, nothing else), this diff allowed me to retain HMR functionality but not reload the page:
diff --git a/dist/client/client.mjs b/dist/client/client.mjs
index e944ed731f936d5a35fe8368ce55c0518ade537e..ba56411cce8960a5dd0c47bf9b3bb13a8b8498f6 100644
--- a/dist/client/client.mjs
+++ b/dist/client/client.mjs
@@ -483,7 +483,7 @@ function setupWebSocket(protocol, hostAndPath, onCloseWithoutOpen) {
notifyListeners('vite:ws:disconnect', { webSocket: socket });
console.log(`[vite] server connection lost. polling for restart...`);
await waitForSuccessfulPing(protocol, hostAndPath);
- location.reload();
+ setupWebSocket(protocol, hostAndPath, onCloseWithoutOpen)
});
return socket;
}
It's not clear if this would break other things but it seems to work just fine for me.
any update on this?
+1 on wondering if this will be fixed
+1 so many issues and pull requests have been created, but they are being closed and rejected, and this error is still being repeated. Why?
It seems like first part of the change ("Change the ping to use WebSocket instead of HTTP") is already here
But I’m failing to understand where we should handle WebSocket connection in createWebSocketServer implementation.
From what I can see, that is already handled (vite-ping references).
@niksy What I explained at https://github.com/vitejs/vite/issues/5675#issuecomment-1345588419 was implemented by #17891
@sapphi-red I’m referencing this situation; it doesn’t seem to work.
What happens is Nodemon restarts whole application (including Vite dev server) and client keeps pinging but after some time it decides to reload whole page instead of using new WebSocket connection.
If the connection is lost and the ping successes later on, Vite will reload the page. This is an intended behavior. Vite cannot just connect the WebSocket, because the Vite cannot know whether the page content that is already loaded is stale or not.
I don't remember why I said "For that case, I think implementing https://github.com/vitejs/vite/issues/5675 would work." at https://github.com/vitejs/vite/pull/9007#issuecomment-1345190952. I should have said "For https://github.com/vitejs/vite/pull/9007#issuecomment-1179560998" instead of "For that case".
If the connection is lost and the ping successes later on, Vite will reload the page. This is an intended behavior. Vite cannot just connect the WebSocket, because the Vite cannot know whether the page content that is already loaded is stale or not.
So give us some options.
- Reload the page (like now)
- Assume it's not stale and attempt to do a hot reload anyway
- Print a warning to the console that a full page reload is required but do nothing
Assume it's not stale and attempt to do a hot reload anyway
Is this anyhow possible? Like try to create fresh connection and use it for subsequent interactions.
Assume it's not stale and attempt to do a hot reload anyway
Is this anyhow possible? Like try to create fresh connection and use it for subsequent interactions.
Dunno. Clearly it's reconnecting if it's forcing a refresh, whether or not that can technically be used for subsequent interactions I'm not sure. Maybe there's some kind of state that's kept in memory and it's flushed when Vite dies so it's not possible. I'm really not sure how the internals work. But if it's not possible, I'd still prefer (3) over (1)
I'm debugging in webview on iOS and this problem is driving me crazy...
Will stop HMR help? I just don’t want the page to reload, don’t care if it is stale or not.
I'm debugging in webview on iOS and this problem is driving me crazy...
We have found a way. see https://github.com/vitejs/vite/issues/18489#issuecomment-2848404974
I'm debugging in webview on iOS and this problem is driving me crazy...
We have found a way. see #18489 (comment)
I used the same approach, but now I'm moving to rsbuild, it's much more sane. :)
Probably we can keep the last send message on the server and the last received message on the client. Then, we can avoid the reload if the value is same.
Relevant issue - https://github.com/vitejs/vite/issues/17858 (@nefcanto)
I just had the worst day today, spending more than 15/30 seconds on a breakpoint causes a connection loss and reload, and leaves dev tools frozen.
I decided to just use another server. (python http.server)
Angular ... Vite did you switch to this junk?
Throwing an error on vite:ws:disconnect can prevent page from reloading
import.meta.hot.on('vite:ws:disconnect', () => {
throw new Error('Connection lost! Please refresh the page manually');
});