找回密码
 立即注册
首页 业界区 业界 Nuxt 3组件开发与管理

Nuxt 3组件开发与管理

士沌 2025-6-6 15:25:15
title: Nuxt 3组件开发与管理
date: 2024/6/20
updated: 2024/6/20
author:<template>
<template>
  <input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>cmdragon
excerpt:
摘要:本文深入探讨了Nuxt 3的组件开发与管理,从基础概念、安装配置、目录结构、组件分类与开发实践、生命周期与优化,到测试与维护策略。详细介绍了Nuxt 3的核心特点,如服务器端渲染(SSR)、静态站点生成(SSG)以及与Vue生态系统的无缝集成。文章以Nuxt 3为基础,指导开发者如何高效构建高性能、可维护的Vue应用程序。内容涵盖了基本组件的定义与分类、独立组件与函数式组件的示例、Props和Slots的使用、Composition API的引入,以及组件的生命周期与优化方法。同时,文章还提供了组件开发的实践案例,包括自定义组件开发、异步加载组件、事件与方法的使用,以及组件测试与文档化指南。通过结构化的目录组织与详细的代码示例,旨在帮助开发者高效管理与维护组件,实现代码的复用与模块化。
categories:

  • 前端开发
tags:

  • Vue
  • Nuxt3
  • 组件开发
  • 管理策略
  • 生命周期
  • 性能优化
  • 测试文档
1.png

2.jpeg

扫码关注或者微信搜一搜:编程智域 前端至全栈交流与成长
第1章:Nuxt 3简介

1.1 Nuxt 3概述
1.1.1 什么是Nuxt.js Nuxt.js是一个基于Vue.js的开源框架,专为构建高性能、可维护的服务器渲染和静态站点生成应用而设计。Nuxt 3是其最新版本,它采用Vue 3和TSX(TypeScript的扩展)作为基础,提供了更简洁的API和更好的性能。
1.1.2 Nuxt 3特点

  • 服务器端渲染(SSR):提高SEO和初始加载速度。
  • 前端应用(SPA):现代的单页应用体验。
  • 静态站点生成(SSG):支持快速的静态内容部署。
  • 集成强大:与Vue生态系统无缝集成,如Vuex、Vuex-ORM等。
1.2 安装与配置
1.2.1 安装Nuxt 3 在命令行中运行以下命令安装Nuxt 3 CLI:
  1. npm install -g npx
  2. npx create-nuxt-app my-app
复制代码
或者使用Yarn:
  1. yarn global add create-nuxt-app
  2. create-nuxt-app my-app
复制代码
1.2.2 配置基本项目

  • 进入项目目录:cd my-app
  • 配置nuxt.config.ts:设置基础配置,如目标环境、SSR、SSG等。
  1. // nuxt.config.ts
  2. export default {
  3. <template>
  4. <template>
  5.   <input type="text" @input="handleInput" />
  6. </template><ChildComponent @custom-event="handleCustomEvent" />
  7. </template>target: 'server', // 或者 'static', 根据需求选择
  8. <template>
  9. <template>
  10.   <input type="text" @input="handleInput" />
  11. </template><ChildComponent @custom-event="handleCustomEvent" />
  12. </template>// 更多配置...
  13. }
复制代码
1.3 基本架构介绍
1.3.1 项目结构

  • pages:存放所有路由相关的组件,如pages/index.vue。
  • components:存放可复用的Vue组件。
  • layouts:定义页面布局,如layouts/default.vue。
  • plugins:全局或局部插件。
  • store:Vuex状态管理。
1.3.2 主要组件类型

  • Layouts: 共享的页面结构,如头部、尾部和主要内容区域。
  • Pages: 与特定路由关联的组件,处理业务逻辑和视图。
  • Modules: 对项目进行分模块管理,如API、状态管理等。
1.3.3 CLI命令

  • nuxt generate:生成静态站点。
  • nuxt start:启动开发服务器。
  • nuxt build:构建生产环境。
第2章:Nuxt 3组件基础

2.1 组件定义与分类

2.1.1 组件定义 在Nuxt 3中,组件是可复用的代码块,可以是Vue组件或自定义的函数式组件。Vue组件使用.vue文件扩展名,而函数式组件使用[/code]2.2.2 函数式组件示例
  1. [/code][b]2.3 Props和Slots的使用[/b]
  2. [b]2.3.1 Props(属性)[/b]
  3. [list]
  4. [*]Props是组件间的通信方式,父组件向子组件传递数据。
  5. [*]props对象定义组件接受的属性,如上述按钮组件的buttonText和isActive。
  6. [/list][b]2.3.2 Slots(插槽)[/b]
  7. [list]
  8. [*]Slots用于在组件中定义可替换的部分,父组件可以通过标签传递内容给子组件。
  9. [*]子组件可以使用来定义默认插槽或命名插槽。
  10. [/list][b]2.4 使用Composition API[/b]
  11. [b]2.4.1 Refs(响应式引用)[/b]
  12. [list]
  13. [*]Composition API引入了ref,用于创建响应式的变量,如上述isActive和buttonText。
  14. [/list][b]2.4.2 Setup Function(设置函数)[/b]
  15. [list]
  16. [*]setup函数是组件的逻辑核心,替代了.vue文件中的export default部分,用于导入和导出函数、变量和方法。
  17. [/list][code]
复制代码
第3章:组件开发实践

3.1 自定义组件开发
3.1.1 组件结构

在Nuxt 3中,自定义组件通常包含、[/code]在父组件中,可以使用v-bind来传递title属性:
  1. <template>
  2. <template>
  3. <template>
  4.   <input type="text" @input="handleInput" />
  5. </template><ChildComponent @custom-event="handleCustomEvent" />
  6. </template>
  7. <template>
  8. <template>
  9.   <input type="text" @input="handleInput" />
  10. </template><ChildComponent @custom-event="handleCustomEvent" />
  11. </template><template>
  12. <template>
  13.   <input type="text" @input="handleInput" />
  14. </template><ChildComponent @custom-event="handleCustomEvent" />
  15. </template><MyComponent :title="'Custom Title'" />
  16. <template>
  17. <template>
  18.   <input type="text" @input="handleInput" />
  19. </template><ChildComponent @custom-event="handleCustomEvent" />
  20. </template>
  21. </template>
复制代码
3.2 异步加载组件
在 Nuxt 3 中,可以使用异步加载来延迟加载组件,从而提高应用程序的性能和用户体验。
3.2.1 动态导入

可以使用动态导入来异步加载组件。在[/code]在上面的例子中,AsyncComponent是一个动态导入的组件,只有在点击按钮时才会加载。
3.2.2 异步数据

如果组件需要加载异步数据,可以在[/code]在上面的例子中,点击按钮时会调用loadData方法,加载异步数据,并在数据加载完成后渲染AsyncDataComponent组件。
3.3 事件与方法
方法(Methods)


在 Nuxt 3 中,组件内的方法是通过在 [/code]在这个例子中,increment 方法会在点击按钮时被调用,使得 count 的值增加。
事件(Events)

自定义事件

组件可以通过 $emit 方法发出自定义事件,父组件可以通过监听这些事件来响应。
以下是如何在子组件中发出一个自定义事件的示例:
  1. <template>
  2. <template>
  3. <template>
  4.   <input type="text" @input="handleInput" />
  5. </template><ChildComponent @custom-event="handleCustomEvent" />
  6. </template><button @click="sendEvent">发送事件</button>
  7. </template>
复制代码
父组件可以像这样监听这个事件:
  1. <template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>
复制代码
原生事件

你还可以在组件上直接监听原生 DOM 事件,如下所示:
  1. <template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>
复制代码
使用 Vuex 中的事件和方法

如果在使用 Vuex 来管理状态,你可能会在组件中调用 Vuex 的 actions 或 mutations。
  1. <template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>增加
复制代码
在这个例子中,当按钮被点击时,会调用 Vuex store 中的 increment action。
第4章:组件组织与管理

