Skip to content
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

feat(nuxt): allow accessing NuxtPage ref via pageRef #19403

Merged
merged 19 commits into from
Jun 10, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix watcher infinite loop
  • Loading branch information
huang-julien committed Mar 2, 2023
commit 103011bf8096cc461ea580b5e1eccd9ecf7f82b6
48 changes: 25 additions & 23 deletions packages/nuxt/src/pages/runtime/page.ts
Original file line number Diff line number Diff line change
@@ -1,4 1,4 @@
import { computed, defineComponent, h, provide, reactive, onMounted, nextTick, Suspense, Transition, watch, getCurrentInstance, shallowRef } from 'vue'
import { computed, defineComponent, h, provide, reactive, onMounted, nextTick, Suspense, Transition, watch, getCurrentInstance, shallowRef, markRaw } from 'vue'
import type { VNode, KeepAliveProps, TransitionProps, Ref } from 'vue'
import { RouterView } from 'vue-router'
import { defu } from 'defu'
Expand Down Expand Up @@ -34,31 34,32 @@ export default defineComponent({
default: null
}
},
setup (props, { attrs }) {
setup (props, { attrs, expose }) {
const nuxtApp = useNuxtApp()

const vnode: Ref<VNode| null> = shallowRef(null)

const exposed = markRaw({}) as Record<string, any>
expose(exposed)
const instance = getCurrentInstance()!

// forward the exposed data from the route component
watch(vnode, async (vnode) => {
// await 2 ticks - one for this component and another for the RouteProvider's nextTick
await nextTick()
await nextTick()
if (vnode && vnode.component && vnode.component.exposed) {
instance.exposed = vnode.component.exposed
} else {
instance.exposed = null
}
instance.parent?.update()
})

return () => {
return h(RouterView, { name: props.name, route: props.route, ...attrs }, {
default: (routeProps: RouterViewSlotProps) => {
if (!routeProps.Component) { return }

if (process.client) {
// expose the exposed route components data to the exposed object
nextTick(async () => {
// await a second tick for the route component's tick
await nextTick()
Object.keys(exposed).forEach(key => delete exposed[key])
if (vnode.value && vnode.value.component && vnode.value.component.exposed) {
Object.assign(exposed, vnode.value.component.exposed)
}
instance.parent?.update()
})
}

const key = generateRouteKey(routeProps, props.pageKey)
const done = nuxtApp.deferHydration()

Expand Down Expand Up @@ -105,7 106,7 @@ const RouteProvider = defineComponent({
// TODO: Type props
// eslint-disable-next-line vue/require-prop-types
props: ['routeProps', 'pageKey', 'hasTransition'],
setup (props) {
setup (props, { expose }) {
// Prevent reactivity when the page will be rerendered in a different suspense fork
// eslint-disable-next-line vue/no-setup-props-destructure
const previousKey = props.pageKey
Expand All @@ -122,14 123,15 @@ const RouteProvider = defineComponent({

const vnode: Ref<VNode| null> = shallowRef(null)

const exposed = markRaw({}) as Record<string, any>

expose(exposed)
// forward the exposed data from the route component
const instance = getCurrentInstance()!
watch(vnode, async (vnode) => {
watch(vnode, async () => {
await nextTick()
if (vnode && vnode.component && vnode.component.exposed) {
instance.exposed = vnode.component.exposed
} else {
instance.exposed = null
Object.keys(exposed).forEach(key => delete exposed[key])
if (vnode.value && vnode.value.component && vnode.value.component.exposed) {
Object.assign(exposed, vnode.value.component.exposed)
}
})

Expand Down