mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-21 21:25:11 +00:00
feat(nuxt): allow accessing NuxtPage
ref via pageRef
(#19403)
This commit is contained in:
parent
c6a62268c3
commit
319935fc95
@ -46,6 +46,23 @@ definePageMeta({
|
||||
|
||||
:button-link[Open on StackBlitz]{href="https://stackblitz.com/github/nuxt/nuxt/tree/main/examples/routing/pages?file=app.vue" blank}
|
||||
|
||||
## Accessing a page's component ref
|
||||
|
||||
To get the ref of a page component, access it through `ref.value.pageRef`
|
||||
|
||||
````html
|
||||
<template>
|
||||
<NuxtPage ref="page" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const page = ref()
|
||||
function logFoo () {
|
||||
page.value.pageRef.foo()
|
||||
}
|
||||
</script>
|
||||
````
|
||||
|
||||
## Custom Props
|
||||
|
||||
In addition, `NuxtPage` also accepts custom props that you may need to pass further down the hierarchy. These custom props are accessible via `attrs` in the Nuxt app.
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Suspense, Transition, computed, defineComponent, h, nextTick, onMounted, provide, reactive } from 'vue'
|
||||
import { Suspense, Transition, computed, defineComponent, h, nextTick, onMounted, provide, reactive, ref } from 'vue'
|
||||
import type { KeepAliveProps, TransitionProps, VNode } from 'vue'
|
||||
import { RouterView } from '#vue-router'
|
||||
import { defu } from 'defu'
|
||||
@ -34,8 +34,12 @@ export default defineComponent({
|
||||
default: null
|
||||
}
|
||||
},
|
||||
setup (props, { attrs }) {
|
||||
setup (props, { attrs, expose }) {
|
||||
const nuxtApp = useNuxtApp()
|
||||
const pageRef = ref()
|
||||
|
||||
expose({ pageRef })
|
||||
|
||||
return () => {
|
||||
return h(RouterView, { name: props.name, route: props.route, ...attrs }, {
|
||||
default: (routeProps: RouterViewSlotProps) => {
|
||||
@ -57,7 +61,7 @@ export default defineComponent({
|
||||
suspensible: true,
|
||||
onPending: () => nuxtApp.callHook('page:start', routeProps.Component),
|
||||
onResolve: () => { nextTick(() => nuxtApp.callHook('page:finish', routeProps.Component).finally(done)) }
|
||||
}, { default: () => h(RouteProvider, { key, routeProps, pageKey: key, hasTransition } as {}) })
|
||||
}, { default: () => h(RouteProvider, { key, routeProps, pageKey: key, hasTransition, pageRef } as {}) })
|
||||
)).default()
|
||||
}
|
||||
})
|
||||
@ -81,7 +85,7 @@ const RouteProvider = defineComponent({
|
||||
name: 'RouteProvider',
|
||||
// TODO: Type props
|
||||
// eslint-disable-next-line vue/require-prop-types
|
||||
props: ['routeProps', 'pageKey', 'hasTransition'],
|
||||
props: ['routeProps', 'pageKey', 'hasTransition', 'pageRef'],
|
||||
setup (props) {
|
||||
// Prevent reactivity when the page will be rerendered in a different suspense fork
|
||||
// eslint-disable-next-line vue/no-setup-props-destructure
|
||||
@ -111,11 +115,11 @@ const RouteProvider = defineComponent({
|
||||
|
||||
return () => {
|
||||
if (process.dev && process.client) {
|
||||
vnode = h(props.routeProps.Component)
|
||||
vnode = h(props.routeProps.Component, { ref: props.pageRef })
|
||||
return vnode
|
||||
}
|
||||
|
||||
return h(props.routeProps.Component)
|
||||
return h(props.routeProps.Component, { ref: props.pageRef })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -326,6 +326,27 @@ describe('pages', () => {
|
||||
await expectNoClientErrors('/client-only-explicit-import')
|
||||
})
|
||||
|
||||
it('/wrapper-expose/page', async () => {
|
||||
await expectNoClientErrors('/wrapper-expose/page')
|
||||
let lastLog: string|undefined
|
||||
const page = await createPage('/wrapper-expose/page')
|
||||
page.on('console', (log) => {
|
||||
lastLog = log.text()
|
||||
})
|
||||
page.on('pageerror', (log) => {
|
||||
lastLog = log.message
|
||||
})
|
||||
await page.waitForLoadState('networkidle')
|
||||
await page.locator('#log-foo').click()
|
||||
expect(lastLog === 'bar').toBeTruthy()
|
||||
// change page
|
||||
await page.locator('#to-hello').click()
|
||||
await page.locator('#log-foo').click()
|
||||
expect(lastLog?.includes('.foo is not a function')).toBeTruthy()
|
||||
await page.locator('#log-hello').click()
|
||||
expect(lastLog === 'world').toBeTruthy()
|
||||
})
|
||||
|
||||
it('client-fallback', async () => {
|
||||
const classes = [
|
||||
'clientfallback-non-stateful-setup',
|
||||
|
@ -35,7 +35,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
|
||||
|
||||
it('default server bundle size', async () => {
|
||||
stats.server = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
|
||||
expect(roundToKilobytes(stats.server.totalBytes)).toMatchInlineSnapshot('"61.9k"')
|
||||
expect(roundToKilobytes(stats.server.totalBytes)).toMatchInlineSnapshot('"61.8k"')
|
||||
|
||||
const modules = await analyzeSizes('node_modules/**/*', serverDir)
|
||||
expect(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"2286k"')
|
||||
|
24
test/fixtures/basic/pages/wrapper-expose/page.vue
vendored
Normal file
24
test/fixtures/basic/pages/wrapper-expose/page.vue
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div>
|
||||
<NuxtPage ref="page" />
|
||||
|
||||
<button id="log-hello" @click="logHello">
|
||||
hello
|
||||
</button>
|
||||
<button id="log-foo" @click="logFoo">
|
||||
foo
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const page = ref()
|
||||
|
||||
function logFoo () {
|
||||
page.value.pageRef.foo()
|
||||
}
|
||||
|
||||
function logHello () {
|
||||
page.value.pageRef.hello()
|
||||
}
|
||||
</script>
|
21
test/fixtures/basic/pages/wrapper-expose/page/another.vue
vendored
Normal file
21
test/fixtures/basic/pages/wrapper-expose/page/another.vue
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div>
|
||||
<p>
|
||||
hello
|
||||
</p>
|
||||
<NuxtLink id="to-foo" to="/wrapper-expose/page">
|
||||
to foo
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
function hello () {
|
||||
console.log('world')
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
hello
|
||||
})
|
||||
</script>
|
19
test/fixtures/basic/pages/wrapper-expose/page/index.vue
vendored
Normal file
19
test/fixtures/basic/pages/wrapper-expose/page/index.vue
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div>
|
||||
<p>foo</p>
|
||||
<NuxtLink id="to-hello" to="/wrapper-expose/page/another">
|
||||
to hello
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
function foo () {
|
||||
console.log('bar')
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
foo
|
||||
})
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user