Skip to content

大家好,我是村长!

从本篇开始我们将探讨 Nuxt 工程架构相关的知识,我们将学习一些进阶知识,从更高层级审视 Nuxt,从而更加得心应手地使用 Nuxt。本篇涉及内容如下:

  • Nuxt 框架生命周期;
  • 中间件使用;
  • 插件的使用;
  • 模块的使用;
  • 层的使用;
  • 项目模版使用;
  • 工程化搭建。

Nuxt 生命周期钩子

本节探讨的是 Nuxt 的生命周期钩子,为什么要了解这个知识呢?

主要是因为我们后续的项目扩展知识需要用到各种钩子函数,比如编写一个 Nuxt 插件或者模块,我们常常需要在框架执行的某个特定阶段做一些特定的事情,又或者我们需要获取并修改 Nuxt 应用实例或者 Vue 实例,等等。

比如前面章节中在错误处理时,我们就用到过钩子函数:

typescript
    export default defineNuxtPlugin((nuxtApp) => {
      nuxtApp.hook('app:error', (..._args) => {
        console.log('app:error')
      })
      nuxtApp.hook('vue:error', (..._args) => {
        console.log('vue:error')
      })
    })

Nuxt 生命周期分类

由于 Nuxt 整合了 Vue、Nitro 前后端两个运行时,再加上它自身的创建过程,因此框架生命周期钩子分为三类:

  • Nuxt 钩子;
  • Vue App 钩子;
  • Nitro App 钩子。

Nuxt 钩子

Nuxt 钩子在构建时执行,贯穿初始化和构建过程中各种工具和引擎,例如 Nuxi、Vite、Webpack、Nitro 等,主要用于编写模块时构建上下文。

基本用法如下:

typescript
    import { defineNuxtModule } from '@nuxt/kit'
    
    export default defineNuxtModule({
      setup (options, nuxt) {
        nuxt.hook('ready', async (nuxt: Nuxt) => { 
          // 在这里配置 nuxt
        })
      }
    })

我们做一个实际应用作为演示:在整合 NaiveUI 时,如果按照官方操作自动导入,则无法获得 TS 类型支持。

这个需求可以用一个模块来完成:这里利用了 prepare:types 这个钩子配置 ts:

typescript
    import { defineNuxtModule } from '@nuxt/kit'
    
    export default defineNuxtModule({
      setup (options, nuxt) {
        nuxt.hook('prepare:types', ({ tsConfig, references }) => {
          tsConfig.compilerOptions!.types.push('naive-ui/volar')
          references.push({
            path: resolve(nuxt.options.buildDir, 'types/naive-ui.d.ts'),
          })
        })
      }
    })

Vue App 钩子

会在运行时调用,主要用于编写插件,从而可以在渲染生命周期中插入代码逻辑。

基本用法如下:

typescript
    export default defineNuxtPlugin((nuxtApp) => {
      nuxtApp.hook('app:created', (vueApp) => {
        // 可以在这里修改 vue 实例
      })
    })

我们做一个实际应用作为演示:给 Vue 添加一个全局的方法 $alert。

这个需求可以用一个插件来完成:这里利用了 app:created 这个钩子配置 Vue 实例:

typescript
    // plugins/alert.ts
    export default defineNuxtPlugin((nuxtApp) => {
      nuxtApp.hook("app:created", (vueApp) => {
        vueApp.config.globalProperties.$alert = (msg: string) => {
          const message = useMessage();
          message.warning(msg);
        };
      });
    });

试用一下,index.vue:

typescript
    const ins = getCurrentInstance()
    onMounted(() => {
      ins?.proxy?.$alert('component mounted!')
    })

Nitro App 钩子

会在 Nitro 引擎运行时调用,用于编写服务端插件,从而可以修改或扩展引擎的默认行为。

例如下面插件利用 render:html 钩子修改了最终渲染的 html 内容,并在响应时打了一条日志:

typescript
    export default defineNitroPlugin((nitroApp) => {
      nitroApp.hooks.hook('render:html', (html, { event }) => {
        console.log('render:html', html)
        html.bodyAppend.push('<hr>Appended by custom plugin')
      })
      nitroApp.hooks.hook('render:response', (response, { event }) => {
        console.log('render:response', response)
      })
    })

可用钩子列表

文档中有一个比较详细的可用钩子列表,大家可以参考:

https://nuxt.com/docs/api/advanced/hooks

总结

Nuxt 中的钩子函数比 Vue 复杂多了,想要一下掌握非常困难。我建议大家可以先整体上了解一下都有哪些钩子,大概是干什么的,以后在真正有需要的时候,知道有这么个东西,就会给你带来解决问题的思路。另外学习其他优秀项目的时候,如果见到了,也可以很容易理解和消化人家的代码。

下次预告

关于生命周期就给大家讲到这里了,后续的学习中我们还会见到它们。下次内容我们将给大家介绍中间件的使用。

Released under the MIT License.