4.1 组件目录结构
在 Nuxt.js 3.x 中,组件目录结构非常灵活,你可以根据项目需求自定义组件的存放位置。但是,为了保持代码结构的清晰和可维护,建议按照以下目录结构组织组件:

  • 通用组件:将所有可复用的组件放在components目录中。例如:
    1. components/<template>
    2. <template>
    3.   <input type="text" @input="handleInput" />
    4. </template><ChildComponent @custom-event="handleCustomEvent" />
    5. </template>- Button.vue<template>
    6. <template>
    7.   <input type="text" @input="handleInput" />
    8. </template><ChildComponent @custom-event="handleCustomEvent" />
    9. </template>- Input.vue<template>
    10. <template>
    11.   <input type="text" @input="handleInput" />
    12. </template><ChildComponent @custom-event="handleCustomEvent" />
    13. </template>- Icon.vue
    复制代码
  • 页面组件:每个页面组件都可以作为一个单独的.vue文件存在,并放在pages目录中。例如:
    1. pages/<template>
    2. <template>
    3.   <input type="text" @input="handleInput" />
    4. </template><ChildComponent @custom-event="handleCustomEvent" />
    5. </template>- index.vue<template>
    6. <template>
    7.   <input type="text" @input="handleInput" />
    8. </template><ChildComponent @custom-event="handleCustomEvent" />
    9. </template>- about.vue<template>
    10. <template>
    11.   <input type="text" @input="handleInput" />
    12. </template><ChildComponent @custom-event="handleCustomEvent" />
    13. </template>- products/<template>
    14. <template>
    15.   <input type="text" @input="handleInput" />
    16. </template><ChildComponent @custom-event="handleCustomEvent" />
    17. </template><template>
    18. <template>
    19.   <input type="text" @input="handleInput" />
    20. </template><ChildComponent @custom-event="handleCustomEvent" />
    21. </template>- index.vue<template>
    22. <template>
    23.   <input type="text" @input="handleInput" />
    24. </template><ChildComponent @custom-event="handleCustomEvent" />
    25. </template><template>
    26. <template>
    27.   <input type="text" @input="handleInput" />
    28. </template><ChildComponent @custom-event="handleCustomEvent" />
    29. </template>- product-1.vue
    复制代码
  • 布局组件:布局组件可以放在layouts目录中。例如:
    1. layouts/<template>
    2. <template>
    3.   <input type="text" @input="handleInput" />
    4. </template><ChildComponent @custom-event="handleCustomEvent" />
    5. </template>- default.vue<template>
    6. <template>
    7.   <input type="text" @input="handleInput" />
    8. </template><ChildComponent @custom-event="handleCustomEvent" />
    9. </template>- admin.vue
    复制代码
  • 插件:如果你需要在组件中使用第三方库或自定义插件,可以将它们放在plugins目录中。例如:
    1. plugins/<template>
    2. <template>
    3.   <input type="text" @input="handleInput" />
    4. </template><ChildComponent @custom-event="handleCustomEvent" />
    5. </template>- third-party.js<template>
    6. <template>
    7.   <input type="text" @input="handleInput" />
    8. </template><ChildComponent @custom-event="handleCustomEvent" />
    9. </template>- custom.js
    复制代码
  • 模块:如果你需要在项目中使用自定义模块,可以将它们放在modules目录中。例如:
    1. modules/<template>
    2. <template>
    3.   <input type="text" @input="handleInput" />
    4. </template><ChildComponent @custom-event="handleCustomEvent" />
    5. </template>- custom-module.js
    复制代码
4.2 分级组件和模块化
在 Nuxt 3 中,组件化和模块化是两个核心概念,它们有助于构建可维护和可扩展的应用程序。以下是关于 Nuxt 3 中的分级组件和模块化的详细说明:
分级组件(Hierarchical Components)

分级组件指的是组件之间的嵌套关系,这种关系可以帮助我们构建复杂的用户界面。在 Nuxt 3 中,组件可以按照以下方式组织:

  • 根组件:通常位于 pages 目录下的 .vue 文件,它们是页面的入口。
  • 子组件:可以在页面组件中或其它组件中嵌套使用的组件。子组件通常放在 components 目录下,并可以进一步细分为子目录,以反映它们的功能或用途。
以下是一个分级组件的例子:
  1. components/<template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>- Header/<template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template><template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template>- Logo.vue<template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template><template>
  18. <template>
  19.   <input type="text" @input="handleInput" />
  20. </template><ChildComponent @custom-event="handleCustomEvent" />
  21. </template>- Navigation.vue<template>
  22. <template>
  23.   <input type="text" @input="handleInput" />
  24. </template><ChildComponent @custom-event="handleCustomEvent" />
  25. </template>- Footer/<template>
  26. <template>
  27.   <input type="text" @input="handleInput" />
  28. </template><ChildComponent @custom-event="handleCustomEvent" />
  29. </template><template>
  30. <template>
  31.   <input type="text" @input="handleInput" />
  32. </template><ChildComponent @custom-event="handleCustomEvent" />
  33. </template>- Contact.vue<template>
  34. <template>
  35.   <input type="text" @input="handleInput" />
  36. </template><ChildComponent @custom-event="handleCustomEvent" />
  37. </template><template>
  38. <template>
  39.   <input type="text" @input="handleInput" />
  40. </template><ChildComponent @custom-event="handleCustomEvent" />
  41. </template>- SocialLinks.vue<template>
  42. <template>
  43.   <input type="text" @input="handleInput" />
  44. </template><ChildComponent @custom-event="handleCustomEvent" />
  45. </template>- Hero.vue<template>
  46. <template>
  47.   <input type="text" @input="handleInput" />
  48. </template><ChildComponent @custom-event="handleCustomEvent" />
  49. </template>- ArticleList.vue
复制代码
在这个结构中,Header 和 Footer 目录包含了与它们相关的子组件,而 Hero 和 ArticleList 是独立的组件。
模块化(Modularization)

模块化是一种将代码分解成可重用模块的方法,每个模块都专注于一个特定的功能。在 Nuxt 3 中,模块化可以通过以下方式实现:

  • Nuxt 模块:Nuxt 3 支持通过 modules 目录或 nuxt.config.ts 文件自动注册本地或第三方模块。这些模块可以扩展 Nuxt 的核心功能或提供额外的工具。
  • 可复用逻辑:将可复用的逻辑(如 API 调用、状态管理、工具函数)放在单独的文件或目录中,然后在需要的地方导入它们。
以下是一个模块化的例子:
  1. composables/<template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>- useApi.js<template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template>- useAuth.js<template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template>- useUtils.jsstore/<template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template>- index.jsutils/<template>
  18. <template>
  19.   <input type="text" @input="handleInput" />
  20. </template><ChildComponent @custom-event="handleCustomEvent" />
  21. </template>- helpers.js<template>
  22. <template>
  23.   <input type="text" @input="handleInput" />
  24. </template><ChildComponent @custom-event="handleCustomEvent" />
  25. </template>- validators.js
复制代码
在这个结构中,composables 目录包含了可复用的组合式函数,store 目录包含了状态管理逻辑,而 utils 目录包含了工具函数和验证器。
结合分级组件和模块化

在 Nuxt 3 中,你可以将分级组件和模块化结合起来,创建一个既清晰又易于维护的项目结构:

  • 使用分级组件来组织你的用户界面。
  • 使用模块化来组织你的应用逻辑。
