vuejs / vue

This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/core
http://v2.vuejs.org
MIT License
207.97k stars 33.68k forks source link

SSR fails to render component inside v-else of a v-if with v-html #11299

Open tuomassalo opened 4 years ago

tuomassalo commented 4 years ago

Version

2.6.11

Reproduction link

https://github.com/tuomassalo/vue-ssr-v-html-bug

Steps to reproduce

What is expected?

I expect SSR to render "bar: Bar!", as the client-side does.

Or, I'd like to get an eslint warning that this is a bad idea (if that is the problem).

What is actually happening?

App.vue fails to render bar-component on the server. Instead, it outputs <bar-component></bar-component>, and the dev server gives the warning "The client-side rendered virtual DOM tree is not matching server-rendered content."


The key part of App.vue is this:

<div v-if="foo" v-html="'Foo.'"/>
<div v-else>
  bar: <bar-component/>
</div>

My original component was naturally longer. I ran into this problem after changing the v-if line from something like:

<div v-if="foo">{{ foo }}</div>

To:

<div v-if="foo" v-html="foo"/>

... which seemed innocuous to me.

Finally, apologies for posting a very possible duplicate.

posva commented 4 years ago

A workaround is using a template tag:

<div id="app">
  <template v-if="foo" >
    <div v-html="'Foo.'" key="hey"/>
  </template>
  <div v-else>
    bar: <BarComponent />
  </div>
</div>

For SSR repros, it's usually easier to do them with the bare minimum. Here is an example:

const Vue = require('vue')

const BarComponent = {
  template: `
   <div>Bar!</div>
  `,
}

const app = new Vue({
  template: `
<div id="app">
  <div v-if="foo" v-html="'Foo.'" key="hey"/>
  <div v-else>
    bar: <BarComponent />
  </div>
</div>
  `,
  data: () => ({ foo: false }),
  components: { BarComponent },
})

const renderer = require('vue-server-renderer').createRenderer()

renderer.renderToString(app, (err, html) => {
  if (err) throw err
  console.log(html)
})

It could also help anybody picking up the issue

Lizhooh commented 4 years ago

me too

simonmaass commented 4 years ago

having the same issue!

MorevM commented 3 years ago

I also ran into an issue and spent half a day to figure out what was going on. Finally, I feel like a fool but:

<div v-if="smth === true" v-html="html"></div>
<component v-else ... />                        <----- doesn't work
// but...
<div v-if="smth === true" v-html="html"></div>
<component v-if="smth !== true" />              <----- this works

Hope this helps someone who stuck with that too.

IamAfnanSk commented 1 year ago

I also ran into an issue and spent half a day to figure out what was going on. Finally, I feel like a fool but:

<div v-if="smth === true" v-html="html"></div>
<component v-else ... />                        <----- doesn't work
// but...
<div v-if="smth === true" v-html="html"></div>
<component v-if="smth !== true" />              <----- this works

Hope this helps someone who stuck with that too.

this does work, but how? can someone please explain what's going on here?

when I wrote this it worked

<div v-if="colorModeIcon">
  <Icon @click="switchColorMode" :name="colorModeIcon" />
</div>
<div v-if="!colorModeIcon"> // <- work
  <Icon name="line-md:loading-twotone-loop" />
</div>

but this does not

<div v-if="colorModeIcon">
  <Icon @click="switchColorMode" :name="colorModeIcon" />
</div>
<div v-else> // <- don't work
  <Icon name="line-md:loading-twotone-loop" />
</div>
slucas-elsa commented 10 months ago

I'm also having this problem with Nuxt .... strange