-
-
Notifications
You must be signed in to change notification settings - Fork 348
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
Client only components with Vue 3 #278
Comments
I just came up with another idea: // composables/useClientOnly.js
import { h, defineComponent } from 'vue';
const isBrowser = typeof window !== 'undefined';
export function useClientOnly(asyncComponent) {
return defineComponent({
setup(props, context) {
return () => isBrowser ? h(asyncComponent, { ...props, ...context.attrs }) : h('div');
}
});
}; And use it on the page like this: <script setup>
// pages/song.page.vue
import { useClientOnly } from '../../composables/useClientOnly';
const MidiPlayer = defineAsyncComponent(() => import('../../components/MidiPlayer.vue'));
const AsyncMidiPlayer = useClientOnly(MidiPlayer);
</script>
<template>
<AsyncMidiPlayer />
</template> Still I think that this is not the optimal solution for this problem. |
I found another issue (egoist/vue-client-only#122) that pointed me to the current <script setup>
// pages/song.vue
import { ref, onMounted, nextTick } from 'vue';
import ClientOnly from '../components/ClientOnly.js';
import MidiPlayer from '../components/MidiPlayer.vue';
const midiPlayer = ref(null);
onMounted(() => {
nextTick(async () => {
console.log(midiPlayer.value);
});
});
</script>
<template>
<ClientOnly>
<MidiPlayer ref="midiPlayer" />
</ClientOnly>
</template> In my case it is important that I get access to the component as template ref to call an exposed method. So I really would prefer not to use an async component but instead a regular component in combination with a If I run
Any idea why it works fine with This issue also is somewhat related when I tried to get it work with the |
NB: My {
"dependencies": {
"vue-verovio-canvas": "file:../../vue-verovio-canvas"
}
} Running
But if I change this dependency to the GitHub repository instead of the local file link everything works just fine again: {
"dependencies": {
"vue-verovio-canvas": "github:WolfgangDrescher/vue-verovio-canvas"
}
} The development server is still not running tough and still prints the same error as mentioned before:
|
Most(/all?) seem to be user land issues. But I agree a working example would be nice. One thing you may want to try is |
I've used the ClientOnly component from vitepress with no issues ClientOnly.ts |
https://vitepress.vuejs.org/guide/global-component.html#clientonly Thanks @DoubleJ-G. I will update the docs. I'm leaving this open until I do. |
A similar issue i found far quicker though was a similar issue for react though. A bit off topic: I will try the vitepress component soon. Thanks all involved! |
I don't see the lack of documentation: I think https://vite-plugin-ssr.com/client-only-components and https://vite-plugin-ssr.com/dynamic-import covers it. |
I'm unsure about my ability to provide a PR soon, but the current Statement of Using the Client Only Component does fix that issue though, but may make migration of an existing app (using a similar, but inferior Implementation of the renderer) somewhat more painful than these statements may make one believe. Which is why, i linked the above issues together for better visibility :) |
Вариант решения для Quasar 2, vue 3 <template>
<q-no-ssr> <!-- Важно -->
<YaMap />
</q-no-ssr>
</template>
<script>
components: {
YaMap: defineAsyncComponent(() =>
import('components/modules/YaMap.vue') /* Важно */
)
},
</script> |
@KirillOlegovichH Up for creating a full example? |
Обновил ответ. Пример применим только для Quasar framework 2. Прошу прощение что не уточнил сразу |
Closing as it's mostly done. Contribution welcome to create a repository example. |
A simple ClientOnly.vue can do the trick <template>
<template v-if="isMounted"><slot /></template>
<template v-else><slot name="placeholder" /></template>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const isMounted = ref(false)
onMounted(() => {
isMounted.value = true
})
</script> and here's the one with Suspense if you have async setup in you component <template>
<template v-if="isMounted">
<Suspense>
<slot />
</Suspense>
</template>
<template v-else><slot name="placeholder" /></template>
</template>
<script setup>
import { ref, onMounted, Suspense } from 'vue'
const isMounted = ref(false)
onMounted(() => {
isMounted.value = true
})
</script> Here's how to use ClientOnly.vue <template>
<ClientOnly>
<OnlyRenderOnClientComponent v-bind="someProps"/>
<template #placeholder>This is for SSR/SEO</template>
</ClientOnly>
</template> |
@yuanoook Added to the docs a7dfce5. @WolfgangDrescher What did you end up with? I'm looking for a |
Once Nuxt 3 was ready for production I started migrating to it. So I do not have an example for you with |
Reply to: I was excited to use this component as it makes so much sense and straight forward. I wrapped my HJ29/vue3-tabs as so:
but unfortunately for some reason, the Tabs component (which reads For now what I did for my vue component is utilize the
Pretty sure this is not the optimal/correct way of doing it but for now it works. Though I'm not happy with the warning sign of not having markRaw. Here is the full context of the code. Need your insights on this:
I am not sure how to also add what to render on server side for SEO similar to the feature of the |
I wrote a little component that includes third party packages that cannot be rendered server side (
midi-player-js
andsoundfont-player
; they are using AudioContext and XMLHttpRequest). Following the docs the best way to solve this problem would be to use dynamic imports. So shouldn't it be enough to just usedefineAsyncComponent
because Vue wraps the component into a Promise?But if I use it like this the server still tries to render the component and throws
XMLHttpRequest is not defined
when runningnpm run dev
. With a condition on the component to only render it in browser it still failes withAudioContext is not defined
since the component gets loaded in SSR even if it's not displayed in the template.So I wrote a litte
<ClientOnly>
component:This seems to work better but I get an error:
Hydration completed but contains mismatches.
Is there a way to prevent this from happening?At the end the best way to do this for me was to add an additional component
AsyncMidiPlayer
:And to use it like this:
But since I have multiple components that cannot be interpreted server side I would like to have a better solution than writing a custom wrapper for each of them. I was not able to create a generic version of this because the import source needs to be specific (I ran into
The above dynamic import cannot be analyzed by vite.
). I'm sure there is a better solution for this. Did anyone find a better solution to this than me? It would be nice to extend the vite-plugin-ssr docs with a working vue 3 example.The text was updated successfully, but these errors were encountered: