士沌 发表于 5 天前

Nuxt 3组件开发与管理

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章: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:
npm install -g npx
npx create-nuxt-app my-app或者使用Yarn:
yarn global add create-nuxt-app
create-nuxt-app my-app1.2.2 配置基本项目

[*]进入项目目录:cd my-app
[*]配置nuxt.config.ts:设置基础配置,如目标环境、SSR、SSG等。
// nuxt.config.ts
export default {
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>target: 'server', // 或者 'static', 根据需求选择
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>// 更多配置...
}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文件扩展名,而函数式组件使用2.2.2 函数式组件示例
2.3 Props和Slots的使用
2.3.1 Props(属性)

[*]Props是组件间的通信方式,父组件向子组件传递数据。
[*]props对象定义组件接受的属性,如上述按钮组件的buttonText和isActive。
2.3.2 Slots(插槽)

[*]Slots用于在组件中定义可替换的部分,父组件可以通过标签传递内容给子组件。
[*]子组件可以使用来定义默认插槽或命名插槽。
2.4 使用Composition API
2.4.1 Refs(响应式引用)

[*]Composition API引入了ref,用于创建响应式的变量,如上述isActive和buttonText。
2.4.2 Setup Function(设置函数)

[*]setup函数是组件的逻辑核心,替代了.vue文件中的export default部分,用于导入和导出函数、变量和方法。
第3章:组件开发实践

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

在Nuxt 3中,自定义组件通常包含、在父组件中,可以使用v-bind来传递title属性:
<template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><MyComponent :title="'Custom Title'" />
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
</template>3.2 异步加载组件
在 Nuxt 3 中,可以使用异步加载来延迟加载组件,从而提高应用程序的性能和用户体验。
3.2.1 动态导入

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

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


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

自定义事件

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

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

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

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

[*]通用组件:将所有可复用的组件放在components目录中。例如:
components/<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- Button.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- Input.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- Icon.vue
[*]页面组件:每个页面组件都可以作为一个单独的.vue文件存在,并放在pages目录中。例如:
pages/<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- index.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- about.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- products/<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- index.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- product-1.vue
[*]布局组件:布局组件可以放在layouts目录中。例如:
layouts/<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- default.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- admin.vue
[*]插件:如果你需要在组件中使用第三方库或自定义插件,可以将它们放在plugins目录中。例如:
plugins/<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- third-party.js<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- custom.js
[*]模块:如果你需要在项目中使用自定义模块,可以将它们放在modules目录中。例如:
modules/<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- custom-module.js
4.2 分级组件和模块化
在 Nuxt 3 中,组件化和模块化是两个核心概念,它们有助于构建可维护和可扩展的应用程序。以下是关于 Nuxt 3 中的分级组件和模块化的详细说明:
分级组件(Hierarchical Components)

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

[*]根组件:通常位于 pages 目录下的 .vue 文件,它们是页面的入口。
[*]子组件:可以在页面组件中或其它组件中嵌套使用的组件。子组件通常放在 components 目录下,并可以进一步细分为子目录,以反映它们的功能或用途。
以下是一个分级组件的例子:
components/<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- Header/<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- Logo.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- Navigation.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- Footer/<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- Contact.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- SocialLinks.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- Hero.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- ArticleList.vue在这个结构中,Header 和 Footer 目录包含了与它们相关的子组件,而 Hero 和 ArticleList 是独立的组件。
模块化(Modularization)

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

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

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

[*]使用分级组件来组织你的用户界面。
[*]使用模块化来组织你的应用逻辑。
通过这种方式,你可以确保每个组件都专注于展示逻辑,而模块则处理应用的业务逻辑,从而实现关注点分离。例如:
components/<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- Header.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- Header/<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- Logo.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- Navigation.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- Footer.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- Footer/<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- Contact.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- SocialLinks.vue<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- ArticleList.vuecomposables/<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- useHeader.js<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- useFooter.jsstore/<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- index.jsutils/<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</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 的文件:
layouts/<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>- default.vue
[*]在 default.vue 文件中定义你的布局:
<template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><MyComponent :title="'Custom Title'" />
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>我的应用程序

<template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><MyComponent :title="'Custom Title'" />
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
</template><template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><MyComponent :title="'Custom Title'" />
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
</template><template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><MyComponent :title="'Custom Title'" />
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>© 2023 我的应用程序
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
[*]在你的页面中使用 Layouts:
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>我的页面

<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>这是我的页面内容。
在这个示例中,我们在 layouts 目录中创建了一个名为 default.vue 的布局,并在其中定义了一个包含 header、main 和 footer 的结构。在页面中,我们可以使用  插槽来显示页面的内容。
Modules

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

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