通过这种方式,你可以确保每个组件都专注于展示逻辑,而模块则处理应用的业务逻辑,从而实现关注点分离。例如:
  1. components/<template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>- Header.vue<template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template>- Header/<template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template><template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template>- Logo.vue<template>
  18. <template>
  19.   <input type="text" @input="handleInput" />
  20. </template><ChildComponent @custom-event="handleCustomEvent" />
  21. </template><template>
  22. <template>
  23.   <input type="text" @input="handleInput" />
  24. </template><ChildComponent @custom-event="handleCustomEvent" />
  25. </template>- Navigation.vue<template>
  26. <template>
  27.   <input type="text" @input="handleInput" />
  28. </template><ChildComponent @custom-event="handleCustomEvent" />
  29. </template>- Footer.vue<template>
  30. <template>
  31.   <input type="text" @input="handleInput" />
  32. </template><ChildComponent @custom-event="handleCustomEvent" />
  33. </template>- Footer/<template>
  34. <template>
  35.   <input type="text" @input="handleInput" />
  36. </template><ChildComponent @custom-event="handleCustomEvent" />
  37. </template><template>
  38. <template>
  39.   <input type="text" @input="handleInput" />
  40. </template><ChildComponent @custom-event="handleCustomEvent" />
  41. </template>- Contact.vue<template>
  42. <template>
  43.   <input type="text" @input="handleInput" />
  44. </template><ChildComponent @custom-event="handleCustomEvent" />
  45. </template><template>
  46. <template>
  47.   <input type="text" @input="handleInput" />
  48. </template><ChildComponent @custom-event="handleCustomEvent" />
  49. </template>- SocialLinks.vue<template>
  50. <template>
  51.   <input type="text" @input="handleInput" />
  52. </template><ChildComponent @custom-event="handleCustomEvent" />
  53. </template>- ArticleList.vuecomposables/<template>
  54. <template>
  55.   <input type="text" @input="handleInput" />
  56. </template><ChildComponent @custom-event="handleCustomEvent" />
  57. </template>- useHeader.js<template>
  58. <template>
  59.   <input type="text" @input="handleInput" />
  60. </template><ChildComponent @custom-event="handleCustomEvent" />
  61. </template>- useFooter.jsstore/<template>
  62. <template>
  63.   <input type="text" @input="handleInput" />
  64. </template><ChildComponent @custom-event="handleCustomEvent" />
  65. </template>- index.jsutils/<template>
  66. <template>
  67.   <input type="text" @input="handleInput" />
  68. </template><ChildComponent @custom-event="handleCustomEvent" />
  69. </template>- helpers.js
复制代码
在这个结构中,Header 和 Footer 组件可以导入对应的 useHeader 和 useFooter 组合式函数来获取所需的数据和逻辑。这样的组织方式有助于保持代码的清晰和可维护性。
4.3 使用Layouts和Modules
Nuxt 3 中的 Layouts 和 Modules 是两个重要的概念,它们可以帮助你构建更加灵活和可扩展的应用程序。以下是关于 Nuxt 3 中的 Layouts 和 Modules 的详细说明:
Layouts

Layouts 是一种在 Nuxt 中定义应用程序布局的方式,它们可以让你在不同页面之间共享相同的布局。在 Nuxt 3 中,你可以在 layouts 目录中创建自定义的布局。
以下是一个简单的 Layouts 示例:

  • 创建一个名为 layouts 的目录,并在其中创建一个名为 default.vue 的文件:
    1. layouts/<template>
    2. <template>
    3.   <input type="text" @input="handleInput" />
    4. </template><ChildComponent @custom-event="handleCustomEvent" />
    5. </template>- default.vue
    复制代码
  • 在 default.vue 文件中定义你的布局:
    1. <template>
    2. <template>
    3. <template>
    4.   <input type="text" @input="handleInput" />
    5. </template><ChildComponent @custom-event="handleCustomEvent" />
    6. </template>
    7. <template>
    8. <template>
    9.   <input type="text" @input="handleInput" />
    10. </template><ChildComponent @custom-event="handleCustomEvent" />
    11. </template><template>
    12. <template>
    13.   <input type="text" @input="handleInput" />
    14. </template><ChildComponent @custom-event="handleCustomEvent" />
    15. </template><MyComponent :title="'Custom Title'" />
    16. <template>
    17. <template>
    18.   <input type="text" @input="handleInput" />
    19. </template><ChildComponent @custom-event="handleCustomEvent" />
    20. </template>
    21. </template><template>
    22. <template>
    23.   <input type="text" @input="handleInput" />
    24. </template><ChildComponent @custom-event="handleCustomEvent" />
    25. </template><template>
    26. <template>
    27.   <input type="text" @input="handleInput" />
    28. </template><ChildComponent @custom-event="handleCustomEvent" />
    29. </template>[size=6]我的应用程序[/size]
    30. <template>
    31. <template>
    32. <template>
    33.   <input type="text" @input="handleInput" />
    34. </template><ChildComponent @custom-event="handleCustomEvent" />
    35. </template>
    36. <template>
    37. <template>
    38.   <input type="text" @input="handleInput" />
    39. </template><ChildComponent @custom-event="handleCustomEvent" />
    40. </template><template>
    41. <template>
    42.   <input type="text" @input="handleInput" />
    43. </template><ChildComponent @custom-event="handleCustomEvent" />
    44. </template><MyComponent :title="'Custom Title'" />
    45. <template>
    46. <template>
    47.   <input type="text" @input="handleInput" />
    48. </template><ChildComponent @custom-event="handleCustomEvent" />
    49. </template>
    50. </template><template>
    51. <template>
    52. <template>
    53.   <input type="text" @input="handleInput" />
    54. </template><ChildComponent @custom-event="handleCustomEvent" />
    55. </template>
    56. <template>
    57. <template>
    58.   <input type="text" @input="handleInput" />
    59. </template><ChildComponent @custom-event="handleCustomEvent" />
    60. </template><template>
    61. <template>
    62.   <input type="text" @input="handleInput" />
    63. </template><ChildComponent @custom-event="handleCustomEvent" />
    64. </template><MyComponent :title="'Custom Title'" />
    65. <template>
    66. <template>
    67.   <input type="text" @input="handleInput" />
    68. </template><ChildComponent @custom-event="handleCustomEvent" />
    69. </template>
    70. </template><template>
    71. <template>
    72. <template>
    73.   <input type="text" @input="handleInput" />
    74. </template><ChildComponent @custom-event="handleCustomEvent" />
    75. </template>
    76. <template>
    77. <template>
    78.   <input type="text" @input="handleInput" />
    79. </template><ChildComponent @custom-event="handleCustomEvent" />
    80. </template><template>
    81. <template>
    82.   <input type="text" @input="handleInput" />
    83. </template><ChildComponent @custom-event="handleCustomEvent" />
    84. </template><MyComponent :title="'Custom Title'" />
    85. <template>
    86. <template>
    87.   <input type="text" @input="handleInput" />
    88. </template><ChildComponent @custom-event="handleCustomEvent" />
    89. </template>
    90. </template><template>
    91. <template>
    92.   <input type="text" @input="handleInput" />
    93. </template><ChildComponent @custom-event="handleCustomEvent" />
    94. </template><template>
    95. <template>
    96.   <input type="text" @input="handleInput" />
    97. </template><ChildComponent @custom-event="handleCustomEvent" />
    98. </template>© 2023 我的应用程序
    99. <template>
    100. <template>
    101.   <input type="text" @input="handleInput" />
    102. </template><ChildComponent @custom-event="handleCustomEvent" />
    103. </template><template>
    104. <template>
    105.   <input type="text" @input="handleInput" />
    106. </template><ChildComponent @custom-event="handleCustomEvent" />
    107. </template><template>
    108. <template>
    109.   <input type="text" @input="handleInput" />
    110. </template><ChildComponent @custom-event="handleCustomEvent" />
    111. </template>
    复制代码
  • 在你的页面中使用 Layouts:
    1. <template>
    2. <template>
    3.   <input type="text" @input="handleInput" />
    4. </template><ChildComponent @custom-event="handleCustomEvent" />
    5. </template>[size=5]我的页面[/size]
    6. <template>
    7. <template>
    8.   <input type="text" @input="handleInput" />
    9. </template><ChildComponent @custom-event="handleCustomEvent" />
    10. </template>这是我的页面内容。
    复制代码
    在这个示例中,我们在 layouts 目录中创建了一个名为 default.vue 的布局,并在其中定义了一个包含 header、main 和 footer 的结构。在页面中,我们可以使用  插槽来显示页面的内容。
