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

When using SSR, HMR works in one place and then doesn't work elsewhere in the exact same component template!! #6567

Closed
7 tasks done
AaronBeaudoin opened this issue Jan 19, 2022 · 3 comments
Labels
bug: upstream Bug in a dependency of Vite feat: hmr feat: ssr

Comments

@AaronBeaudoin
Copy link

Describe the bug

When using SSR, HMR seems to be breaking in a extremely weird way. In a specific example component I've simplified down from an actual real-world component I'm building, I've found that in a certain part of the component I can make changes and HMR works perfectly, but in another part of the exact same component template, HMR doesn't work!

See the reproduction for the most concise example I could come up with. Instructions for reproducing the issue are in the README. Please be aware that while the repository is using the vite-plugin-ssr plugin to simplify SSR, I also was able to reproduce the issue in Vite's own ssr-vue playground, so I know it is an underlying issue that doesn't have anything to do with vite-plugin-ssr. The repo with the vite-plugin-ssr plugin is much more minimal IMO, but if you're having issues with it, I can provide a reproduction without the plugin as well.

Per the bug report form instructions, I've done my best to narrow down the bug to the point where I'm confident it's an issue with Vite. However, I acknowledge that I'm not familiar enough with the whole Vue ecosystem of packages to be 100% sure.

Reproduction

https://github.com/AaronBeaudoin/vite-vue-ssr-bug-repr

System Info

System:
    OS: macOS 12.0.1
    CPU: (8) x64 Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz
    Memory: 128.05 MB / 16.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 14.16.0 - ~/.nvm/versions/node/v14.16.0/bin/node
    npm: 6.14.11 - ~/.nvm/versions/node/v14.16.0/bin/npm
  Browsers:
    Chrome: 97.0.4692.71
    Firefox: 88.0.1
    Safari: 15.1
  npmPackages:
    @vitejs/plugin-vue: ^2.0.1 => 2.0.1 
    @vitejs/plugin-vue-jsx: ^1.3.3 => 1.3.3 
    vite: ^2.7.13 => 2.7.13

Used Package Manager

npm

Logs

THIS ERROR IS NOT FROM `vite` or `vite build` BUT RATHER IN THE BROWSER. SEE THE INSTRUCTIONS IN THE README OF THE REPRODUCTION REPO I'VE ATTACHED FOR HOW TO GET THE ERROR.

runtime-core.esm-bundler.js:569 TypeError: Cannot set properties of null (setting 'textContent')
    at setElementText (runtime-dom.esm-bundler.js:33:12)
    at patchChildren (runtime-core.esm-bundler.js:5196:17)
    at patchElement (runtime-core.esm-bundler.js:4720:13)
    at processElement (runtime-core.esm-bundler.js:4568:13)
    at patch (runtime-core.esm-bundler.js:4485:21)
    at patchKeyedChildren (runtime-core.esm-bundler.js:5261:17)
    at patchChildren (runtime-core.esm-bundler.js:5204:21)
    at patchElement (runtime-core.esm-bundler.js:4720:13)
    at processElement (runtime-core.esm-bundler.js:4568:13)
    at patch (runtime-core.esm-bundler.js:4485:21)

Validations

@AaronBeaudoin
Copy link
Author

AaronBeaudoin commented Jan 21, 2022

Since this is something I'd really like to see some progress on, I've done a bit of my own digging into the source code to see if I can find some more information. Here's what I've found. Hopefully it is helpful.

First a Brief Review

This app is using Vite's SSR functionality. HMR is working when the text inside of <div>WORKS</div> is changed, but not when the text inside of <div>DOESN'T WORK</div> is changed. The complete root component (page) template looks like this:

<template>
  <div>
    <div>WORKS</div>
    <div> <!-- <<< We'll call this `div` "A" -->
      <div>DOESN'T WORK</div> <!-- <<< We'll call this `div` "B" -->
    </div>
  </div>
</template>

The comments will become useful in a moment below. When "DOESN'T WORK" is changed to (for example) "DOESN'T WORK ARGHHH", the following error is logged to the console:

runtime-core.esm-bundler.js:569 TypeError: Cannot set properties of null (setting 'textContent')
    at setElementText (runtime-dom.esm-bundler.js:33:12)
    at patchChildren (runtime-core.esm-bundler.js:5196:17)
    at patchElement (runtime-core.esm-bundler.js:4720:13)
    at processElement (runtime-core.esm-bundler.js:4568:13)
    at patch (runtime-core.esm-bundler.js:4485:21)
    at patchKeyedChildren (runtime-core.esm-bundler.js:5261:17)
    at patchChildren (runtime-core.esm-bundler.js:5204:21)
    at patchElement (runtime-core.esm-bundler.js:4720:13)
    at processElement (runtime-core.esm-bundler.js:4568:13)
    at patch (runtime-core.esm-bundler.js:4485:21)

Investigation

As you can see in the message, this error is coming from runtime-dom.esm-bundler.js:33, whose surrounding code looks like this:

const nodeOps = {
    ...
    setElementText: (el, text) => {
        el.textContent = text;
    }
    ...
};

So the problem here is that for some reason when Vue tries to set the new textContent of the element, el is null. So the question is then: Why hasn't el been set?

Using a debugger I've been able to narrow things down to the hydrateElement function at runtime-core.esm-bundler.js:4170. It looks like at line 4177 there is an if block with the condition forcePatchValue || patchFlag !== -1. When div "A" (see above) is being run through this function, this condition is false, causing div "B" to be skipped, which seems to also cause it's el to never be set. According to the comment in the code, the reason for this is because div "A" is a "hoisted static node"—whatever that means—and because of this the props and children should be skipped.

Something about this logic must be wrong, because it results in the app having a tree where div "B" has no el set. That's why when HMR is run, updates to specifically that element fail.

Call for Help

This is where I'm lost. I don't know enough about how Vue works internally to say what exactly is wrong with the logic here. It seems to me that clearly div "B" should have an el set, because it exists right there in the component template and even the DOM. Can someone with more knowledge step in here and help figure out what the core problem is?

@AaronBeaudoin
Copy link
Author

Anyone willing to step in here and help figure out what's causing this issue?

@sapphi-red
Copy link
Member

Closing as I confirmed that this was fixed with vuejs/core#5406.

@github-actions github-actions bot locked and limited conversation to collaborators May 7, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug: upstream Bug in a dependency of Vite feat: hmr feat: ssr
Projects
None yet
Development

No branches or pull requests

4 participants