nuxt
Version:
98 lines (97 loc) • 3.51 kB
JavaScript
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;
}