Modules

Modules 是一种在 Nuxt 中扩展应用程序功能的方式,它们可以让你在整个应用程序中使用自定义的功能或第三方插件。在 Nuxt 3 中,你可以使用 modules 目录或 nuxt.config.ts 文件来注册本地或第三方模块。
以下是一个简单的 Modules 示例:

  • 创建一个名为 modules 的目录,并在其中创建一个名为 example.ts 的文件:
    1. modules/<template>
    2. <template>
    3.   <input type="text" @input="handleInput" />
    4. </template><ChildComponent @custom-event="handleCustomEvent" />
    5. </template>- example.ts
    复制代码
  • 在 example.ts 文件中定义你的模块:
    1. import { ModuleOptions } from '@nuxt/types'export default function exampleModule(options: ModuleOptions) {<template>
    2. <template>
    3.   <input type="text" @input="handleInput" />
    4. </template><ChildComponent @custom-event="handleCustomEvent" />
    5. </template>this.nuxt.hook('render:route', (route) => {<template>
    6. <template>
    7.   <input type="text" @input="handleInput" />
    8. </template><ChildComponent @custom-event="handleCustomEvent" />
    9. </template><template>
    10. <template>
    11.   <input type="text" @input="handleInput" />
    12. </template><ChildComponent @custom-event="handleCustomEvent" />
    13. </template>console.log(`渲染路由:${route.fullPath}`)<template>
    14. <template>
    15.   <input type="text" @input="handleInput" />
    16. </template><ChildComponent @custom-event="handleCustomEvent" />
    17. </template>})}
    复制代码
    在这个示例中,我们创建了一个名为 exampleModule 的函数,它接收一个 ModuleOptions 类型的参数。在函数中,我们使用 this.nuxt.hook 钩子函数来注册一个名为 render:route 的钩子,并在钩子函数中记录当前渲染的路由。
  • 在 nuxt.config.ts 文件中注册你的模块:
    1. import { defineNuxtConfig } from 'nuxt'import exampleModule from './modules/example'export default defineNuxtConfig({<template>
    2. <template>
    3.   <input type="text" @input="handleInput" />
    4. </template><ChildComponent @custom-event="handleCustomEvent" />
    5. </template>modules: [<template>
    6. <template>
    7.   <input type="text" @input="handleInput" />
    8. </template><ChildComponent @custom-event="handleCustomEvent" />
    9. </template><template>
    10. <template>
    11.   <input type="text" @input="handleInput" />
    12. </template><ChildComponent @custom-event="handleCustomEvent" />
    13. </template>exampleModule<template>
    14. <template>
    15.   <input type="text" @input="handleInput" />
    16. </template><ChildComponent @custom-event="handleCustomEvent" />
    17. </template>]})
    复制代码
    在这个示例中,我们在 nuxt.config.ts 文件中使用 modules 数组来注册我们的 exampleModule 模块。
4.4 CSS模块化与 scoped CSS
CSS 模块化


CSS 模块化是一种将 CSS 文件与 JavaScript 文件耦合在一起的技术,它可以帮助你在构建应用程序时更好地管理和组织你的样式表。在 Nuxt.js 3.4 中,你可以使用 [/code]
在这个示例中,我们在 MyComponent.vue 文件中使用 [/code]在这个示例中,我们在 MyComponent.vue 文件中使用 scoped 属性来定义我们的 scoped CSS。在 scoped CSS 中,我们可以使用普通的 CSS 类来定义样式,这些样式将只应用于当前组件。
</ol>第五章:组件生命周期与优化

Nuxt 3 中的生命周期钩子

Nuxt 3 是基于 Vue 3 的服务器端渲染(SSR)框架,它提供了一套完整的生命周期钩子,允许开发者在不同阶段对组件进行操作。在 Nuxt 3 中,生命周期钩子的使用与 Vue 3 相似,但由于其 SSR 的特性,有一些区别。以下是 Nuxt 3 中常用的生命周期钩子:
setup

setup 是 Vue 3 Composition API 的入口,它是一个选项,作为组件的入口点,在组件创建之前执行。在 Nuxt 3 中,你可以在 setup 函数中定义组件的响应式数据和逻辑。
  1. export default defineComponent({<template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>setup() {<template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template><template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template>const count = ref(0);<template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template><template>
  18. <template>
  19.   <input type="text" @input="handleInput" />
  20. </template><ChildComponent @custom-event="handleCustomEvent" />
  21. </template>// 逻辑代码...<template>
  22. <template>
  23.   <input type="text" @input="handleInput" />
  24. </template><ChildComponent @custom-event="handleCustomEvent" />
  25. </template><template>
  26. <template>
  27.   <input type="text" @input="handleInput" />
  28. </template><ChildComponent @custom-event="handleCustomEvent" />
  29. </template>return { count };<template>
  30. <template>
  31.   <input type="text" @input="handleInput" />
  32. </template><ChildComponent @custom-event="handleCustomEvent" />
  33. </template>}});
复制代码
服务器端渲染相关钩子


  • beforeRouteEnter: 在路由进入该组件的对应路由之前调用。
  • beforeRouteUpdate: 在当前路由改变,但是该组件被复用时调用。
  • beforeRouteLeave: 导航离开该组件的对应路由时调用。
客户端渲染相关钩子

以下是一些客户端特有的生命周期钩子:

  • onBeforeMount: 在组件挂载之前调用。
  • onMounted: 在组件挂载之后调用。
  • onBeforeUpdate: 在组件更新之前调用。
  • onUpdated: 在组件更新之后调用。
  • onBeforeUnmount: 在组件卸载之前调用。
  • onUnmounted: 在组件卸载之后调用。
created 和 beforeDestroy

在 Vue 2 中常用的 created 和 beforeDestroy 钩子,在 Vue 3 中仍然可以使用,但在 Nuxt 3 中,你可能会更倾向于使用 Composition API 中的生命周期函数。以下是它们在 Nuxt 3 中的对应:

  • created: 可以使用 onBeforeMount 或 onMounted 代替,因为 Nuxt 3 是基于 Vue 3 的,created 钩子在服务器端也会被调用,但它不保证在客户端执行。
  • beforeDestroy: 可以使用 onBeforeUnmount 代替。
以下是如何在 Nuxt 3 中使用 onBeforeUnmount 的示例:
  1. export default defineComponent({<template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>setup() {<template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template><template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template>onBeforeUnmount(() => {<template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template><template>
  18. <template>
  19.   <input type="text" @input="handleInput" />
  20. </template><ChildComponent @custom-event="handleCustomEvent" />
  21. </template><template>
  22. <template>
  23.   <input type="text" @input="handleInput" />
  24. </template><ChildComponent @custom-event="handleCustomEvent" />
  25. </template>console.log('组件即将被卸载');<template>
  26. <template>
  27.   <input type="text" @input="handleInput" />
  28. </template><ChildComponent @custom-event="handleCustomEvent" />
  29. </template><template>
  30. <template>
  31.   <input type="text" @input="handleInput" />
  32. </template><ChildComponent @custom-event="handleCustomEvent" />
  33. </template>});<template>
  34. <template>
  35.   <input type="text" @input="handleInput" />
  36. </template><ChildComponent @custom-event="handleCustomEvent" />
  37. </template><template>
  38. <template>
  39.   <input type="text" @input="handleInput" />
  40. </template><ChildComponent @custom-event="handleCustomEvent" />
  41. </template>// 其他逻辑...<template>
  42. <template>
  43.   <input type="text" @input="handleInput" />
  44. </template><ChildComponent @custom-event="handleCustomEvent" />
  45. </template>}});
