Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid unnecessary reload after HMR WebSocket connection lost #5675

Open
4 tasks done
vilicvane opened this issue Nov 14, 2021 · 10 comments
Open
4 tasks done

Avoid unnecessary reload after HMR WebSocket connection lost #5675

vilicvane opened this issue Nov 14, 2021 · 10 comments
Labels
enhancement New feature or request feat: hmr

Comments

@vilicvane
Copy link

vilicvane commented Nov 14, 2021

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

@zhengjiaqi
Copy link

I have same problem #6089

@hkhanna
Copy link

hkhanna commented Oct 12, 2022

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.

@sapphi-red
Copy link
Member

@niksy

How this happens is:

When the web socket connection was disconnected, Vite's HMR client starts to ping to http://yourserver/websocket-path.

async function waitForSuccessfulPing(
socketProtocol: string,
hostAndPath: string,
ms = 1000,
) {
const pingHostProtocol = socketProtocol === 'wss' ? 'https' : 'http'
// eslint-disable-next-line no-constant-condition
while (true) {
try {
// A fetch on a websocket URL will return a successful promise with status 400,
// but will reject a networking error.
// When running on middleware mode, it returns status 426, and an cors error happens if mode is not no-cors
await fetch(`${pingHostProtocol}://${hostAndPath}`, {
mode: 'no-cors',
})
break
} catch (e) {
// wait ms before attempting to ping again
await new Promise((resolve) => setTimeout(resolve, ms))
}
}
}

When the Vite server starts, Vite will respond to that ping request.
Then, Vite's HMR client reload the page.
console.log(`[vite] server connection lost. polling for restart...`)
await waitForSuccessfulPing(protocol, hostAndPath)
location.reload()

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:

I guess we need to use a different protocol(sec-websocket-protocol) to differentiate the normal connection and the ping connection.

@niksy
Copy link
Contributor

niksy commented Dec 14, 2022

@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?

@sapphi-red
Copy link
Member

Does this mean we need to create our own WebSocket server and reference it in Vite server.hmr.server property?

No, Vite already has a WebSocket server and we can reuse that.

@IvanKalinin

This comment was marked as off-topic.

@sapphi-red

This comment was marked as off-topic.

@psychedelicious
Copy link

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.

@mxvsh
Copy link

mxvsh commented Oct 2, 2024

any update on this?

@jeanlescure
Copy link

+1 on wondering if this will be fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request feat: hmr
Projects
None yet
Development

No branches or pull requests

9 participants