UNPKG

nuxt

Version:

Nuxt is a free and open-source framework with an intuitive and extendable way to create type-safe, performant and production-grade full-stack web applications and websites with Vue.js.

98 lines (97 loc) 3.51 kB
import { cloneVNode, createElementBlock, createStaticVNode, defineComponent, getCurrentInstance, h, onMounted, provide, ref } from "vue"; import { useNuxtApp } from "../nuxt.js"; import { getFragmentHTML } from "./utils.js"; export const clientOnlySymbol = Symbol.for("nuxt:client-only"); export default defineComponent({ name: "ClientOnly", inheritAttrs: false, props: ["fallback", "placeholder", "placeholderTag", "fallbackTag"], setup(_, { slots, attrs }) { const mounted = ref(false); onMounted(() => { mounted.value = true; }); if (import.meta.dev) { const nuxtApp = useNuxtApp(); nuxtApp._isNuxtPageUsed = true; nuxtApp._isNuxtLayoutUsed = true; } provide(clientOnlySymbol, true); return (props) => { if (mounted.value) { return slots.default?.(); } const slot = slots.fallback || slots.placeholder; if (slot) { return slot(); } const fallbackStr = props.fallback || props.placeholder || ""; const fallbackTag = props.fallbackTag || props.placeholderTag || "span"; return createElementBlock(fallbackTag, attrs, fallbackStr); }; } }); const cache = /* @__PURE__ */ new WeakMap(); // @__NO_SIDE_EFFECTS__ export function createClientOnly(component) { if (cache.has(component)) { return cache.get(component); } const clone = { ...component }; if (clone.render) { clone.render = (ctx, cache2, $props, $setup, $data, $options) => { if ($setup.mounted$ ?? ctx.mounted$) { const res = component.render?.bind(ctx)(ctx, cache2, $props, $setup, $data, $options); return res.children === null || typeof res.children === "string" ? cloneVNode(res) : h(res); } else { const fragment = getFragmentHTML(ctx._.vnode.el ?? null) ?? ["<div></div>"]; return import.meta.client ? createStaticVNode(fragment.join(""), fragment.length) : h("div", ctx.$attrs ?? ctx._.attrs); } }; } else if (clone.template) { clone.template = ` <template v-if="mounted$">${component.template}</template> <template v-else><div></div></template> `; } clone.setup = (props, ctx) => { const instance = getCurrentInstance(); const attrs = { ...instance.attrs }; const directives = extractDirectives(instance); for (const key in attrs) { delete instance.attrs[key]; } const mounted$ = ref(false); onMounted(() => { Object.assign(instance.attrs, attrs); instance.vnode.dirs = directives; mounted$.value = true; }); return Promise.resolve(component.setup?.(props, ctx) || {}).then((setupState) => { if (typeof setupState !== "function") { setupState = setupState || {}; setupState.mounted$ = mounted$; return setupState; } return (...args) => { if (mounted$.value) { const res = setupState(...args); return res.children === null || typeof res.children === "string" ? cloneVNode(res) : h(res); } else { const fragment = getFragmentHTML(instance?.vnode.el ?? null) ?? ["<div></div>"]; return import.meta.client ? createStaticVNode(fragment.join(""), fragment.length) : h("div", ctx.attrs); } }; }); }; cache.set(component, clone); return clone; } function extractDirectives(instance) { if (!instance || !instance.vnode.dirs) { return null; } const directives = instance.vnode.dirs; instance.vnode.dirs = null; return directives; }