复制代码
在 Nuxt 3 中,由于它支持 Vue 3 的 Composition API,建议使用新的生命周期函数,它们提供了更细粒度的控制,并允许你在不同的生命周期阶段更清晰地组织代码。
5.2 性能优化:懒加载、预渲染
Nuxt 3 提供了多种性能优化策略,包括懒加载(Lazy Loading)和预渲染(Prerendering)。以下是关于这两个方面的简要介绍:
1. 懒加载(Lazy Loading)

Nuxt 3 的懒加载功能允许你只加载用户需要的部分内容,而不是一次性加载整个页面。这主要通过使用 vue-lazyload 或 vue-meta 等库,以及 Nuxt 的官方 nuxt-lazyload 插件来实现。

  • vue-lazyload: 可以在单个组件或整个页面上设置图片、子组件等元素的懒加载。
  • vue-meta: 可以配置路由的  标签,控制路由的预加载和懒加载。
  • nuxt-lazyload: Nuxt 提供的官方插件,可以全局配置懒加载策略。
在 nuxt.config.js 中配置懒加载插件:
  1. export default {<template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>// ...<template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template>plugins: [<template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template><template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template>{ src: '~/plugins/nuxt-lazyload', ssr: false }, // ssr: false 表示在客户端执行懒加载<template>
  18. <template>
  19.   <input type="text" @input="handleInput" />
  20. </template><ChildComponent @custom-event="handleCustomEvent" />
  21. </template>],<template>
  22. <template>
  23.   <input type="text" @input="handleInput" />
  24. </template><ChildComponent @custom-event="handleCustomEvent" />
  25. </template>// ...}
复制代码
2. 预渲染(Prerendering)

Nuxt 3 支持两种预渲染方法:静态预渲染(Static Rendering)和服务器端渲染(Server-side Rendering,SSR)。

  • 静态预渲染(Static Rendering) : 使用 nuxt generate 命令生成静态 HTML 版本的页面,这些页面在服务器上预先加载和解析,提高首屏加载速度。这对于 SEO 有显著优势,但不支持实时更新。
  • 服务器端渲染(SSR) : 在用户访问页面时,Nuxt 会先在服务器上渲染整个页面,然后将渲染结果返回给客户端。这提供了更好的用户体验,尤其是对于动态内容,但服务器资源消耗较大。
为了优化 SSR,可以考虑以下策略:

  • 使用 nuxt optimize 命令进行性能分析和优化。
  • 避免在 SSR 中执行复杂的计算或网络请求。
  • 使用 nuxt.config.js 中的 generate 和 build 配置,控制预渲染的范围和时机。
5.3 代码复用与模块化策略
Nuxt 3 支持多种代码复用与模块化策略,以帮助开发人员提高代码的可重用性和可维护性。以下是一些常用的 Nuxt 3 模块化策略:
1. 共享组件(Shared Components)

在 Nuxt 3 中,可以将组件放在 components 目录下,这些组件可以在整个应用中共享使用。例如,创建一个 components/MyButton.vue 文件,其中包含一个自定义按钮组件:
  1. <template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template><template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template><template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template>{{ label }}<template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template>
复制代码
在其他组件中使用 MyButton 组件:
  1. <template>
  2. <template>
  3. <template>
  4.   <input type="text" @input="handleInput" />
  5. </template><ChildComponent @custom-event="handleCustomEvent" />
  6. </template>
  7. <template>
  8. <template>
  9.   <input type="text" @input="handleInput" />
  10. </template><ChildComponent @custom-event="handleCustomEvent" />
  11. </template><template>
  12. <template>
  13.   <input type="text" @input="handleInput" />
  14. </template><ChildComponent @custom-event="handleCustomEvent" />
  15. </template><MyComponent :title="'Custom Title'" />
  16. <template>
  17. <template>
  18.   <input type="text" @input="handleInput" />
  19. </template><ChildComponent @custom-event="handleCustomEvent" />
  20. </template>
  21. </template>
复制代码
2. 插件(Plugins)

Nuxt 3 支持使用插件来扩展应用的功能。可以在 plugins 目录下创建插件文件,例如 plugins/my-plugin.js:
  1. export default function ({ app }) {<template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>app.mixin({<template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template><template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template>methods: {<template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template><template>
  18. <template>
  19.   <input type="text" @input="handleInput" />
  20. </template><ChildComponent @custom-event="handleCustomEvent" />
  21. </template><template>
  22. <template>
  23.   <input type="text" @input="handleInput" />
  24. </template><ChildComponent @custom-event="handleCustomEvent" />
  25. </template>$myMethod() {<template>
  26. <template>
  27. <template>
  28.   <input type="text" @input="handleInput" />
  29. </template><ChildComponent @custom-event="handleCustomEvent" />
  30. </template>
  31. <template>
  32. <template>
  33.   <input type="text" @input="handleInput" />
  34. </template><ChildComponent @custom-event="handleCustomEvent" />
  35. </template><template>
  36. <template>
  37.   <input type="text" @input="handleInput" />
  38. </template><ChildComponent @custom-event="handleCustomEvent" />
  39. </template><MyComponent :title="'Custom Title'" />
  40. <template>
  41. <template>
  42.   <input type="text" @input="handleInput" />
  43. </template><ChildComponent @custom-event="handleCustomEvent" />
  44. </template>
  45. </template>// ...<template>
  46. <template>
  47.   <input type="text" @input="handleInput" />
  48. </template><ChildComponent @custom-event="handleCustomEvent" />
  49. </template><template>
  50. <template>
  51.   <input type="text" @input="handleInput" />
  52. </template><ChildComponent @custom-event="handleCustomEvent" />
  53. </template><template>
  54. <template>
  55.   <input type="text" @input="handleInput" />
  56. </template><ChildComponent @custom-event="handleCustomEvent" />
  57. </template>},<template>
  58. <template>
  59.   <input type="text" @input="handleInput" />
  60. </template><ChildComponent @custom-event="handleCustomEvent" />
  61. </template><template>
  62. <template>
  63.   <input type="text" @input="handleInput" />
  64. </template><ChildComponent @custom-event="handleCustomEvent" />
  65. </template>},<template>
  66. <template>
  67.   <input type="text" @input="handleInput" />
  68. </template><ChildComponent @custom-event="handleCustomEvent" />
  69. </template>});}
复制代码
在 nuxt.config.js 中配置插件:
  1. export default {<template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>// ...<template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template>plugins: [<template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template><template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template>'~/plugins/my-plugin',<template>
  18. <template>
  19.   <input type="text" @input="handleInput" />
  20. </template><ChildComponent @custom-event="handleCustomEvent" />
  21. </template>],<template>
  22. <template>
  23.   <input type="text" @input="handleInput" />
  24. </template><ChildComponent @custom-event="handleCustomEvent" />
  25. </template>// ...}
复制代码
3. 模块(Modules)

Nuxt 3 支持使用模块来扩展应用的功能。可以在 modules 目录下创建模块文件,例如 modules/my-module.js:
  1. export default function (moduleOptions) {<template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>this.nuxt.hook('components:dirs', (dirs) => {<template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template><template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template>dirs.push('~/components/my-module');<template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template>});<template>
  18. <template>
  19.   <input type="text" @input="handleInput" />
  20. </template><ChildComponent @custom-event="handleCustomEvent" />
  21. </template>this.nuxt.hook('build:before', () => {<template>
  22. <template>
  23.   <input type="text" @input="handleInput" />
  24. </template><ChildComponent @custom-event="handleCustomEvent" />
  25. </template><template>
  26. <template>
  27.   <input type="text" @input="handleInput" />
  28. </template><ChildComponent @custom-event="handleCustomEvent" />
  29. </template>// ...<template>
  30. <template>
  31.   <input type="text" @input="handleInput" />
  32. </template><ChildComponent @custom-event="handleCustomEvent" />
  33. </template>});}
