feat(nuxt): working only with navigation (missing payload on app initialization)

This commit is contained in:
julien huang 2023-08-11 00:38:05 +02:00
parent 2331d38e24
commit 202fe052f0
3 changed files with 36 additions and 23 deletions

View File

@ -1,6 +1,6 @@
import { relative } from 'node:path'
import { Teleport, defineComponent, h } from 'vue' import { Teleport, defineComponent, h } from 'vue'
import { useNuxtApp } from '#app' import { useNuxtApp } from '#app'
import { relative } from 'path'
/** /**
* component only used with componentsIsland * component only used with componentsIsland
@ -24,7 +24,6 @@ export default defineComponent({
} }
}, },
setup (props, { slots }) { setup (props, { slots }) {
const app = useNuxtApp() const app = useNuxtApp()
const islandContext = app.ssrContext!.islandContext const islandContext = app.ssrContext!.islandContext
@ -33,14 +32,13 @@ export default defineComponent({
console.log(slot) console.log(slot)
if (process.dev) { if (process.dev) {
console.log(app) console.log(app)
const path = '__nuxt/' + relative(props.rootDir, slot.type.__file) const path = '_nuxt/' + relative(props.rootDir, slot.type.__file)
islandContext.chunks[slot.type.__name] = path islandContext.chunks[slot.type.__name] = path
} }
islandContext.propsData[props.to] = slot.props || {} islandContext.propsData[props.to] = slot.props || {}
// todo set prop in payload // todo set prop in payload
return () => { return () => {
if (props.nuxtClient) { if (props.nuxtClient) {
return [h('div', { return [h('div', {
style: 'display: contents;', style: 'display: contents;',

View File

@ -1,3 +1,4 @@
import type { Component } from 'vue'
import { Fragment, Teleport, computed, createStaticVNode, createVNode, defineComponent, getCurrentInstance, h, nextTick, onMounted, ref, watch } from 'vue' import { Fragment, Teleport, computed, createStaticVNode, createVNode, defineComponent, getCurrentInstance, h, nextTick, onMounted, ref, watch } from 'vue'
import { debounce } from 'perfect-debounce' import { debounce } from 'perfect-debounce'
import { hash } from 'ohash' import { hash } from 'ohash'
@ -6,6 +7,7 @@ import { useHead } from '@unhead/vue'
import { randomUUID } from 'uncrypto' import { randomUUID } from 'uncrypto'
import { joinURL, withQuery } from 'ufo' import { joinURL, withQuery } from 'ufo'
import type { FetchResponse } from 'ofetch' import type { FetchResponse } from 'ofetch'
import { join } from 'pathe'
// eslint-disable-next-line import/no-restricted-paths // eslint-disable-next-line import/no-restricted-paths
import type { NuxtIslandResponse } from '../../core/runtime/nitro/renderer' import type { NuxtIslandResponse } from '../../core/runtime/nitro/renderer'
@ -24,18 +26,17 @@ const SLOTNAME_RE = /nuxt-ssr-slot-name="([^"]*)"/g
const SLOT_FALLBACK_RE = /<div nuxt-slot-fallback-start="([^"]*)"[^>]*><\/div>(((?!<div nuxt-slot-fallback-end[^>]*>)[\s\S])*)<div nuxt-slot-fallback-end[^>]*><\/div>/g const SLOT_FALLBACK_RE = /<div nuxt-slot-fallback-start="([^"]*)"[^>]*><\/div>(((?!<div nuxt-slot-fallback-end[^>]*>)[\s\S])*)<div nuxt-slot-fallback-end[^>]*><\/div>/g
let id = 0 let id = 0
const getId = process.client ? () => (id++).toString() : randomUUID const getId = import.meta.client ? () => (id++).toString() : randomUUID
const components = process.client ? new Map<string, unknown>() : undefined const components = import.meta.client ? new Map<string, Component>() : undefined
async function loadComponents (paths: Record<string, string>) { async function loadComponents (source = '/', paths: Record<string, string>) {
const promises = [] const promises = []
debugger
for (const component in paths) { for (const component in paths) {
if (!(components!.has(component))) { if (!(components!.has(component))) {
promises.push((async () => { promises.push((async () => {
const c = await import('http://localhost:3000/__nuxt/components/SugarCounter.vue') const chunkSource = join(source, paths[component])
debugger const c = await import(chunkSource)
components!.set(component, c.default ?? c) components!.set(component, c.default ?? c)
})()) })())
} }
@ -109,9 +110,24 @@ export default defineComponent({
const uid = ref<string>(ssrHTML.value.match(SSR_UID_RE)?.[1] ?? randomUUID()) const uid = ref<string>(ssrHTML.value.match(SSR_UID_RE)?.[1] ?? randomUUID())
const availableSlots = computed(() => [...ssrHTML.value.matchAll(SLOTNAME_RE)].map(m => m[1])) const availableSlots = computed(() => [...ssrHTML.value.matchAll(SLOTNAME_RE)].map(m => m[1]))
// no need for reactivity
let interactiveComponentsList: Record<string, Record<string, any>> = {}
const html = computed(() => { const html = computed(() => {
const currentSlots = Object.keys(slots) const currentSlots = Object.keys(slots)
return ssrHTML.value.replace(SLOT_FALLBACK_RE, (full, slotName, content) => { let html = ssrHTML.value
if (process.client) {
const el = document.createElement('html')
el.innerHTML = html
Object.entries(interactiveComponentsList).forEach(([id]) => {
const interactiveWrapper = el.querySelector(`[nuxt-ssr-client="${id}"]`)
if (interactiveWrapper) {
interactiveWrapper.innerHTML = ''
}
})
html = el.innerHTML
}
return html.replace(SLOT_FALLBACK_RE, (full, slotName, content) => {
// remove fallback to insert slots // remove fallback to insert slots
if (currentSlots.includes(slotName)) { if (currentSlots.includes(slotName)) {
return '' return ''
@ -120,8 +136,6 @@ export default defineComponent({
}) })
}) })
// no need for reactivity
let interactiveComponentsList = {}
function setUid () { function setUid () {
uid.value = ssrHTML.value.match(SSR_UID_RE)?.[1] ?? getId() as string uid.value = ssrHTML.value.match(SSR_UID_RE)?.[1] ?? getId() as string
} }
@ -176,16 +190,16 @@ export default defineComponent({
// must await next tick for Teleport to work correctly with static node re-rendering // must await next tick for Teleport to work correctly with static node re-rendering
await nextTick() await nextTick()
} }
if (process.client) {
await loadComponents(props.source, res.chunks)
interactiveComponentsList = res.props
}
setUid() setUid()
} catch (e) { } catch (e) {
error.value = e error.value = e
} }
setUid()
if (process.client) {
await loadComponents(res.chunks)
interactiveComponentsList = res.props
}
} }
if (import.meta.hot) { if (import.meta.hot) {
@ -221,10 +235,11 @@ export default defineComponent({
} }
} }
if (process.client && html.value.includes('nuxt-ssr-client') && mounted.value) { if (process.client && html.value.includes('nuxt-ssr-client') && mounted.value) {
for (const [id, props] of Object.entries(interactiveComponentsList)) {
for (const id in interactiveComponentsList) {
const vnode = createVNode(Teleport, { to: `[nuxt-ssr-component-uid='${uid.value}'] [nuxt-ssr-client="${id}"]` }, { const vnode = createVNode(Teleport, { to: `[nuxt-ssr-component-uid='${uid.value}'] [nuxt-ssr-client="${id}"]` }, {
default: () => [h(components!.get(id.split('-')[0]), interactiveComponentsList[id])] default: () => {
return [h(components!.get(id.split('-')[0])!, props)]
}
}) })
nodes.push(vnode) nodes.push(vnode)
} }

View File

@ -424,7 +424,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
islandHead.style.push({ key: 'island-style-' + hash(tag.innerHTML), innerHTML: tag.innerHTML }) islandHead.style.push({ key: 'island-style-' + hash(tag.innerHTML), innerHTML: tag.innerHTML })
} }
} }
const islandResponse: NuxtIslandResponse = { const islandResponse: NuxtIslandResponse = {
id: islandContext.id, id: islandContext.id,
head: islandHead, head: islandHead,