vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
https://vuejs.org/
MIT License
47.66k stars 8.33k forks source link

v-text and v-html and may be other built-in (custom may be too) directives on custom component with slot in SSR #6435

Open max5432112345 opened 2 years ago

max5432112345 commented 2 years ago

Vue version

3

Link to minimal reproduction

Vue 3 https://stackblitz.com/edit/github-lf1exv?terminal=dev

Nuxt 3 https://github.com/max5432112345/nuxt-error-2/

Steps to reproduce

If use v-text on custom component with slot when in SPA text is displayed in page but in SSR not displayed (hot reload only sometime make it to display but after refresh page disappear).

Index.vue

<template>
  <div>
    <CustomText v-text="'test'" />
     <CustomText> TEST </CustomText>
  </div>
</template>

CustomText.vue

<template>
  <span>
    <slot name="default" />
  </span>
</template>

What is expected?

We certainly want behaviour similar on SSR vs SPA when using v-text

What is actually happening?

In vue 2 it produced early warnings (Hydration children mismatch) when used v-text without inner text (and I add $nbsp; to bypass that) but now no errors or warnings. Now in vue 3 it not permit to use inner text and v-text together.

In that situation it not show errors or warnings like vue 2:

System Info

vue 3
linux

Any additional comments?

https://github.com/nuxt/nuxt.js/issues/14530

max5432112345 commented 2 years ago

@yyx990803 v-html the same story.

mittci commented 2 years ago

@yyx990803

edison1105 commented 1 year ago

The root cause is that directives with children overwrite (e.g. v-html & v-text) are not working with Component yet in SSR. here is a workaround

wvffle commented 1 year ago

@edison1105 Can we get any ETA for this issue?

vampics commented 1 year ago

+ 1

nimens55 commented 1 year ago
edison1105 commented 1 year ago

see #6553 and https://github.com/vuejs/core/issues/6553#issuecomment-1232468857

rivatove commented 1 year ago

My use case:

I want to create a JSON-LD script tag with metadata. I want to use data passed to a component as props.

No workaround is available for this AFAIK.

rudolfbyker commented 1 year ago

Here is my workaround:

/**
 * This is similar to `component(:is="tag")`, except that it also works in SSR.
 *
 * See:
 * - https://discord.com/channels/473401852243869706/1152225826910244955/1152225826910244955
 * - https://stackblitz.com/edit/github-gs1ptn
 * - https://github.com/vuejs/core/issues/6553#issuecomment-1232468857
 * - https://github.com/vuejs/core/issues/6435
 * - https://play.vuejs.org/#__SSR__eNp9UctOwzAQ/BXLl7RSlR64hbQSVJUA8RL0hjlEybZ1SWzLXpdIVf4dP5ISJMCHaDM7O57xnuiVUunRAs1ojtCoukBYMkFIXvFjKFy5sgZls4EWSYbuu2A0QTCYMErmkT2P9Hw+EslNqblCYgCtcv+8UVIjGalttWxIks5XsgkuEq8Qp5Z0RgfYm+vRs8qJ7EnXC8RJaEOjgm1ha0fwvpSWymRvzm2LyfvMQ8HNJDSmkUSIdpgWZDIliz4zIftJ4jIls4HjDxcC9M3m4T6LyqnXDar+dNNLX3YO6H4GQVNKseW79GCkcGmCJKOly8dr0E8KuRSG0Wy4jNGiruXnXcBQW+gvcTN7KD9+wQ+m9RijzxoM6CMweu5hoXeAsb1+fXSeR81GVrZ27H+aL2Bkbb3HSLu2onK2R7zg9jZshovdxqxbBGGGUN7o8DKe7fblV/tX9G+7F+lFmHMPSrsvfIzfCg==
 */
export default defineComponent({
  props: {
    tag: {
      type: String,
      required: true,
    },
    html: {
      type: String,
      required: true,
    },
  },

  setup(props, { slots }) {
    return () => h(props.tag, { innerHTML: props.html });
  },
});
oppianmatt commented 11 months ago

Not that hard to make your own v-html directive that works SSR. Here is mine:

  nuxtApp.vueApp.directive('thtml', {
    mounted: (el: HTMLElement, binding: any, vnode: any) => {
      el.innerHTML = binding.value;
    },
    getSSRProps(binding: any) {
      return {
        innerHTML: binding.value
      }
    }
  })
mitar commented 8 months ago

But v-html does not work on custom components even in SPA mode, not just SSR, see example here.

Expected output should be:

<div class="component"><div class="wrap"><div class="inner">Inner</div></div></div>

But it is:

<div class="component"><div class="wrap"><div class="wrap2"><div class="inner">Inner</div></div></div></div>

There is an eslint rule because of this. Maybe this means that this is a more general bug/limitation of Vue?