复制代码
在 nuxt.config.js 中配置模块:
  1. export default {<template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>// ...<template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template>modules: [<template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template><template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template>'~/modules/my-module',<template>
  18. <template>
  19.   <input type="text" @input="handleInput" />
  20. </template><ChildComponent @custom-event="handleCustomEvent" />
  21. </template>],<template>
  22. <template>
  23.   <input type="text" @input="handleInput" />
  24. </template><ChildComponent @custom-event="handleCustomEvent" />
  25. </template>// ...}
复制代码
4. 布局(Layouts)

Nuxt 3 支持使用布局来实现页面的通用结构。可以在 layouts 目录下创建布局文件,例如 layouts/default.vue:
  1. <template>
  2. <template>
  3. <template>
  4.   <input type="text" @input="handleInput" />
  5. </template><ChildComponent @custom-event="handleCustomEvent" />
  6. </template>
  7. <template>
  8. <template>
  9.   <input type="text" @input="handleInput" />
  10. </template><ChildComponent @custom-event="handleCustomEvent" />
  11. </template><template>
  12. <template>
  13.   <input type="text" @input="handleInput" />
  14. </template><ChildComponent @custom-event="handleCustomEvent" />
  15. </template><MyComponent :title="'Custom Title'" />
  16. <template>
  17. <template>
  18.   <input type="text" @input="handleInput" />
  19. </template><ChildComponent @custom-event="handleCustomEvent" />
  20. </template>
  21. </template><template>
  22. <template>
  23. <template>
  24.   <input type="text" @input="handleInput" />
  25. </template><ChildComponent @custom-event="handleCustomEvent" />
  26. </template>
  27. <template>
  28. <template>
  29.   <input type="text" @input="handleInput" />
  30. </template><ChildComponent @custom-event="handleCustomEvent" />
  31. </template><template>
  32. <template>
  33.   <input type="text" @input="handleInput" />
  34. </template><ChildComponent @custom-event="handleCustomEvent" />
  35. </template><MyComponent :title="'Custom Title'" />
  36. <template>
  37. <template>
  38.   <input type="text" @input="handleInput" />
  39. </template><ChildComponent @custom-event="handleCustomEvent" />
  40. </template>
  41. </template><template>
  42. <template>
  43. <template>
  44.   <input type="text" @input="handleInput" />
  45. </template><ChildComponent @custom-event="handleCustomEvent" />
  46. </template>
  47. <template>
  48. <template>
  49.   <input type="text" @input="handleInput" />
  50. </template><ChildComponent @custom-event="handleCustomEvent" />
  51. </template><template>
  52. <template>
  53.   <input type="text" @input="handleInput" />
  54. </template><ChildComponent @custom-event="handleCustomEvent" />
  55. </template><MyComponent :title="'Custom Title'" />
  56. <template>
  57. <template>
  58.   <input type="text" @input="handleInput" />
  59. </template><ChildComponent @custom-event="handleCustomEvent" />
  60. </template>
  61. </template><template>
  62. <template>
  63.   <input type="text" @input="handleInput" />
  64. </template><ChildComponent @custom-event="handleCustomEvent" />
  65. </template>
复制代码
在页面组件中使用布局:
  1. <template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template><template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template><template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template>[size=6]My Page[/size]
  14. <template>
  15. <template>
  16.   <input type="text" @input="handleInput" />
  17. </template><ChildComponent @custom-event="handleCustomEvent" />
  18. </template>
复制代码
5. 存储(Store)

Nuxt 3 支持使用 Vuex 实现应用的状态管理。可以在 store 目录下创建模块文件,例如 store/index.js:
  1. export const state = () => ({<template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>count: 0,});export const mutations = {<template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template>increment(state) {<template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template><template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template>state.count++;<template>
  18. <template>
  19.   <input type="text" @input="handleInput" />
  20. </template><ChildComponent @custom-event="handleCustomEvent" />
  21. </template>},};export const actions = {<template>
  22. <template>
  23.   <input type="text" @input="handleInput" />
  24. </template><ChildComponent @custom-event="handleCustomEvent" />
  25. </template>async incrementAsync({ commit }) {<template>
  26. <template>
  27.   <input type="text" @input="handleInput" />
  28. </template><ChildComponent @custom-event="handleCustomEvent" />
  29. </template><template>
  30. <template>
  31.   <input type="text" @input="handleInput" />
  32. </template><ChildComponent @custom-event="handleCustomEvent" />
  33. </template>await new Promise((resolve) => setTimeout(resolve, 1000));<template>
  34. <template>
  35.   <input type="text" @input="handleInput" />
  36. </template><ChildComponent @custom-event="handleCustomEvent" />
  37. </template><template>
  38. <template>
  39.   <input type="text" @input="handleInput" />
  40. </template><ChildComponent @custom-event="handleCustomEvent" />
  41. </template>commit('increment');<template>
  42. <template>
  43.   <input type="text" @input="handleInput" />
  44. </template><ChildComponent @custom-event="handleCustomEvent" />
  45. </template>},};export const getters = {<template>
  46. <template>
  47.   <input type="text" @input="handleInput" />
  48. </template><ChildComponent @custom-event="handleCustomEvent" />
  49. </template>doubleCount(state) {<template>
  50. <template>
  51.   <input type="text" @input="handleInput" />
  52. </template><ChildComponent @custom-event="handleCustomEvent" />
  53. </template><template>
  54. <template>
  55.   <input type="text" @input="handleInput" />
  56. </template><ChildComponent @custom-event="handleCustomEvent" />
  57. </template>return state.count * 2;<template>
  58. <template>
  59.   <input type="text" @input="handleInput" />
  60. </template><ChildComponent @custom-event="handleCustomEvent" />
  61. </template>},};
复制代码
在页面组件中使用存储:
  1. <template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template><template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template><template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template>Count: {{ count }}
  14. <template>
  15. <template>
  16.   <input type="text" @input="handleInput" />
  17. </template><ChildComponent @custom-event="handleCustomEvent" />
  18. </template><template>
  19. <template>
  20.   <input type="text" @input="handleInput" />
  21. </template><ChildComponent @custom-event="handleCustomEvent" />
  22. </template>Double Count: {{ doubleCount }}
  23. <template>
  24. <template>
  25.   <input type="text" @input="handleInput" />
  26. </template><ChildComponent @custom-event="handleCustomEvent" />
  27. </template><template>
  28. <template>
  29.   <input type="text" @input="handleInput" />
  30. </template><ChildComponent @custom-event="handleCustomEvent" />
  31. </template>Increment<template>
  32. <template>
  33.   <input type="text" @input="handleInput" />
  34. </template><ChildComponent @custom-event="handleCustomEvent" />
  35. </template><template>
  36. <template>
  37.   <input type="text" @input="handleInput" />
  38. </template><ChildComponent @custom-event="handleCustomEvent" />
  39. </template>Increment Async<template>
  40. <template>
  41.   <input type="text" @input="handleInput" />
  42. </template><ChildComponent @custom-event="handleCustomEvent" />
  43. </template>
复制代码
第6章:组件测试与维护

6.1 使用 Vue Test Utils 进行单元测试

Vue Test Utils 是 Vue.js 官方提供的测试工具库,用于编写 Vue.js 组件的单元测试。在 Nuxt 3 中,可以使用 Vue Test Utils 来测试组件的行为和状态。
首先,需要安装 Vue Test Utils 和一些其他依赖库:
  1. npm install --save-dev @vue/test-utils vitest
复制代码
然后,在项目根目录下创建一个 tests 目录,并在其中创建一个 unit 目录,用于存放单元测试文件。例如,测试 components/MyButton.vue 组件:
  1. <template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template><template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template><template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template>{{ label }}<template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template>
