尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Nuxt 内置路由出口组件 `<NuxtPage>` 完全指南:Props、页面过渡与 Suspense 生命周期

Nuxt 内置路由出口组件 `<NuxtPage>` 完全指南:Props、页面过渡与 Suspense 生命周期 Nuxt 内置路由出口组件NuxtPage完全指南Props、页面过渡与 Suspense 生命周期【免费下载链接】nuxtthe full-stack Vue framework项目地址: https://gitcode.com/GitHub_Trending/nu/nuxtNuxtPage是 Nuxt 框架内置的路由出口组件用于渲染位于app/pages/目录下的顶层或嵌套页面是文件路由系统的核心渲染节点。本文基于当前仓库Nuxt 全栈 Vue 框架的官方 API 文档与packages/nuxt/src/pages/runtime/page.ts源码实现系统讲解它的内部结构、全部 Props、页面过渡与 keep-alive 配置方式、Suspense 下的生命周期差异、页面实例引用获取以及自定义 Props 透传技巧读完即可在应用布局中正确地配置与调优路由页面渲染。为什么应该使用NuxtPage而不是RouterView在 Nuxt 应用中NuxtPage与 Vue Router 的RouterView组件职责相似——它们都是路由组件的出口决定当前匹配的路由组件渲染在哪里。但官方文档明确建议必须使用NuxtPage而不是直接使用RouterView。NuxtPage本质上是RouterView的一层封装它的额外价值在于负责维护 Nuxt 内部的页面状态例如页面级 route 的响应式派生、page:start/page:finish等生命周期 Hook 的触发如果绕过它直接使用RouterView内部状态得不到正确处理可能导致useRoute()返回错误的路径Nuxt 会自动扫描并渲染app/pages/目录下的所有 Vue 组件因此在使用NuxtPage时无需手动传入name与route它会由框架自动解析见页面目录文档。源码中的组件定义印证了这一点在 page.ts 中组件通过h(RouterView, { name: props.name, route: props.route, ...attrs }, ...)将用户传入的name与route原样交给RouterView并通过自定义的插槽渲染逻辑接管了页面组件的挂载方式。这也解释了它为什么在 Props 类型上继承了RouterViewProps。NuxtPage的内部控制结构从实现视角看NuxtPage在客户端渲染的组件树大致等效于下面这段模板仅示意实际经组合式 API 与 VNode 构建template RouterView v-slot{ Component } !-- 可选启用页面切换过渡时 -- Transition !-- 可选启用页面状态保持时 -- KeepAlive Suspense component :isComponent / /Suspense /KeepAlive /Transition /RouterView /template这一嵌套层级对应了源码中的真实包装顺序在 page.ts 中可以看到最终渲染的 vnode 由_wrapInTransition(...)包裹wrapInKeepAlive(...)包裹Suspense组成而Suspense内部再渲染RouteProvider来提供页面级的响应式 route。三个要点默认不启用Transition与KeepAlive。在 schema 的 app 配置 中app.pageTransition与app.keepalive的默认值都是false。需要在三个层级中启用它们nuxt.config全局配置、NuxtPage组件上的transition/keepaliveProps、页面组件内通过definePageMeta按页配置。在页面组件中启用Transition时必须保证页面模板只有一个根元素否则过渡动画无法正确执行。启用 Transition 与 KeepAlive 的三种方式方式一全局配置nuxt.configexport default defineNuxtConfig({ app: { pageTransition: { name: page, mode: out-in }, keepalive: true, }, })app.pageTransition默认值为falseapp.keepalive同样默认关闭见 packages/schema/src/config/app.ts。方式二在NuxtPage组件上按使用位置配置template NuxtPage transitionpage keepalive / /template方式三在页面组件内用definePageMeta单独定义script setup langts definePageMeta({ key: route route.fullPath, transition: { name: page, mode: out-in }, keepalive: true, }) /script从源码看这些来源之间存在明确的优先级链。以 transition 为例在 page.ts 中const hasTransition !!(props.transition ?? routeProps.route.meta.pageTransition ?? defaultPageTransition)即NuxtPage的transitionProp 路由记录的meta.pageTransition来自definePageMeta 全局默认app.pageTransition。随后这些配置通过_mergeTransitionProps合并定义于 packages/nuxt/src/app/components/utils.ts并在onAfterLeave回调里触发page:transition:finishHook。keepalive 的解析逻辑类似见源码第 184 行const routeKeepaliveConfig props.keepalive ?? routeProps.route.meta.keepalive ?? defaultKeepaliveConfig另外源码中还处理了一个重要细节如果某些页面通过definePageMeta开启了 keep-alive当导航到未开启 keep-alive 的页面时Nuxt 会把已开启页面组件的名称累积到keepAliveInclude集合中并注入到有效的KeepAlive配置的include列表中从而保证切换路由时已缓存页面不被清空对应 issue #33610 的修复。这一逻辑就实现在 page.ts 的shouldAugmentInclude分支中。Suspense 下的页面生命周期差异NuxtPage在底层使用Suspense包装页面因此页面切换时组件的生命周期行为与典型 Vue 应用不同在典型 Vue 应用中新页面组件会在旧页面完全卸载之后才被挂载在 Nuxt 中由于 VueSuspense的实现机制新页面组件会在旧页面卸载之前就被挂载。这一差异主要影响同时观察旧页面卸载与新页面挂载两个生命周期的代码例如在onUnmounted/onMounted中执行清理与初始化逻辑的场景编写跨页面共享状态或动画时需留意时序。源码对该机制做了额外加固快速连续导航时组件会通过递增suspenseKey重新挂载 Suspense 边界仅在已成功 resolve 过一次之后避免未 resolve 的 Suspense 被提前拆除导致父级组件挂起对应 issue #28425 / #34683。同时客户端在初次 hydration 期间如果组件在 Suspense resolve 前被卸载例如布局切换会通过onBeforeUnmount中的done()确保 hydration 流程正常收尾。此外客户端渲染分支还做了陈旧 vnode 复用处理当导航导致某个NuxtPage暂时没有匹配的子页面组件时会优先渲染旧的 vnode 直到新路由解析完成对于已经卸载的 Suspense 边界上遗留的陈旧 vnode则通过isStaleVNode判断并丢弃避免 hydration 阶段读取空el报错对应 issue #23232。Props 详解NuxtPage的 Props 在源码 page.ts 中有完整的类型声明与运行时定义汇总如下Prop类型作用说明namestring告诉RouterView渲染匹配路由记录components选项中对应名称的组件。配合命名视图使用对应nameview.vue的命名文件约定见页面目录文档的 Named Views 一节routeRouteLocationNormalized所有组件都已解析完毕的路由位置对象pageKeystring或(route) string控制NuxtPage何时被重新渲染transitionboolean或TransitionProps为通过该NuxtPage渲染的所有页面定义全局过渡keepaliveboolean或KeepAliveProps控制通过该NuxtPage渲染的页面状态保持运行时类型校验与文档一致transition接受Boolean/Objectkeepalive同样接受Boolean/ObjectpageKey接受Function/String默认null。除了显式 PropsNuxt 会自动解析name与route因为页面系统会扫描并渲染app/pages/目录下所有 Vue 组件文件并将每个组件与对应的路由记录自动关联起来。pageKey控制页面组件的重新渲染pageKey用于控制NuxtPage何时重新渲染页面组件。理解它最直接的方式是看示例。如果传入一个恒定不变的 keyNuxtPage只会在首次挂载时渲染一次template NuxtPage page-keystatic / /template也可以基于当前路由使用动态 keyNuxtPage :page-keyroute route.fullPath /⚠️ 官方文档特别警告不要在这里使用$route对象因为它会干扰NuxtPage基于Suspense的页面渲染机制可能引发渲染异常。除了组件上直接传 ProppageKey也可以在页面组件的script中通过definePageMeta以key字段传入script setup langts definePageMeta({ key: route route.fullPath, }) /script关于pageKey的默认行为可以从工具函数 packages/nuxt/src/pages/runtime/utils.ts 的generateRouteKey中看出端倪当没有显式传入pageKey、路由也没有meta.key时Nuxt 默认基于路由匹配到的路径将:param等动态段替换为实际参数值生成 key——这意味着默认情况下同一个页面组件在不同参数如/users/1与/users/2之间切换会被判定为不同 key 而触发重渲染。用key: route route.fullPath显式定义则可以让 key 精确跟随完整路径。另外在客户端当pageKey发生变化时源码会通过 watcher 触发page:loading:startHook见 page.ts 第 83-89 行并在页面 resolve 后依次触发page:finish与page:loading:end从而实现与useLoadingIndicator加载进度条的联动。获取页面组件实例ref 与 pageRef由于NuxtPage内部有多层包装直接给NuxtPage绑ref拿到的并不是页面组件本身而是NuxtPage组件实例。Nuxt 通过expose({ pageRef })将真正渲染的页面组件实例暴露出来因此需要通过ref.value.pageRef访问。script setup langts const page ref() function logFoo () { page.value.pageRef.foo() } /script template NuxtPage refpage / /template对应的页面组件需要把方法暴露出去才能被外部调用script setup langts const foo () { console.log(foo method called) } defineExpose({ foo, }) /script源码实现中pageRef定义于setup中并通过expose({ pageRef })暴露同时通过RouteProvider的vnodeRef传入并作为页面 vnode 的ref绑定见 route-provider.ts 第 73 行h(props.vnode, { ref: props.vnodeRef })从而保证pageRef始终指向实际渲染的页面组件实例。向页面透传自定义 PropsNuxtPage除了上述内置 Props 外还接受任何自定义 Props并会把它们继续向下传递到页面组件。例如在布局入口传入一个自定义 Propfoobartemplate NuxtPage :foobar123 / /template在页面组件中可以通过defineProps正常接收script setup langts const props defineProps{ foobar: number }() console.log(props.foobar) // 输出: 123 /script如果页面组件没有用defineProps声明该 Prop仍然可以通过attrsuseAttrs()拿到透传值script setup langts const attrs useAttrs() console.log(attrs.foobar) // 输出: 123 /script这在实现布局统一注入页面公共参数如页面标题 key、分区标识等时非常实用。值得一提的是源码中NuxtPage设置了inheritAttrs: false并在渲染时把除内置 Props 外的attrs原样展开传给RouterViewh(RouterView, { ..., ...attrs }, ...)再经由插槽与RouteProvider传递到页面 vnode这正是自定义 Props 能够一路透传到页面组件的底层原因。源码中的配套实现与测试围绕NuxtPage当前仓库的源码与测试形成了完整的印证链条组件主实现packages/nuxt/src/pages/runtime/page.ts —— 涵盖 Props 声明、Suspense包装、transition/keepalive 合并与优先级、suspenseKey重挂载策略、page:start/page:finish/page:loading:end等 Hook 触发、hydration 期间错误 Hook 的注册等。路由 key 与 KeepAlive 工具packages/nuxt/src/pages/runtime/utils.ts ——generateRouteKey默认 key 推导与pageKey覆盖、wrapInKeepAlive。页面级响应式 route 提供者packages/nuxt/src/app/components/route-provider.ts ——RouteProvider通过provide(PageRouteSymbol, ...)向页面提供派生自当前渲染分叉的 route并承担pageRef绑定。过渡合并工具packages/nuxt/src/app/components/utils.ts ——_mergeTransitionProps与_wrapInTransition。全局默认配置packages/schema/src/config/app.ts ——app.pageTransition: false与app.keepalive: false的默认值。端到端测试test/nuxt/nuxt-page.test.ts —— 覆盖不同嵌套深度路由下NuxtPage的挂载行为、setup/render 次数统计等可作为理解其生命周期语义的补充材料该测试文件超过 1100 行还包含多层级嵌套与异步 setup 场景的回归用例。若需要进一步了解页面文件到路由的映射关系、命名视图nameview.vue约定以及definePageMeta的全部可用字段可继续阅读 页面目录文档 与 definePageMeta 工具文档。【免费下载链接】nuxtthe full-stack Vue framework项目地址: https://gitcode.com/GitHub_Trending/nu/nuxt创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表