CSS 模块化是一种将 CSS 文件与 JavaScript 文件耦合在一起的技术,它可以帮助你在构建应用程序时更好地管理和组织你的样式表。在 Nuxt.js 3.4 中,你可以使用 
在这个示例中,我们在 MyComponent.vue 文件中使用 在这个示例中,我们在 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 函数中定义组件的响应式数据和逻辑。
export default defineComponent({<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>setup() {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>const count = ref(0);<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>// 逻辑代码...<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>return { count };<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</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 的示例:
export default defineComponent({<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>setup() {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>onBeforeUnmount(() => {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>console.log('组件即将被卸载');<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>});<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>// 其他逻辑...<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</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 中配置懒加载插件:
export default {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>// ...<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>plugins: [<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>{ src: '~/plugins/nuxt-lazyload', ssr: false }, // ssr: false 表示在客户端执行懒加载<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>],<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</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 文件,其中包含一个自定义按钮组件:
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>{{ label }}<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>在其他组件中使用 MyButton 组件:
<template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><MyComponent :title="'Custom Title'" />
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
</template>2. 插件(Plugins)

Nuxt 3 支持使用插件来扩展应用的功能。可以在 plugins 目录下创建插件文件,例如 plugins/my-plugin.js:
export default function ({ app }) {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>app.mixin({<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>methods: {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>$myMethod() {<template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><MyComponent :title="'Custom Title'" />
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
</template>// ...<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>},<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>},<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>});}在 nuxt.config.js 中配置插件:
export default {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>// ...<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>plugins: [<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>'~/plugins/my-plugin',<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>],<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>// ...}3. 模块(Modules)

Nuxt 3 支持使用模块来扩展应用的功能。可以在 modules 目录下创建模块文件,例如 modules/my-module.js:
export default function (moduleOptions) {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>this.nuxt.hook('components:dirs', (dirs) => {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>dirs.push('~/components/my-module');<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>});<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>this.nuxt.hook('build:before', () => {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>// ...<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>});}在 nuxt.config.js 中配置模块:
export default {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>// ...<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>modules: [<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>'~/modules/my-module',<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>],<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>// ...}4. 布局(Layouts)

Nuxt 3 支持使用布局来实现页面的通用结构。可以在 layouts 目录下创建布局文件,例如 layouts/default.vue:
<template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><MyComponent :title="'Custom Title'" />
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
</template><template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><MyComponent :title="'Custom Title'" />
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
</template><template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><MyComponent :title="'Custom Title'" />
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>在页面组件中使用布局:
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>My Page

<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>5. 存储(Store)

Nuxt 3 支持使用 Vuex 实现应用的状态管理。可以在 store 目录下创建模块文件,例如 store/index.js:
export const state = () => ({<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>count: 0,});export const mutations = {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>increment(state) {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>state.count++;<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>},};export const actions = {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>async incrementAsync({ commit }) {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>await new Promise((resolve) => setTimeout(resolve, 1000));<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>commit('increment');<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>},};export const getters = {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>doubleCount(state) {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>return state.count * 2;<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>},};在页面组件中使用存储:
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>Count: {{ count }}
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>Double Count: {{ doubleCount }}
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>Increment<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>Increment Async<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>第6章:组件测试与维护

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

Vue Test Utils 是 Vue.js 官方提供的测试工具库,用于编写 Vue.js 组件的单元测试。在 Nuxt 3 中,可以使用 Vue Test Utils 来测试组件的行为和状态。
首先,需要安装 Vue Test Utils 和一些其他依赖库:
npm install --save-dev @vue/test-utils vitest然后,在项目根目录下创建一个 tests 目录,并在其中创建一个 unit 目录,用于存放单元测试文件。例如,测试 components/MyButton.vue 组件:
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>{{ label }}<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>创建一个 tests/unit/MyButton.spec.js 文件,用于测试 MyButton 组件:
import { mount } from '@vue/test-utils';import MyButton from '~/components/MyButton.vue';describe('MyButton', () => {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>it('renders correctly', () => {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>const wrapper = mount(MyButton);<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>expect(wrapper.element).toMatchSnapshot();<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>});<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>it('emits click event when clicked', async () => {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>const wrapper = mount(MyButton);<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>await wrapper.trigger('click');<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>expect(wrapper.emitted('click')).toBeTruthy();<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>});<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>it('applies primary class when type is primary', () => {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>const wrapper = mount(MyButton, {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>propsData: {<template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><MyComponent :title="'Custom Title'" />
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
</template>type: 'primary',<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>},<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>});<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>expect(wrapper.classes()).toContain('btn-primary');<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>});<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>it('applies secondary class when type is secondary', () => {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>const wrapper = mount(MyButton, {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>propsData: {<template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><MyComponent :title="'Custom Title'" />
<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>
</template>type: 'secondary',<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>},<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>});<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>expect(wrapper.classes()).toContain('btn-secondary');<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>});});最后,在 package.json 中配置测试命令:
{<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>"scripts": {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>"test": "vitest"<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>}}运行测试命令:
npm test6.2 使用 Storybook 进行组件开发与文档化

Storybook 是一个用于开发和文档化 UI 组件的工具。在 Nuxt 3 中,可以使用 Storybook 来开发和文档化组件。
首先,需要安装 Storybook 和一些其他依赖库:
npx sb init --builder @storybook/builder-webpack5 --typescript然后,在项目根目录下创建一个 .storybook 目录,用于存放 Storybook 配置文件。例如,创建一个 .storybook/main.js 文件,用于配置 Storybook:
module.exports = {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>stories: ['../components/**/*.stories.@(js|jsx|ts|tsx)'],<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>addons: [<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>'@storybook/addon-links',<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>'@storybook/addon-essentials',<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>'@storybook/addon-interactions',<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>],<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>framework: '@storybook/vue3',<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>core: {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>builder: '@storybook/builder-webpack5',<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>},};在 components 目录下创建一个 MyButton.stories.ts 文件,用于定义 MyButton 组件的 Story:
import MyButton from './MyButton.vue';export default {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>title: 'Components/MyButton',<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>component: MyButton,};const Template = (args) => ({<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>components: { MyButton },<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>setup() {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>return { args };<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>},<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>template: `<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>`,});export const Primary = Template.bind({});Primary.args = {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>label: 'Primary',<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>type: 'primary',};export const Secondary = Template.bind({});Secondary.args = {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>label: 'Secondary',<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>type: 'secondary',};最后,在 package.json 中配置 Storybook 命令:
{<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>"scripts": {<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template><template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>"storybook": "start-storybook -p 6006"<template>
<template>
<input type="text" @input="handleInput" />
</template><ChildComponent @custom-event="handleCustomEvent" />
</template>}}运行 Storybook 命令:
npm run storybook6.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

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: Nuxt 3组件开发与管理