复制代码
创建一个 tests/unit/MyButton.spec.js 文件,用于测试 MyButton 组件:
  1. import { mount } from '@vue/test-utils';import MyButton from '~/components/MyButton.vue';describe('MyButton', () => {<template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>it('renders correctly', () => {<template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template><template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template>const wrapper = mount(MyButton);<template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template><template>
  18. <template>
  19.   <input type="text" @input="handleInput" />
  20. </template><ChildComponent @custom-event="handleCustomEvent" />
  21. </template>expect(wrapper.element).toMatchSnapshot();<template>
  22. <template>
  23.   <input type="text" @input="handleInput" />
  24. </template><ChildComponent @custom-event="handleCustomEvent" />
  25. </template>});<template>
  26. <template>
  27.   <input type="text" @input="handleInput" />
  28. </template><ChildComponent @custom-event="handleCustomEvent" />
  29. </template>it('emits click event when clicked', async () => {<template>
  30. <template>
  31.   <input type="text" @input="handleInput" />
  32. </template><ChildComponent @custom-event="handleCustomEvent" />
  33. </template><template>
  34. <template>
  35.   <input type="text" @input="handleInput" />
  36. </template><ChildComponent @custom-event="handleCustomEvent" />
  37. </template>const wrapper = mount(MyButton);<template>
  38. <template>
  39.   <input type="text" @input="handleInput" />
  40. </template><ChildComponent @custom-event="handleCustomEvent" />
  41. </template><template>
  42. <template>
  43.   <input type="text" @input="handleInput" />
  44. </template><ChildComponent @custom-event="handleCustomEvent" />
  45. </template>await wrapper.trigger('click');<template>
  46. <template>
  47.   <input type="text" @input="handleInput" />
  48. </template><ChildComponent @custom-event="handleCustomEvent" />
  49. </template><template>
  50. <template>
  51.   <input type="text" @input="handleInput" />
  52. </template><ChildComponent @custom-event="handleCustomEvent" />
  53. </template>expect(wrapper.emitted('click')).toBeTruthy();<template>
  54. <template>
  55.   <input type="text" @input="handleInput" />
  56. </template><ChildComponent @custom-event="handleCustomEvent" />
  57. </template>});<template>
  58. <template>
  59.   <input type="text" @input="handleInput" />
  60. </template><ChildComponent @custom-event="handleCustomEvent" />
  61. </template>it('applies primary class when type is primary', () => {<template>
  62. <template>
  63.   <input type="text" @input="handleInput" />
  64. </template><ChildComponent @custom-event="handleCustomEvent" />
  65. </template><template>
  66. <template>
  67.   <input type="text" @input="handleInput" />
  68. </template><ChildComponent @custom-event="handleCustomEvent" />
  69. </template>const wrapper = mount(MyButton, {<template>
  70. <template>
  71.   <input type="text" @input="handleInput" />
  72. </template><ChildComponent @custom-event="handleCustomEvent" />
  73. </template><template>
  74. <template>
  75.   <input type="text" @input="handleInput" />
  76. </template><ChildComponent @custom-event="handleCustomEvent" />
  77. </template><template>
  78. <template>
  79.   <input type="text" @input="handleInput" />
  80. </template><ChildComponent @custom-event="handleCustomEvent" />
  81. </template>propsData: {<template>
  82. <template>
  83. <template>
  84.   <input type="text" @input="handleInput" />
  85. </template><ChildComponent @custom-event="handleCustomEvent" />
  86. </template>
  87. <template>
  88. <template>
  89.   <input type="text" @input="handleInput" />
  90. </template><ChildComponent @custom-event="handleCustomEvent" />
  91. </template><template>
  92. <template>
  93.   <input type="text" @input="handleInput" />
  94. </template><ChildComponent @custom-event="handleCustomEvent" />
  95. </template><MyComponent :title="'Custom Title'" />
  96. <template>
  97. <template>
  98.   <input type="text" @input="handleInput" />
  99. </template><ChildComponent @custom-event="handleCustomEvent" />
  100. </template>
  101. </template>type: 'primary',<template>
  102. <template>
  103.   <input type="text" @input="handleInput" />
  104. </template><ChildComponent @custom-event="handleCustomEvent" />
  105. </template><template>
  106. <template>
  107.   <input type="text" @input="handleInput" />
  108. </template><ChildComponent @custom-event="handleCustomEvent" />
  109. </template><template>
  110. <template>
  111.   <input type="text" @input="handleInput" />
  112. </template><ChildComponent @custom-event="handleCustomEvent" />
  113. </template>},<template>
  114. <template>
  115.   <input type="text" @input="handleInput" />
  116. </template><ChildComponent @custom-event="handleCustomEvent" />
  117. </template><template>
  118. <template>
  119.   <input type="text" @input="handleInput" />
  120. </template><ChildComponent @custom-event="handleCustomEvent" />
  121. </template>});<template>
  122. <template>
  123.   <input type="text" @input="handleInput" />
  124. </template><ChildComponent @custom-event="handleCustomEvent" />
  125. </template><template>
  126. <template>
  127.   <input type="text" @input="handleInput" />
  128. </template><ChildComponent @custom-event="handleCustomEvent" />
  129. </template>expect(wrapper.classes()).toContain('btn-primary');<template>
  130. <template>
  131.   <input type="text" @input="handleInput" />
  132. </template><ChildComponent @custom-event="handleCustomEvent" />
  133. </template>});<template>
  134. <template>
  135.   <input type="text" @input="handleInput" />
  136. </template><ChildComponent @custom-event="handleCustomEvent" />
  137. </template>it('applies secondary class when type is secondary', () => {<template>
  138. <template>
  139.   <input type="text" @input="handleInput" />
  140. </template><ChildComponent @custom-event="handleCustomEvent" />
  141. </template><template>
  142. <template>
  143.   <input type="text" @input="handleInput" />
  144. </template><ChildComponent @custom-event="handleCustomEvent" />
  145. </template>const wrapper = mount(MyButton, {<template>
  146. <template>
  147.   <input type="text" @input="handleInput" />
  148. </template><ChildComponent @custom-event="handleCustomEvent" />
  149. </template><template>
  150. <template>
  151.   <input type="text" @input="handleInput" />
  152. </template><ChildComponent @custom-event="handleCustomEvent" />
  153. </template><template>
  154. <template>
  155.   <input type="text" @input="handleInput" />
  156. </template><ChildComponent @custom-event="handleCustomEvent" />
  157. </template>propsData: {<template>
  158. <template>
  159. <template>
  160.   <input type="text" @input="handleInput" />
  161. </template><ChildComponent @custom-event="handleCustomEvent" />
  162. </template>
  163. <template>
  164. <template>
  165.   <input type="text" @input="handleInput" />
  166. </template><ChildComponent @custom-event="handleCustomEvent" />
  167. </template><template>
  168. <template>
  169.   <input type="text" @input="handleInput" />
  170. </template><ChildComponent @custom-event="handleCustomEvent" />
  171. </template><MyComponent :title="'Custom Title'" />
  172. <template>
  173. <template>
  174.   <input type="text" @input="handleInput" />
  175. </template><ChildComponent @custom-event="handleCustomEvent" />
  176. </template>
  177. </template>type: 'secondary',<template>
  178. <template>
  179.   <input type="text" @input="handleInput" />
  180. </template><ChildComponent @custom-event="handleCustomEvent" />
  181. </template><template>
  182. <template>
  183.   <input type="text" @input="handleInput" />
  184. </template><ChildComponent @custom-event="handleCustomEvent" />
  185. </template><template>
  186. <template>
  187.   <input type="text" @input="handleInput" />
  188. </template><ChildComponent @custom-event="handleCustomEvent" />
  189. </template>},<template>
  190. <template>
  191.   <input type="text" @input="handleInput" />
  192. </template><ChildComponent @custom-event="handleCustomEvent" />
  193. </template><template>
  194. <template>
  195.   <input type="text" @input="handleInput" />
  196. </template><ChildComponent @custom-event="handleCustomEvent" />
  197. </template>});<template>
  198. <template>
  199.   <input type="text" @input="handleInput" />
  200. </template><ChildComponent @custom-event="handleCustomEvent" />
  201. </template><template>
  202. <template>
  203.   <input type="text" @input="handleInput" />
  204. </template><ChildComponent @custom-event="handleCustomEvent" />
  205. </template>expect(wrapper.classes()).toContain('btn-secondary');<template>
  206. <template>
  207.   <input type="text" @input="handleInput" />
  208. </template><ChildComponent @custom-event="handleCustomEvent" />
  209. </template>});});
