mirror of
https://github.com/nuxt/nuxt.git
synced 2025-01-30 23:32:38 +00:00
perf(nuxt): remove code duplication in client-only (#30460)
This commit is contained in:
parent
2438130f6b
commit
8b8a731dff
@ -1,12 +1,14 @@
|
|||||||
import { cloneVNode, createElementBlock, createStaticVNode, defineComponent, getCurrentInstance, h, onMounted, provide, ref } from 'vue'
|
import { cloneVNode, createElementBlock, defineComponent, getCurrentInstance, h, onMounted, provide, ref } from 'vue'
|
||||||
import type { ComponentInternalInstance, ComponentOptions, InjectionKey } from 'vue'
|
import type { ComponentInternalInstance, ComponentOptions, InjectionKey } from 'vue'
|
||||||
import { isPromise } from '@vue/shared'
|
import { isPromise } from '@vue/shared'
|
||||||
import { useNuxtApp } from '../nuxt'
|
import { useNuxtApp } from '../nuxt'
|
||||||
import { getFragmentHTML } from './utils'
|
|
||||||
import ServerPlaceholder from './server-placeholder'
|
import ServerPlaceholder from './server-placeholder'
|
||||||
|
import { elToStaticVNode } from './utils'
|
||||||
|
|
||||||
export const clientOnlySymbol: InjectionKey<boolean> = Symbol.for('nuxt:client-only')
|
export const clientOnlySymbol: InjectionKey<boolean> = Symbol.for('nuxt:client-only')
|
||||||
|
|
||||||
|
const STATIC_DIV = '<div></div>'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'ClientOnly',
|
name: 'ClientOnly',
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
@ -54,16 +56,14 @@ export function createClientOnly<T extends ComponentOptions> (component: T) {
|
|||||||
return (res.children === null || typeof res.children === 'string')
|
return (res.children === null || typeof res.children === 'string')
|
||||||
? cloneVNode(res)
|
? cloneVNode(res)
|
||||||
: h(res)
|
: h(res)
|
||||||
} else {
|
|
||||||
const fragment = getFragmentHTML(ctx._.vnode.el ?? null) ?? ['<div></div>']
|
|
||||||
return createStaticVNode(fragment.join(''), fragment.length)
|
|
||||||
}
|
}
|
||||||
|
return elToStaticVNode(ctx._.vnode.el, STATIC_DIV)
|
||||||
}
|
}
|
||||||
} else if (clone.template) {
|
} else if (clone.template) {
|
||||||
// handle runtime-compiler template
|
// handle runtime-compiler template
|
||||||
clone.template = `
|
clone.template = `
|
||||||
<template v-if="mounted$">${component.template}</template>
|
<template v-if="mounted$">${component.template}</template>
|
||||||
<template v-else><div></div></template>
|
<template v-else>${STATIC_DIV}</template>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,10 +105,8 @@ export function createClientOnly<T extends ComponentOptions> (component: T) {
|
|||||||
return (res.children === null || typeof res.children === 'string')
|
return (res.children === null || typeof res.children === 'string')
|
||||||
? cloneVNode(res)
|
? cloneVNode(res)
|
||||||
: h(res)
|
: h(res)
|
||||||
} else {
|
|
||||||
const fragment = getFragmentHTML(instance?.vnode.el ?? null) ?? ['<div></div>']
|
|
||||||
return createStaticVNode(fragment.join(''), fragment.length)
|
|
||||||
}
|
}
|
||||||
|
return elToStaticVNode(instance?.vnode.el, STATIC_DIV)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -117,8 +115,7 @@ export function createClientOnly<T extends ComponentOptions> (component: T) {
|
|||||||
if (mounted$.value) {
|
if (mounted$.value) {
|
||||||
return h(setupState(...args), ctx.attrs)
|
return h(setupState(...args), ctx.attrs)
|
||||||
}
|
}
|
||||||
const fragment = getFragmentHTML(instance?.vnode.el ?? null) ?? ['<div></div>']
|
return elToStaticVNode(instance?.vnode.el, STATIC_DIV)
|
||||||
return createStaticVNode(fragment.join(''), fragment.length)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Object.assign(setupState, { mounted$ })
|
return Object.assign(setupState, { mounted$ })
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { h } from 'vue'
|
import { createStaticVNode, h } from 'vue'
|
||||||
import type { Component, RendererNode } from 'vue'
|
import type { Component, RendererNode, VNode } from 'vue'
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
import { isString, isPromise, isArray, isObject } from '@vue/shared'
|
import { isString, isPromise, isArray, isObject } from '@vue/shared'
|
||||||
import type { RouteLocationNormalized } from 'vue-router'
|
import type { RouteLocationNormalized } from 'vue-router'
|
||||||
@ -117,9 +117,9 @@ export function vforToArray (source: any): any[] {
|
|||||||
* Handles `<!--[-->` Fragment elements
|
* Handles `<!--[-->` Fragment elements
|
||||||
* @param element the element to retrieve the HTML
|
* @param element the element to retrieve the HTML
|
||||||
* @param withoutSlots purge all slots from the HTML string retrieved
|
* @param withoutSlots purge all slots from the HTML string retrieved
|
||||||
* @returns {string[]} An array of string which represent the content of each element. Use `.join('')` to retrieve a component vnode.el HTML
|
* @returns {string[]|undefined} An array of string which represent the content of each element. Use `.join('')` to retrieve a component vnode.el HTML
|
||||||
*/
|
*/
|
||||||
export function getFragmentHTML (element: RendererNode | null, withoutSlots = false): string[] | null {
|
export function getFragmentHTML (element: RendererNode | null, withoutSlots = false): string[] | undefined {
|
||||||
if (element) {
|
if (element) {
|
||||||
if (element.nodeName === '#comment' && element.nodeValue === '[') {
|
if (element.nodeName === '#comment' && element.nodeValue === '[') {
|
||||||
return getFragmentChildren(element, [], withoutSlots)
|
return getFragmentChildren(element, [], withoutSlots)
|
||||||
@ -131,7 +131,6 @@ export function getFragmentHTML (element: RendererNode | null, withoutSlots = fa
|
|||||||
}
|
}
|
||||||
return [element.outerHTML]
|
return [element.outerHTML]
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFragmentChildren (element: RendererNode | null, blocks: string[] = [], withoutSlots = false) {
|
function getFragmentChildren (element: RendererNode | null, blocks: string[] = [], withoutSlots = false) {
|
||||||
@ -151,6 +150,20 @@ function getFragmentChildren (element: RendererNode | null, blocks: string[] = [
|
|||||||
return blocks
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a static vnode from an element
|
||||||
|
* Default to a div if the element is not found and if a fallback is not provided
|
||||||
|
* @param el renderer node retrieved from the component internal instance
|
||||||
|
* @param staticNodeFallback fallback string to use if the element is not found. Must be a valid HTML string
|
||||||
|
*/
|
||||||
|
export function elToStaticVNode (el: RendererNode | null, staticNodeFallback?: string): VNode {
|
||||||
|
const fragment: string[] | undefined = el ? getFragmentHTML(el) : staticNodeFallback ? [staticNodeFallback] : undefined
|
||||||
|
if (fragment) {
|
||||||
|
return createStaticVNode(fragment.join(''), fragment.length)
|
||||||
|
}
|
||||||
|
return h('div')
|
||||||
|
}
|
||||||
|
|
||||||
function isStartFragment (element: RendererNode) {
|
function isStartFragment (element: RendererNode) {
|
||||||
return element.nodeName === '#comment' && element.nodeValue === '['
|
return element.nodeName === '#comment' && element.nodeValue === '['
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user