复制代码
最后,在 package.json 中配置测试命令:
  1. {<template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>"scripts": {<template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template><template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template>"test": "vitest"<template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template>}}
复制代码
运行测试命令:
  1. npm test
复制代码
6.2 使用 Storybook 进行组件开发与文档化

Storybook 是一个用于开发和文档化 UI 组件的工具。在 Nuxt 3 中,可以使用 Storybook 来开发和文档化组件。
首先,需要安装 Storybook 和一些其他依赖库:
  1. npx sb init --builder @storybook/builder-webpack5 --typescript
复制代码
然后,在项目根目录下创建一个 .storybook 目录,用于存放 Storybook 配置文件。例如,创建一个 .storybook/main.js 文件,用于配置 Storybook:
  1. module.exports = {<template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>stories: ['../components/**/*.stories.@(js|jsx|ts|tsx)'],<template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template>addons: [<template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template><template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template>'@storybook/addon-links',<template>
  18. <template>
  19.   <input type="text" @input="handleInput" />
  20. </template><ChildComponent @custom-event="handleCustomEvent" />
  21. </template><template>
  22. <template>
  23.   <input type="text" @input="handleInput" />
  24. </template><ChildComponent @custom-event="handleCustomEvent" />
  25. </template>'@storybook/addon-essentials',<template>
  26. <template>
  27.   <input type="text" @input="handleInput" />
  28. </template><ChildComponent @custom-event="handleCustomEvent" />
  29. </template><template>
  30. <template>
  31.   <input type="text" @input="handleInput" />
  32. </template><ChildComponent @custom-event="handleCustomEvent" />
  33. </template>'@storybook/addon-interactions',<template>
  34. <template>
  35.   <input type="text" @input="handleInput" />
  36. </template><ChildComponent @custom-event="handleCustomEvent" />
  37. </template>],<template>
  38. <template>
  39.   <input type="text" @input="handleInput" />
  40. </template><ChildComponent @custom-event="handleCustomEvent" />
  41. </template>framework: '@storybook/vue3',<template>
  42. <template>
  43.   <input type="text" @input="handleInput" />
  44. </template><ChildComponent @custom-event="handleCustomEvent" />
  45. </template>core: {<template>
  46. <template>
  47.   <input type="text" @input="handleInput" />
  48. </template><ChildComponent @custom-event="handleCustomEvent" />
  49. </template><template>
  50. <template>
  51.   <input type="text" @input="handleInput" />
  52. </template><ChildComponent @custom-event="handleCustomEvent" />
  53. </template>builder: '@storybook/builder-webpack5',<template>
  54. <template>
  55.   <input type="text" @input="handleInput" />
  56. </template><ChildComponent @custom-event="handleCustomEvent" />
  57. </template>},};
复制代码
在 components 目录下创建一个 MyButton.stories.ts 文件,用于定义 MyButton 组件的 Story:
  1. import MyButton from './MyButton.vue';export default {<template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>title: 'Components/MyButton',<template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template>component: MyButton,};const Template = (args) => ({<template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template>components: { MyButton },<template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template>setup() {<template>
  18. <template>
  19.   <input type="text" @input="handleInput" />
  20. </template><ChildComponent @custom-event="handleCustomEvent" />
  21. </template><template>
  22. <template>
  23.   <input type="text" @input="handleInput" />
  24. </template><ChildComponent @custom-event="handleCustomEvent" />
  25. </template>return { args };<template>
  26. <template>
  27.   <input type="text" @input="handleInput" />
  28. </template><ChildComponent @custom-event="handleCustomEvent" />
  29. </template>},<template>
  30. <template>
  31.   <input type="text" @input="handleInput" />
  32. </template><ChildComponent @custom-event="handleCustomEvent" />
  33. </template>template: `<template>
  34. <template>
  35.   <input type="text" @input="handleInput" />
  36. </template><ChildComponent @custom-event="handleCustomEvent" />
  37. </template><template>
  38. <template>
  39.   <input type="text" @input="handleInput" />
  40. </template><ChildComponent @custom-event="handleCustomEvent" />
  41. </template><template>
  42. <template>
  43.   <input type="text" @input="handleInput" />
  44. </template><ChildComponent @custom-event="handleCustomEvent" />
  45. </template>`,});export const Primary = Template.bind({});Primary.args = {<template>
  46. <template>
  47.   <input type="text" @input="handleInput" />
  48. </template><ChildComponent @custom-event="handleCustomEvent" />
  49. </template>label: 'Primary',<template>
  50. <template>
  51.   <input type="text" @input="handleInput" />
  52. </template><ChildComponent @custom-event="handleCustomEvent" />
  53. </template>type: 'primary',};export const Secondary = Template.bind({});Secondary.args = {<template>
  54. <template>
  55.   <input type="text" @input="handleInput" />
  56. </template><ChildComponent @custom-event="handleCustomEvent" />
  57. </template>label: 'Secondary',<template>
  58. <template>
  59.   <input type="text" @input="handleInput" />
  60. </template><ChildComponent @custom-event="handleCustomEvent" />
  61. </template>type: 'secondary',};
复制代码
最后,在 package.json 中配置 Storybook 命令:
  1. {<template>
  2. <template>
  3.   <input type="text" @input="handleInput" />
  4. </template><ChildComponent @custom-event="handleCustomEvent" />
  5. </template>"scripts": {<template>
  6. <template>
  7.   <input type="text" @input="handleInput" />
  8. </template><ChildComponent @custom-event="handleCustomEvent" />
  9. </template><template>
  10. <template>
  11.   <input type="text" @input="handleInput" />
  12. </template><ChildComponent @custom-event="handleCustomEvent" />
  13. </template>"storybook": "start-storybook -p 6006"<template>
  14. <template>
  15.   <input type="text" @input="handleInput" />
  16. </template><ChildComponent @custom-event="handleCustomEvent" />
  17. </template>}}
复制代码
运行 Storybook 命令:
  1. npm run storybook
复制代码
6.3 维护与更新最佳实践

在 Nuxt 3 中开发和维护组件时,需要遵循一些最佳实践,以保证组件的可重用性和可维护性。

  • 使用组件库:使用一些已有的组件库,如 Vuetify、Bootstrap-Vue 等,可以减少开发和维护的工作量。
  • 使用组件 props:使用组件 props 可以使组件更加灵活和可重用。
  • 使用组件 slot:使用组件 slot 可以使组件更加灵活和可扩展。
  • 使用组件事件:使用组件事件可以使组件更加交互和可响应。
  • 使用组件样式:使用组件样式可以使组件更加美观和一致。
  • 使用组件测试:使用组件测试可以使组件更加可靠和可维护。
  • 使用组件文档化:使用组件文档化可以使组件更加易于理解和使用。
  • 使用组件更新:使用组件更新可以使组件更加新颖和有用。
往期文章推荐:


  • Nuxt.js 深入浅出:目录结构与文件组织详解 | cmdragon's Blog
  • 友情链接 | cmdragon's Blog
  • 安装 Nuxt.js 的步骤和注意事项 | cmdragon's Blog
  • 探索Web Components | cmdragon's Blog
  • Vue微前端架构与Qiankun实践理论指南 | cmdragon's Blog
  • Vue 3深度探索:自定义渲染器与服务端渲染 | cmdragon's Blog
  • Tailwind CSS 响应式设计实战指南 | cmdragon's Blog
  • Tailwind CSS 实战指南:快速构建响应式网页设计 | cmdragon's Blog
  • Vue 3与ESLint、Prettier:构建规范化的前端开发环境 | cmdragon's Blog
  • Vue TypeScript 实战:掌握静态类型编程 | cmdragon's Blog
  • Vue CLI 4与项目构建实战指南 | cmdragon's Blog

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册