Vue3-企业就绪的-Web-应用架构指南-全-
Vue3 企业就绪的 Web 应用架构指南(全)
原文:
zh.annas-archive.org/md5/f0085d7e0398c2ceadd0a36b5a9345e9
译者:飞龙
前言
构建企业级 Vue.js 应用意味着遵循最佳实践来创建高性能和可扩展的 Vue.js 应用。
对于任何在大型 Vue.js 代码库中工作且性能和可扩展性至关重要的开发者来说,本书是必读的。你将学习如何配置和设置 Vue.js 3 和组合式 API,以及如何使用它构建实际应用。然后,你将学习如何在 Vue.js 3 中创建可重用组件并扩展 Vue.js 3 应用中的性能。
你将学习如何通过异步懒加载、图像压缩、代码拆分和摇树优化来扩展性能。接下来,你将学习如何使用 RESTful API、Docker、GraphQL 以及不同类型的测试来确保你的 Vue.js 3 应用具有可扩展性和可维护性。本书结束时,你将能够使用最佳实践在 Vue.js 3 中实现 RESTful API、Docker、GraphQL 以及不同的测试方法来构建和部署你的企业级 Vue.js 3 应用。
本书面向对象
本书面向 Vue.js 开发者以及希望从设计到部署构建高性能、生产级和企业级可扩展 Vue.js 应用的资深前端开发者。本书假设读者已具备 Vue.js 和 JavaScript 编程的实际知识。
本书涵盖内容
[第一章],Vue.js 3 入门,涵盖了 Vue.js、选项 API、新的 Vue.js 3 以及组合式 API。因此,它建立了对 Vue.js 的理解。此外,本章将深入探讨 Vue.js 3 的组合式 API,并作为理解其他章节的指南。
第二章,使用库进行大型应用开发,涵盖了 Vuex、Axios 和 Vue Router 的基本方面以及如何将它们与 Vue 3 集成以开发企业应用。这些背景信息将使你更好地理解这些库的术语和概念,并帮助你理解如何构建和扩展企业级应用。
第三章, Vue.js 3 中的性能扩展,深入探讨了如何扩展大型 Vue 应用。你将学习如何通过异步懒加载、图像压缩、代码拆分、摇树优化以及许多其他技巧来提高你的 Vue.js 3 企业级应用的性能。
第四章,大型 Web 应用的架构,教你如何处理大型企业级项目,从管理更大的文件结构到使用微前端架构。你还将学习如何处理 Vue.js 3 项目的国际化本地化。
第五章,GraphQL、查询、突变和 RESTful API 简介,探讨了 GraphQL、Apollo Server 2、查询、突变以及如何将这些技术集成到你的 Vue.js 3 应用程序中。此外,你将学习如何利用 GraphQL 来提供可扩展和高性能的应用程序。
第六章,使用 GraphQL 构建完整的 Pinterest 克隆,讨论了如何使用 Vue 3 和 GraphQL 构建完整的 Pinterest 克隆。你将利用 GraphQL 的知识来开发和交付企业应用程序,如 Pinterest,使用 Vue 3 和 GraphQL。
第七章,将 Vue 3 应用 Docker 化,探讨了将你的 Vue 项目 Docker 化的方方面面。此外,你将学习 Docker 化和部署企业级 Vue.js 3 Web 应用程序的最佳实践和行业标准。本章还将通过 Docker 化一个全栈 Web 应用程序并将容器部署到云平台(使用 Docker Compose)来更加实用。你将学习如何使用这个工具处理更大的项目。
第八章,Vue.js 3 中的测试和要测试的内容,探讨了测试的整个概念。你将学习从可用的组件和方法数组中要测试的内容。此外,你还将学习测试库的最佳实践和行业标准,以及如何将它们与 Vue.js 3 集成。
第九章,单元测试的最佳实践,深入探讨了与单元测试相关的所有内容。你将学习如何对 Vue.js 3 组件及其方法和页面进行单元测试。你还将了解单元测试工具,如 Jest 和 Mocha,并使用它们来有效地进行单元测试企业项目。
第十章,Vue.js 3 的集成测试,涵盖了与集成测试相关的所有内容。你将深入了解如何在 Vue.js 3 组件和页面上执行集成测试。你还将了解集成测试工具,如 Vue-Test-Library,以及如何有效地使用它们来测试企业项目。
第十一章,行业标准的端到端测试,探讨了与端到端测试相关的所有内容。你将深入了解如何在 Vue.js 3 组件和页面上执行端到端测试。此外,你还将了解端到端测试工具,如 Cypress 和 Puppeteer,以及如何有效地使用它们来测试企业项目端到端。
第十二章,部署准备就绪的 Vue.js 3,展示了如何将 Vue.js 3 项目部署到 AWS 云。你将学习部署到 AWS 的最佳实践。此外,你还将了解大公司如何部署他们的企业级 Vue 应用程序。
第十三章,高级 Vue.js 框架,提供了 Nuxt.js 的权威指南。您将学习 Nuxt.js 的细节以及如何使用 Vue.js 3 构建和交付企业级 SSR 项目。我们还将提供 Gridsome 的权威指南。您将学习 Gridsome 的细节以及如何使用 Vue.js 3 构建和交付企业级 CSR 项目。
要充分利用本书
本书涵盖的软件/硬件 | 操作系统要求 |
---|---|
Node.js 16.0 或更高版本 | Windows、macOS 或 Linux |
熟悉命令行 | Windows、macOS 或 Linux |
熟悉命令行 | Windows、macOS 或 Linux |
Vue.js 3 | Windows、macOS 或 Linux |
Docker 和 AWS | Windows、macOS 或 Linux |
如果您正在使用本书的数字版,我们建议您亲自输入代码或从本书的 GitHub 仓库(下一节中有一个链接)获取代码。这样做将帮助您避免与代码的复制和粘贴相关的任何潜在错误。
要运行第 7 章 中展示的代码,你需要了解 Docker 和容器化技术。
要充分利用本书,您需要了解 AWS 和云计算,以便运行第 12 章 中展示的代码。
下载示例代码文件
您可以从 GitHub 下载本书的示例代码文件 github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications
。如果代码有更新,它将在 GitHub 仓库中更新。
我们还提供了来自我们丰富的书籍和视频目录中的其他代码包,可在 github.com/PacktPublishing/
找到。查看它们吧!
下载彩色图像
我们还提供了一个包含本书中使用的截图和图表的彩色图像 PDF 文件。您可以从这里下载:packt.link/4Lgta
。
使用的约定
本书使用了多种文本约定。
文本中的代码
:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和推特用户名。以下是一个示例:“如果你正在跟随,请在 .github/workflows
文件夹内创建一个名为 staging.yml
的新文件。”
代码块设置如下:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: |
yarn
yarn lint
任何命令行输入或输出都如下所示:
npm install --save graphql graphql-tag @apollo/client @vue/apollo-composable
粗体:表示新术语、重要单词或屏幕上看到的单词。例如,菜单或对话框中的单词以粗体显示。以下是一个示例:“在其他选项中点击 下一步:权限 选项,最后点击 创建用户 按钮。”
小贴士或重要提示
看起来像这样。
联系我们
欢迎读者反馈。
一般反馈:如果您对本书的任何方面有疑问,请通过 customercare@packtpub.com 给我们发邮件,并在邮件主题中提及书名。
勘误: 尽管我们已经尽最大努力确保内容的准确性,但错误仍然可能发生。如果您在这本书中发现了错误,我们非常感谢您能向我们报告。请访问www.packtpub.com/support/errata并填写表格。
盗版: 如果您在互联网上以任何形式发现我们作品的非法副本,我们非常感谢您能提供位置地址或网站名称。请通过 copyright@packt.com 与我们联系,并提供材料的链接。
如果您有兴趣成为作者:如果您在某个领域有专业知识,并且您有兴趣撰写或为书籍做出贡献,请访问authors.packtpub.com。
分享您的想法
一旦您阅读了《构建 Vue.js 3 企业级 Web 应用程序》,我们非常乐意听听您的想法!请点击此处直接进入该书的亚马逊评论页面并分享您的反馈。
您的评论对我们和科技社区非常重要,并将帮助我们确保我们提供高质量的内容。
下载本书的免费 PDF 副本
感谢您购买这本书!
您喜欢随时随地阅读,但无法携带您的印刷书籍吗?您的电子书购买是否与您选择的设备不兼容?
别担心,现在,每购买一本 Packt 书籍,您都可以免费获得该书的 DRM 免费 PDF 版本。
在任何地方、任何设备上阅读。直接从您最喜欢的技术书籍中搜索、复制和粘贴代码到您的应用程序中。
优惠不会就此停止,您还可以获得独家折扣、新闻通讯以及每天收件箱中的精彩免费内容。
按照以下简单步骤获取福利:
- 扫描下面的二维码或访问以下链接
packt.link/free-ebook/9781801073905
-
提交您的购买证明
-
就这样!我们将直接将您的免费 PDF 和其他福利发送到您的邮箱
第一部分:Vue.js 入门
这第一部分为您提供了本书其余部分的理论和历史背景。它涵盖了 Vue.js、Options API、新的 Vue.js 3 和 Composition API。您还将学习如何使用 Vue CLI 创建新的 Vue 应用程序,然后,我们将更深入地探讨如何使用 Vuex、Vue Router 和 Axios 构建企业级应用程序。
本部分包括以下章节:
-
第一章, Vue.js 3 入门
-
第二章, 大规模应用中的库使用 应用
第一章:Vue.js 3 入门
在我们开始学习如何使用 Vue.js 3 开发企业级应用之前,你需要了解 Vue 3 以及它附带的不同特性,这些特性将帮助你导航构建可扩展的企业级应用。
在本章中,我们将介绍 Vue 3 的基本方面,这将直接影响我们如何使用 Vue.js 3 开发企业级应用。这些背景信息将使你更好地理解 Vue 3 的术语和概念,并帮助你了解如何构建和扩展企业级应用。
在本章中,我们将涵盖以下关键主题:
-
Vue.js 概述
-
介绍 Vue.js 3
-
构建你的第一个 Vue.js 3 应用
一旦你掌握了这些主题,你就可以开始构建你的第一个企业级 Vue.js 3 应用了。
技术要求
要开始,我们建议你具备基本的 JavaScript 知识,并在你的电脑上安装 Node.js,并且必须使用 Vue.js 构建过项目。
Vue.js 概述
Vue.js 是一个开源的渐进式 JavaScript 前端 Web 框架,用于开发交互式前端 Web 界面。它是一个非常流行且简化的 JavaScript 框架,专注于 Web 开发的视图层。它可以轻松集成到大型和企业级 Web 开发项目中。
Vue.js 是一个框架,它为开发者打开了轻松创建和管理大型和可扩展项目的门户,因为代码结构和开发环境对开发者友好。
在下一节中,我们将向你介绍 Vue 3 和组合 API 的奇妙之处。
介绍 Vue.js 3
官方的 Vue.js 3 版本于 2020 年 9 月发布,提供了高度文档化、易于阅读、结构良好的资源,帮助你开始使用 Vue 3。Evan You 在他的文章《过程:制作 Vue 3》(increment.com/frontend/making-vue-3/
) 中提到,重写的一个关键原因是利用新的语言特性,代理。
代理允许框架拦截对对象的操作。Vue 的一个核心特性是能够监听用户定义状态的变化,并反应性地更新 DOM。在 Vue 3 中,使用代理特性是解决 Vue 2 中反应性相关问题的关键。
最重要的是,Vue 3 完全使用 TypeScript 重新编写,并拥有使用 TypeScript 所带来的现代框架的所有优势。
在本节中,我们将探讨一些与构建企业应用相关的特性和改进,最重要的是,新的组合 API。
我们将涵盖以下主题:
-
Vue 3 性能
-
树摇支持
-
组合 API
这些主题让你对 Vue.js 3 的特性有了初步的了解,我们将在本书中从我们已熟悉的 Vue 开始。
Vue 3 性能
Vue 3 的性能提升对企业级应用来说非常出色,因为核心框架中的任何延迟都可能导致资金损失,考虑到企业级项目的巨大规模。
与前版本相比,Vue 3 将性能提高了 55%,更新速度也快了 133%,这对于在部署前开发和测试大型企业级项目来说是非常出色的。此外,内存使用量减少了 54%,大幅降低了企业级项目的计算成本。
摇树优化支持
摇树优化是消除死代码、无用代码或未使用代码的过程,如果与拥有数千个文件且有时不知情地存在未使用文件的企业级应用相比,这会大幅减少应用的构建大小。
Vue 3 原生支持摇树优化,可以消除未使用的文件和代码,从而减小构建大小并提高项目的性能。
组合式 API
组合式 API 是一个全新的添加,也是 Vue 3 最重大的变化。它需要重新学习概念,并完全摒弃 Vue 2 中使用的 Options API。虽然组合式 API 在进步,但之前的 Options API 将继续得到支持。在这本书中,我们使用组合式 API,因为它带来了可读性和性能的提升。
为什么需要组合式 API?
当构建一个简单的应用时,仅凭组件式架构已被证明是开发此类应用的最佳方法,其中各个组件可以被重用以提高可维护性和灵活性。
然而,当构建具有数百个组件的企业级应用时,根据集体经验,仅凭组件式架构可能不足以满足需求,尤其是在应用规模扩大,即使在组件内部共享和重用代码也变得非常重要的情况下,因此引入了组合式 API。
代码示例
让我们假设我们正在构建一个具有独特功能(如过滤和搜索能力)的企业级待办事项应用。我们将使用 Options API,并采用传统的data
、computed
和watch
方法来处理这个项目。
下面的代码块展示了如何使用 Vue 2 的 Options API 创建和管理 Vue 组件:
// src/components/TodoRepositories.vue
export default {
components: { RepositoriesFilters, RepositoriesSortBy,
RepositoriesList },
props: {
todo: {
type: String,
required: true,
},
},
data() {
return {
repositories: [], // 1
filters: {}, // 3
searchQuery: '', // 2
}
},
computed: {
filteredRepositories() {}, // 3
repositoriesMatchingSearchQuery() {}, // 2
},
watch: {
todo: 'getTodoRepositories', // 1
},
mounted() {
this.getTodoRepositories() // 1
},
methods: {
getTodoRepositories() {
// using `this.Todo` to fetch Todo repositories
}, // 1
updateFilters() {}, // 3
},
}
以下组件处理了许多职责,如下所述:
-
从外部 API 获取
Todo
仓库并在用户更改时刷新它 -
使用
searchQuery
字符串搜索Todo
仓库 -
使用
filters
对象过滤Todo
仓库
将组件的逻辑组织方式如前例所示运作得非常完美,但同时也对更大规模和企业的项目,尤其是组件逻辑更复杂的项目的可读性和可维护性提出了巨大的挑战。
如果我们能够将相关逻辑关注点的代码集中在一起,那岂不是完美?这正是组合式 API 能够让我们做到的。
让我们使用组合式 API 重新编写相同的组件,以查看使用它所获得的改进和可读性优势:
<script setup>
import { fetchTodoRepositories } from '@/api/repositories'
import { ref, watch, computed } from 'vue'
const props = defineProps({
todo: {
type: String
default:""
}
})
const repositories = ref([])
const getTodoRepositories = async () => {
repositories.value =
await fetchTodoRepositories(props.todo)
}
getTodoRepositories()
// set a watcher on the Reactive Reference to user todo
// prop
watchEffect(getTodoRepositories)
const searchQuery = ref('')
const repositoriesMatchingSearchQuery = computed(() => {
return repositories.value.filter(
repository =>
repository.name.includes(searchQuery.value)
)
})
</script>
组合式 API 是一个很好的补充,特别是对于开发企业级应用。我们可以将 computed
、mounted
和 watch
生命周期钩子移动到一个独立的组合函数中,并通过 setup
在脚本中导入它,使其易于阅读、灵活且易于维护。要了解更多关于组合式 API 的信息,请访问官方文档(v3.vuejs.org/guide/composition-api-introduction.html#why-composition-api
),它超出了本书的范围。
到目前为止,我们已经概述了 Vue 3 以及 Vue 新引入的功能,这些功能对于构建企业级和可扩展的生产级应用非常有用。我们还介绍了组合式 API 的基础知识,以帮助您理解如何使用 Vue 3 构建现代企业级应用。
在下一节中,我们将通过学习如何使用 Vite 作为构建工具来构建您的第一个 Vue 3 应用程序来测试您的知识。
根据官方文档(vitejs.dev/guide/
),Vite 是一个旨在为现代网络项目提供更快、更精简的开发体验的构建工具。它基于 Rollup,并配置为支持大多数合理的默认设置,适用于现代 JavaScript 框架。
构建 Vue.js 3 的第一个应用
根据需求,Vue.js 可以以多种方式集成到项目中,因为它具有增量适应性。
我们将创建一个完全空白的新 Vue 3 项目,或者您可以使用迁移指南(v3.vuejs.org/guide/migration/migration-build.html#overview
)将您的 Vue 2 项目迁移到 Vue 3 以便跟随。
在本节中,我们将介绍如何使用 Vite 的 命令行界面(CLI)构建我们的 Vue 3 应用程序。
使用 Vite 创建 Vue 3 应用
要创建我们的第一个 Vue 3 应用程序,我们将使用推荐的 Vite 网络开发工具。Vite 是一个网络开发构建工具,由于其本地的 ES 模块导入方法,它允许快速提供代码服务。
在这本书中,我们将构建一个企业级 Pinterest 克隆项目,并且该项目的所有后端数据管理都将由 Strapi 开发和托管。
按照这些简单的命令输入:
npm init @vitejs/app pinterest-app-clone
cd pinterest-app-clone
npm install
npm run dev
// If you're having issues with spaces in username, try using:
npx create-vite-app pinterest-app-clone
上述命令将创建一个包含 Vue 3 并正确设置的 pinterest-app-clone
文件夹。完成后,打开您喜欢的浏览器并访问 localhost:3000
的网页。这就是网页的外观:
图 1.1 – 新安装的 Vue 3 的截图
在本节中,我们探讨了 Vue 3、Composition API 以及如何开始使用 Vue 3 构建你的第一个应用。在下一节中,我们将了解我们将用于数据和管理内容的 Strapi CMS。
什么是 Strapi CMS?
Strapi 是一个基于 Node.js 的开源无头 CMS,用于通过 RESTful API 和 GraphQL 开发和管理内容或数据。
使用 Strapi,我们可以更快地搭建我们的 API,并通过任何 HTTP 客户端或 GraphQL 兼容的前端来消费内容。
搭建 Strapi 项目
搭建新的 Strapi 项目非常简单,其工作方式与安装新的前端框架完全相同。按照以下步骤搭建新的 Strapi 项目:
-
运行以下任一命令并在你的默认浏览器中测试它们:
npx create-strapi-app strapi-api --quickstart # OR yarn create strapi-app strapi-api --quickstart
上述命令将在你指定的目录中搭建一个新的 Strapi 项目。
- 接下来,运行
yarn build
来构建你的应用,最后,如果项目没有自动启动,运行yarn develop
。
yarn develop
命令将打开一个新标签页,显示一个页面以注册系统的新管理员:
图 1.2 – 注册页面
- 请继续填写表格并点击提交按钮以创建新的管理员。
随着我们在本书中的进展,我们将定制我们的 Strapi 后端实例以反映 Pinterest 数据建模。
摘要
本章从 Vue.js 的概述开始,讨论了为什么 Vue.js 可以用来开发企业级应用。我们讨论了 Vue.js 的最新版本以及它是如何通过引入开箱即用的 tree-shaking 功能来提高框架的性能方面的。然后,我们介绍了 Composition API,这是 Vue 3 的一个特性,它提高了 Vue 3 在构建和部署企业应用时的可读性、可维护性和可扩展性。我们还探讨了如何使用 Vite 创建我们的第一个 Vue 3 应用,以及使用 Vite 而不是其他可用选项的基本原因。
最后,我们介绍了 Strapi CMS,它是用于构建和建模后端应用和 API 的后端堆栈和无头 CMS。使用 Strapi,我们将专注于使用 Vue 3 构建 和扩展我们的企业级 Pinterest-clone 应用,而 Strapi CMS 将处理后端。
在下一章中,我们将更深入地探讨如何使用 Vuex、Vue Router 和 Axios 来构建一个企业级应用。你将学习如何正确地利用这些库来开发具有可维护性和可扩展性的大型应用,到本章结束时,你将学会如何使用 Strapi 设置你的后端并将其连接到 Vue 3。
第二章:使用库构建大型应用程序
在你开始学习如何使用不同的库使用 Vue.js 3 开发大型和面向企业的应用程序之前,你需要了解这些单个库以及它们捆绑的各种功能,以帮助你导航构建可扩展和面向企业的应用程序。
在本章中,我们将涵盖 Vuex、Axios 和 Vue Router 的基本方面,以及如何将它们与 Vue 3 集成以开发企业级应用程序。这些背景信息将使你更好地理解这些库的术语和概念,并帮助你了解如何构建和扩展企业级应用程序。
我们在本章中将涵盖以下关键主题:
-
探索大型 Vuex
-
使用存储模式进行结构化
-
创建存储文件夹
-
使用 Vue Router 结构化 Vue 导航
一旦你掌握了这些主题中的每一个,你将准备好开始使用 Vue 3 构建你的第一个企业级应用程序。
技术要求
要开始本章的学习,我们建议阅读 第一章,“Vue.js 3 入门”,以及其中对 Vue 3 和组合 API 的概述,这些将在本章中广泛使用。
探索大型 Vuex
Vuex 是 Vue 应用程序的状态管理库。它作为 Vue 应用程序中所有组件的中心存储。它也是一个专门针对 Vue.js 定制的库实现,以利用其细粒度响应性系统进行高效更新。
使用 Vuex 对 Vue 应用程序进行状态管理可以带来显著的好处。然而,如果不进行适当的结构化,它很容易被误用和压倒,尤其是在构建大型企业级应用程序时——由于项目的规模以及将在项目中引入的组件和功能数量。
为了解决这个结构问题,我们将向您介绍不同的结构来安排您的 Vuex 存储,以及在第 第四章 中讨论的可预测性法则,“大型 Web 应用程序的架构”,以适应大型 Vue 应用程序。
在本节中,我们将讨论 Vuex 的状态、获取器、突变和动作。
实际上,结构化 Vuex 存储的常用方法是让所有代码都位于一个名为 单状态树 的单个 index.js
文件中。这种方法对于小型项目来说非常完美,有助于避免在不同文件中导航以找到单个方法。
然而,当使用 Vuex 开发企业项目时,使用单状态树会变得非常臃肿且难以维护。
为了减少这个大文件并将其拆分为不同的功能,Vuex 允许我们将我们的存储拆分为 Vuex 模块。
在我们深入之前,Vue 社区引入了一个新的状态管理系统,称为 Pinia,它解决了 Vuex 的问题,并且与 Vue 3 直接兼容。截至写作时,Pinia 仍处于开发和测试阶段。你可以在pinia.vuejs.org/
了解更多关于 Pinia 以及如何将其集成到你的 Vue 3 应用程序中的信息。
Vuex 模块
Vuex 模块是根据功能拆分我们的存储的一种方式,其中每个模块可以包含其状态、获取器、动作、突变,甚至嵌套模块。
这种方法允许我们将存储拆分为功能,创建不同的文件和文件夹来正确地安排存储。
我们将在下一小节中学习如何将我们的存储拆分为功能。
假设我们的 Pinterest 应用程序将具有不同的状态,如照片、用户、评论等,我们可以将存储拆分为单独的模块,而不是将其放在一个文件中。
使用 Vuex 模块
如前所述,使用 Vuex 模块带来了巨大的好处,我们将在这本书中坚持使用它。首先,让我们看看我们的 Vuex 模块存储的文件夹结构:
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = createStore({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> `moduleA`'s state
store.state.b // -> `moduleB`'s state
如前代码块所示,我们创建了不同的模块来分别包装我们的 Vuex 状态、动作和突变。这有助于将我们的项目结构化为不同的功能。
现在我们已经了解了如何为企业项目构建 Vuex 存储,让我们在下一节中讨论如何在 Vue 组件中访问和管理存储。
Vuex 状态
首先,也是最重要的,让我们讨论状态以及我们如何管理模块 Vuex 存储的状态。
Vuex 状态是你在 Vuex 存储内部存储的数据,可以在你的 Vue 应用程序的任何地方访问。
Vuex 状态遵循单状态树模式。这个单一的对象包含所有应用程序级别的状态。它作为“单一事实来源”。但是,由于我们正在采用模块化来管理我们的企业级应用程序,我们只将学习如何访问和管理我们的模块状态。
以下代码片段展示了如何创建一个简单的 Vuex 状态:
// initial state
const state = () => ({
photos: [],
})
此外,你还可以在组件外部访问 Vuex 存储。例如,你可以在 Vue 服务、辅助文件中等地方访问 Vuex。然而,在下一节中,我们将探讨在组件中访问状态的不同方法。
不映射访问状态
假设这是我们 Pinterest 应用程序中所有照片的存储,并且我们有那个 photos
状态,我们如何在组件中访问它?
要访问 Photos
数组,我们将使用模块名称与存储名称,如下面的代码片段所示:
const Photos = {
template: `<div v-for="(photo, index) in photos"
:key="index"> <img :src="img/photo.url"></div>`,
computed: {
photos () {
return this.$store.photos.state.photos
}
}
}
以下代码片段展示了如何通过创建一个新的 Photos
组件来访问模块存储,并显示照片状态中的所有照片。
要访问 Photos
状态数组,我们使用了它所属的模块名称,并在 photos
数组之前访问了状态属性。
接下来,我们将探讨如何使用映射方法访问 Vuex 状态。
使用映射访问状态
访问存储的最佳方式是使用 Vuex 状态映射(vuex.vuejs.org/guide/state.html#the-mapstate-helper
),我们将在这本书的整个过程中使用它。你可以访问官方 Vuex(vuex.vuejs.org/
)文档了解更多信息。
当你的组件需要使用多个存储状态属性或获取器时,使用 Vuex 映射器是非常好的。声明所有这些状态和获取器可能会变得重复和冗长,这正是 Vuex 映射器试图解决的问题。
让我们以创建一个简单的 Photos
组件并使用 Vuex 状态来显示不同图像为例:
import { mapState } from 'vuex'
const Photos = {
template: `<div v-for="(photo, index) in photos"
:key="index"> <img :src="img/photo.url"></div>`,
computed: mapState({
photos: state => state.photos.photos,
}
})
}
以下代码片段创建了一个 Photos
组件,遍历 Vuex 状态中的数据,并在存储中显示图像。
就这样。
我们将在与动作、突变和获取器的进一步讨论中使用此方法。你绝对不应该忘记你模块的名称。
我们现在对 Vuex 状态和模块有了相当的了解,以及我们将如何构建我们的企业级和大规模 Vuex 应用程序,以便易于维护和访问。
让我们讨论获取器,以及我们如何在下一节中使用 Vuex 获取器和映射获取器来操作我们的 Vuex 状态。
Vuex 获取器
Vuex 获取器(getters)在操作 Vuex 状态方面非常有用。有时,你可能想在将数据返回给组件之前过滤或排序 Vuex 状态。
Vuex 允许我们创建获取器(getters),就像 Vue 中的计算属性一样操作状态。它还会缓存结果,并在数据更改时更新缓存。
在每个模块中,我们将定义其特定的获取器来操作该模块的状态。例如,我们将在 Photos
模块中创建一个获取器,根据用户 ID 过滤照片,如下所示:
getters: {
getPhotoByID: (state) => (id) => {
return state.photos.find(photo => photo.id === id)
}
}
以下代码片段展示了如何创建一个获取器,该获取器可以过滤出通过 getters
方法传递的特定用户 ID 添加的所有照片。
接下来,让我们使用 map
辅助函数在我们的组件中访问获取器。请看以下代码片段:
...mapGetters({
// map `this.getPhotoByID` to
// `this.$store.getters.getPhotoByID`
getPhotoByID: 'getPhotoByID'
})
Vuex 获取器是操作和管理 Vuex 状态的绝佳方式,在它们发送到我们的组件之前非常有用,并且对于过滤、排序、更新和从 Vuex 状态中删除记录都很有帮助。
接下来,我们将讨论 Vuex 突变以及如何在企业级应用程序中使用它们。
Vuex 突变(mutations)
更改 Vuex 状态的唯一方法是提交一个突变(mutation)。
Vuex 突变类似于事件。它包含一个名为 type
的字符串和一个名为 handler
的函数。handler
函数是执行突变的地方。
假设我们仍在使用我们的 Pinterest 照片存储,我们可以使用以下代码片段向我们的状态中添加一个新的 Photo
对象:
const store = createStore({
state: {
photos: []
},
mutations: {
ADD_NEW_PHOTO (state, photo) {
// mutate state
state.photos.push(photo)
}
}
})
接下来,我们将查看访问 Vuex 突变模块。我们可以使用 Vuex 映射辅助工具来访问它,这是企业项目的推荐方式:
import { mapMutations } from 'vuex'
export default {
methods: {
...mapMutations({
addPhoto: 'photos/ADD_NEW_PHOTO'
})
}
}
最后,我们可以在组件的任何地方调用 addPhoto()
方法,将 Photo
对象作为唯一参数传递,让 Vuex 做它的事情。
此外,使用 Vuex 突变的最佳位置是在 Vuex 动作 中。在下一节中,我们将详细讨论 Vuex 动作并展示它们如何在企业应用程序中使用。
Vuex 动作
Vuex 动作类似于 Vuex 突变,但它们是异步的,主要用于提交 Vuex 突变。
Vuex 动作可以向我们的后端服务器发出 API 调用,并使用 Vuex 突变将响应提交到 Vuex 状态。
传统上,要使用 Vuex 动作进行 API 调用,我们将在存储中直接进行,如下代码片段所示。
使用我们的 Pinterest 照片示例,我们将有一个类似这样的存储:
const store = createStore({
state: {
photos: []
},
mutations: {
ADD_NEW_PHOTO (state, photo) {
state.photos.push(photo)
}
},
actions: {
async getPhoto (context, id) {
const photo = await Axios.get('/photos/'+id);
context.commit('ADD_NEW_PHOTO', photo)
}
}
})
接下来,为了在我们的组件中分发动作,我们将继续使用 Vuex 映射来分发动作并检索与传递到 getPhoto()
方法的 ID 对应的新照片:
import { mapActions } from 'vuex'
export default {
methods: {
...mapActions({
getPhoto: 'photos/getPhoto' // map `this.getPhoto()`
// to `this.$store.dispatch('photos/getPhoto')`
})
}
}
到目前为止,我们已经详细介绍了使用 Vuex 构建大规模应用程序的内容,并阐明了 Vuex 模块、状态、获取器、突变和动作以及如何在构建企业级应用程序中应用它们。
为了进一步解决结构问题,我们将向您介绍在安排 Vuex 存储时使用 仓库模式 的方法,将所有 API 调用结构化到一个仓库中,并在 Vuex 动作中访问它们。
使用仓库模式进行结构化
当构建一个大规模、企业级 Vue 应用程序时,你必须从项目结构开始就做对。
根据应用程序的功能将 Vuex 存储分离成单独的模块是很好的,它提供了直接访问文件,使得调试和维护变得容易。
单独使用这种方法会带来问题。您的 Vuex 动作会变得非常大,包含许多 API 调用,提取 API 数据和处理错误都在 Vuex 动作中发生。
介绍仓库模式有助于消除庞大的代码库,并将 API 调用和管理从 Vuex 中分离出来。
在本节中,我们将首先概述仓库模式。然后,我们将为我们的 Vue 应用程序创建一个仓库文件夹。
首先,在我们探讨如何在 Vuex 中使用仓库模式之前,让我们先对仓库模式有一个清晰的概述以及它能实现什么。
仓库模式概述
仓库模式是在创建企业级应用程序时使用的一个重要模式,无论是任何企业应用程序的前端还是后端。
它限制了我们直接在应用程序中处理数据,并为数据库操作、业务逻辑和应用程序 UI 创建了一个新的层级。
以下是一些你应该在你的前端开发中使用仓库模式的原因列表,尤其是在构建企业应用程序时:
-
数据访问代码在整个项目中任何地方都是可重用的
-
实现领域逻辑非常容易
-
你可以快速对业务逻辑进行单元测试,而无需任何形式的紧密耦合。
-
它有助于解耦业务逻辑和应用程序 UI
依赖注入(DI)在编写可测试的企业代码时很好,而仓库模式可以帮助你在前端项目中实现 DI。
DI
DI 是一种编程技术,它使一个类与其依赖项独立。
在仓库模式中,你通过隐藏如何在 Vuex 存储中检索和处理数据来编写封装的代码库。
要实现仓库模式,我们将遵循我写的关于“在 Vue.js 中使用仓库模式消费 API”的文章(medium.com/backenders-club/consuming-apis-using-the-repository-pattern-in-vue-js-e64671b27b09
)。
要使用仓库模式在 Vue.js 中消费后端 API,让我们通过一个示例来展示如何做到这一点。假设我们有一个 Vuex 存储操作执行不同的 API 调用,如下面的代码片段所示,我们想在它上面实现仓库模式:
actions: {
async all({ commit }, { size = 20, page = 0 }) {
const response = await
Axios.get(`/photos?size=${size}&page=${page}`);
const { data } = response;
if (data.success) {
commit("STORE_PHOTOS", data.photos);
} else {
commit("STORE_ERROR", data.message);
}
},
async getPhoto({ commit }, id) {
const response = await Axios.get('/photos/'+id);
const { data } = response;
if (data.success) {
commit("STORE_PHOTO", data.photo);
} else {
commit("STORE_ERROR", data.message);
}
},
},
现在,我们可以按照接下来的步骤通过实现仓库模式来改进 Vuex 存储。
创建仓库文件夹
首先,通过运行以下命令在根目录或src
文件夹中创建一个文件夹:
mkdir repositories
我们将称之为repositories
。这个文件夹将包含所有你的仓库和 HTTP 客户端配置。
创建客户文件夹
我们将在新创建的repositories
文件夹内创建一个Clients
文件夹。这个文件夹里将包含所有使用的 HTTP 客户端。
有时,由于项目的性质,一些项目可能需要多个 HTTP 客户端来执行 API 调用,原因各不相同。其中一个可以作为默认连接失败时的后备。
因此,创建一个Clients
文件夹对于一次性配置所有 HTTP 客户端至关重要。运行以下命令来创建一个:
cd repositories && mkdir Clients
创建 xxxClient.js 类
你可以创建一个与所使用的 HTTP 客户端对应的类文件。命名是主观的,对于AxiosClient.js
文件,将其放入所有默认配置中。
Axios
Axios 是一个基于 Promise 的 HTTP 客户端,适用于 Node.js 和浏览器。它可以在浏览器和 Node.js 上使用相同的代码库运行。
运行以下命令来创建文件夹:
touch AxiosClient.js
总结来说,你可能需要使用许多 HTTP 客户端,因此为每个客户端创建不同的xxxClient.js
文件,并为其指定特定的配置。
对于 Axios,以下是我为这次测试设置的默认配置:
import axios from "axios";
const baseDomain = "https://localhost:1337"; //For Strapi
const baseURL = `${baseDomain}`; // Incase of /api/v1;
// ALL DEFAULT CONFIGURATION HERE
export default axios.create({
baseURL,
headers: {
// "Authorization": "Bearer xxxxx"
}
});
你可以在前面的文件中为 Axios 添加更多默认配置,并导出 Axios 实例。
创建一个单独的仓库类
接下来,我们将根据我们在企业应用程序中的功能数量创建一个单独的仓库。
例如,我们正在构建一个Pinterest克隆应用程序,我们确信该应用程序将具有照片和用户功能。因此,我们可以通过运行以下命令开始为提到的功能创建仓库:
cd repositories && touch PhotoRepository.js UserRepository.js
这些仓库将包含所有针对单个功能的 API 调用。我们将首先为相应的仓库创建一个创建、读取、更新和删除(CRUD)操作,以获得概述。相比之下,我们将随着本书的进展更新仓库。
打开PhotoRepository.js
文件并添加以下脚本:
import Axios from './Clients/AxiosClient';
const resource = '/photos;
export default {
get() {
return Axios.get(`${resource}`);
},
getPhoto(id) {
return Axios.get(`${resource}/${id}`);
},
create(payload) {
return Axios.post(`${resource}`, payload);
},
update(payload, id) {
return Axios.put(`${resource}/${id}`, payload);
},
delete(id) {
return Axios.delete(`${resource}/${id}`)
},
//b MANY OTHER RELATED ENDPOINTS.
};
接下来,我们将打开UserRespository.js
文件并添加以下脚本:
import Axios from './Clients/AxiosClient';
const resource = '/users;
export default {
get() {
return Axios.get(`${resource}`);
},
getUser(id) {
return Axios.get(`${resource}/${id}`);
},
create(payload) {
return Axios.post(`${resource}`, payload);
},
update(payload, id) {
return Axios.put(`${resource}/${id}`, payload);
},
delete(id) {
return Axios.delete(`${resource}/${id}`)
},
//b MANY OTHER RELATED ENDPOINTS.
};
我们为我们的Pinterest克隆应用程序创建了两个仓库,任何与 API 相关的代码都将放入单独的仓库中。
创建一个RepositoryFactory.js
类
在repositories
文件夹内通过运行以下命令创建一个RepositoryFactory
工厂类,以导出您可能创建的所有不同单独的仓库,以便在应用程序的任何地方轻松使用:
touch RepositoryFactory.js
完成后,粘贴以下代码:
import PhotoRepository from './PhotoRepository';
import UserRepository from './UserRepository';
const repositories = {
'Photos': PhotoRepository,
'Users': UserRepository
}
export default {
get: name => repositories[name]
};
现在我们通过创建仓库改进了我们的 Vuex 存储,让我们看看如何在下一节中使用这些仓库。
使用仓库模式
让我们看看如何在我们的 Vuex 存储中利用我们创建的仓库。打开您之前创建的 Vuex 存储photos
文件,并将getPhoto
动作方法替换为以下代码以利用仓库模式:
import Repository from "@/repositories/RepositoryFactory";
const Photos = Repository.get("Photos");
const Users = Repository.get("Users");
actions: {
async getPhoto (context, id) {
const photo = await Photos.getPhoto(id);
context.commit('ADD_NEW_PHOTO', photo)
}
async getUsers(context) {
const users = await Users.get();
context.commit('ADD_USERS', users)
}
}
使用仓库模式消除了处理错误、在 Vuex 存储中操作从 API 检索的数据以及只返回 Vuex 中实际需要的数据的需求。这种方法还利用了软件工程中的不要重复自己(DRY)原则,因为可以通过创建一个新的来在整个项目中使用仓库。
结构化并不随着您使用仓库模式整理好 HTTP API 调用而结束。它还扩展到您安排导航的方式。导航文件不应该包含大量难以理解的代码库。
在下一节中,我们将使用 Vue Router 来安排和结构化我们的导航,以确保在我们面向企业的项目中保持可维护性和可扩展性。
使用 Vue Router 构建 Vue 导航
当构建一个面向企业的应用程序时,很明显导航系统将会很大,因为会有很多导航、路由和页面。
本节将向您展示如何在企业项目中正确地构建 Vue Router。为了实现这一点,我们将使用按功能分割的方法来组织 Vue Router,使其易于导航,就像我们在本章早期使用 Vuex 时所做的那样。
这种方法将创建一个结构,其中公共和私有路由将被分离,并且还可以单独分离更多路由。
文件夹结构
该文件夹将包含一个 index 文件、一个公共文件和一个私有文件,其中包含每个类别的所有路由。
在您的 src
文件夹根目录下创建一个 router
文件夹,并在文件夹内通过在终端中依次输入以下命令创建以下文件:
cd src && mkdir router
touch index.js
mkdir routes && cd routes
touch public.js private.js combine.js
当前文件夹结构相当简单,随着本书的进展,我们将对其进行更多定制。以下是每个文件将包含的内容:
-
index.js
:此文件将包含beforeEach
逻辑和所有其他路由的组装 -
public.js
:此文件将包含所有不需要限制的面向公共的路由,例如登录页面、注册页面等 -
private.js
:此文件将包含所有用于认证用户和许多限制以及元数据的私有路由 -
combine.js
:此文件将合并私有和公共文件,并使其易于与主路由文件连接
接下来,让我们创建一个 index 文件来包含我们新创建项目的设置。
index.js 文件
index 文件是核心。打开 index.js
文件,添加以下代码以导出在公共和私有文件中创建的所有路由:
import { createRouter, createWebHistory } from "vue-router";
import routes from '@/router/routes/combine.js'
const routes = [
{
path: '/',
redirect: '/'
}
].concat(routes)
const router = createRouter({ history: createWebHistory(), routes });
// ....
// BeforeEach code here
//.....
export default router
在 Vue Router 中,当使用 Vue Router 开发 Vue 应用时,主要使用两种不同的历史模式:
-
Hash 模式
-
HTML5 模式
Hash 模式
这种模式在 URL 前使用 #
(哈希)符号来模拟完整 URL,这样当 URL 发生变化时页面不会重新加载。这是因为哈希符号后的页面或部分永远不会发送到服务器。这意味着它不会影响页面的 SEO,但这是 Vue Router 的默认设置。
HTML5 模式
如前例所示,这是使用 createWebHistory()
函数创建的,并且是适用于企业和生产级应用的推荐方法。它需要在服务器上进行一些繁琐的配置才能正常工作。
combine.js 文件
这是一个单一的实用文件,将所有路由合并到一个文件中,以便导出到主路由文件。打开文件并添加以下代码:
import publicRoutes from '@/router/routes/public.js'
import privateRoutes from '@/router/routes/private.js'
export default publicRoutes.concat(privateRoutes)
在将路由添加到工具文件后,我们将如下一节所示将它们导入到 main.js
文件中。
将路由添加到 Vue
最后,我们将把我们的路由添加到 Vue 实例中,如下一节所示。打开 main.js
文件,添加以下代码:
import { createApp } from "vue"
import App from "./App.vue"
import router from "./router/index.js"
import store from "./store"
createApp(App).use(router).use(store).mount("#app")
随着我们在本书中的进展,我们将重新访问 public.js
和 private.js
文件,根据我们开发的 Pinterest 克隆应用程序添加更多路由。
摘要
本章首先探讨了开发具有 Vue.js 3 的可扩展性和企业级应用程序的不同库。我们详细讨论了各个库及其不同的功能,以加深我们对构建可扩展和企业级应用程序的理解。我们还讨论了 Vuex 的基本要素,讨论了如何通过按功能拆分 Vuex 动作、模块、获取器和状态来构建我们的大规模 Vuex 存储库。
接下来,我们通过使用仓库模式将大型 Vuex 动作拆分为单独的仓库,并使我们的企业应用程序易于维护,讨论了关注点分离(SoC)的基本要素。仓库模式在创建企业应用程序中至关重要,我们展示了如何在 Vue 3 中实现它。
最后,我们讨论了如何构建 Vue Router,以避免在项目变得更大时出现臃肿和庞大的路由文件,因为那时将难以维护。我们讨论了将 Vue Router 文件拆分为不同文件的战略模式,以实现可维护性和易于调试。
在下一章中,我们将更深入地探讨企业 Vue 3 应用程序的性能扩展。我们将探索不同的性能和可扩展性技巧,以构建一个企业级 Vue 3 应用程序,例如异步组件的加载/懒加载、摇树优化、图像压缩等。你将学习如何通过在下一章中应用技巧来适当提高你的 Vue 3 应用程序的性能,以开发具有可维护性和可扩展性的大规模应用程序。
第二部分:大规模应用程序和 Vue.js 3 中的性能扩展
在本部分,你将学习构建大规模应用程序的最佳实践,其中可扩展性和性能是首要考虑的因素。此外,你还将学习和探索不同的技术,以扩展大型应用程序的性能。
本部分包括以下章节:
-
第三章,Vue.js 3 中的性能扩展
-
第四章,大规模 Web 应用程序的架构 应用程序
第三章:在 Vue.js 3 中扩展性能
本章完全依赖于前几章的知识,我们在那里探讨了使用 Vue.js 3 开发大规模和企业级应用的不同库。本章将进一步深入探讨扩展大型 Vue 应用。你将学习如何通过异步懒加载、图像压缩、代码拆分、摇树优化以及许多其他技巧来提高你的 Vue.js 3 企业级应用性能。
在本章中,我们将涵盖以下关键主题:
-
我们为什么需要 Vue.js 性能优化?
-
导致 Vue 性能不佳的主要原因
-
检查你的 Vue.js 应用包大小
-
优化企业级 Vue 应用的性能
一旦你掌握了这些主题中的每一个,你将准备好开始使用 Vue 3 构建你的第一个企业级应用。
技术要求
要开始本章的学习,我建议你阅读第一章,Vue.js 3 入门,在那里你将了解 Vue 3 和本章中广泛使用的组合 API 的概述。
我们为什么需要 Vue.js 性能优化?
在本节中,我们将学习为什么应用中的性能稳定性很重要,以及如何以性能为导向开发应用。
在开发应用时,如果不采取行动确保应用的稳定性能,可能会给应用带来很大损失。开发一个加载、导航、提交或执行任何用户操作需要较长时间的应用,会导致用户流失,从而逐渐偏离应用的初始计划。
假设最终用户对企业的用户体验和加载时间、Vue.js 性能和效率不满意。在这种情况下,投入的时间和编写的代码都不重要;用户可能不会返回到该应用。
以下是一些来自 Kinsta 的不同事实,展示了糟糕的性能如何影响市场上企业级应用的表现:kinsta.com/blog/laravel-caching/
。
一项在线研究(kinsta.com/learn/page-speed/#slow-how-slow
)发现,对于每 1 秒的加载延迟,亚马逊每年损失 16 亿美元的销售额。
另一项谷歌研究(www.thinkwithgoogle.com/future-of-marketing/digital-transformation/the-google-gospel-of-speed-urs-hoelzle/
)报告称,即使搜索结果慢了一秒钟,人们也会减少搜索。这意味着 400 毫秒的延迟会导致搜索量下降 0.44%。
进一步的研究表明,如果有五分之四的互联网用户在视频加载时遇到卡顿,他们将会离开。
前面的研究表明,你的网页加载时间稍有延迟可能会对你的用户体验产生巨大影响,并可能导致大量资金损失。
现在我们知道了为什么我们需要在应用中保持性能稳定性,在下一节中,让我们看看 Vue 性能不佳的主要原因。
Vue 性能不佳的主要原因
Vue 性能不佳的原因有很多,我们将在本节中探讨主要和最显著的原因。
Vue 应用变慢的明显原因在于结构。作为一个企业应用,很明显,应用越大,应用运行就越慢。
例如,企业 Vue 应用性能不佳的一个重要原因可能因不同项目和它们处理服务器端渲染(SSR)的方式而异。
任何 Vue 单页应用(SPA)或 SSR 企业应用性能不佳的主要原因是包大小。包越大,Vue 的性能就越慢。
企业 Vue 应用性能不佳还有其他一些常见原因,例如以下内容:
-
没有正确地结构化 CSS 和 JS 文件
-
没有明智地使用第三方库
-
不必要的 API 请求
-
忽视代码拆分和懒加载
性能不佳的原因还有很多,我们只列举了其中一些。在我们讨论如何解决这些问题之前,让我们在下一节中探讨如何检查企业 Vue 应用的包大小。
检查你的 Vue 应用包大小
包大小是浏览器将要加载的 Vue 应用的总大小。大小越大,应用加载速度就越慢。
在使用 Vue 框架时,检查 Vue 包大小有两种不同的方法。
让我们更详细地逐一介绍这些方法。
生成报告
你可以使用带有--report
标志的build
命令来生成你的应用报告。这种方法提供了所有使用包和每个包大小的视觉表示。此外,通过从该视觉报告中生成的信息,你可以找出如何替换任何占用比预期更多空间和大小的包。
此外,请注意,只有当安装了webpack-bundle-analyzer
时,build
命令才会生成报告。
要为你的应用生成报告,请按照以下步骤操作:
-
首先,使用以下命令安装包:
npm install webpack-bundle-analyzer
-
接下来,在你的
package.json
文件中创建一个脚本:"build-report": "vue-cli-service build --report"
-
最后,执行以下命令以生成报告:
npm run build-report
执行前面的命令后,在dist
文件夹内会创建一个名为report.html
的文件。当你打开这个文件时,你会看到以下内容:
图 3.1 – 应用包大小报告
运行 npm 构建命令
运行你的 Vue 应用程序的build
方法将生成不同块和包大小的列表。从这些信息中,你可以看到有关哪个块具有更大的包大小以及如何改进它的额外警告。以下是它的样子:
图 3.2 – 块和包的大小
在本节中,我们学习了为什么我们需要 Vue.js 性能优化,性能不佳的主要原因以及检查 Vue.js 包大小的不同方法。
在下一节中,我们将学习如何使用不同的标准方法优化 Vue 应用程序的性能。
优化企业 Vue 应用程序的性能
创建企业应用程序的一个缺点是应用程序的大小,包括代码库、数据量以及响应用户操作的速度。
一种可能的解决方案是在企业应用程序的后端和前端实施适当的缓存机制。
你会同意开发一个应用程序是有挑战性的。然而,创建一个性能优化或甚至解决企业应用程序性能瓶颈的应用程序更具挑战性。
在本节中,我们将探讨一些你可以实施的技巧来提高你的企业 Vue 应用程序的性能。
异步/延迟组件加载
我们将首先从异步/延迟组件加载开始,以减轻你探索 Vue.js 性能优化的挑战。
在 Vue.js 中,异步/延迟组件加载是一个术语,用于描述在用户需要模块/组件时加载模块/组件。在企业应用程序中,当用户访问网站时,没有必要从 JavaScript 包中加载所有模块,因为这样做将导致性能瓶颈。
在企业项目中,你会同意存在许多模态框、工具提示和其他相互关联的复杂组件,如果不延迟加载,这些组件将减慢应用程序的性能。
在我们探索如何延迟加载组件之前,你可以通过以下简单步骤检查你网页上使用的实际 JavaScript 代码:
- 点击DevTools。以下屏幕将出现:
图 3.3 – Chrome 中实时 DevTools 的示例
-
按Cmd + Shift + P。
-
输入
Coverage
。一旦输入,屏幕底部将出现以下信息:
图 3.4 – 显示覆盖率标签的屏幕截图
- 点击记录。
在记录和分析网页后,它将以红色突出显示一些 URL,这表明这些 URL 未使用,可以延迟加载:
图 3.5 – 使用 DevTools 分析时显示的不同 URL 的截图
如果懒加载得到适当的实现,您的企业应用程序的包大小可以减少到 60%,从而提高应用程序的速度。
最后,让我们探讨如何强制执行懒加载。我们可以使用 Webpack 动态导入而不是常规导入来分离懒加载模块的块。
传统上,这是 JavaScript 中导入组件/模块的方式:
// photo.js
const Photo = {
testPhoto: function () {
console.log("This is just a Photo Component!")
}
}
export default Photo
// app.js
import Photo from './photo.js'
Photo.testPhoto()
以这种方式导入此模块,Webpack 将在其依赖图中创建一个名为photo.js
的文件作为app.js
文件的节点,并将其捆绑在一起,即使用户不需要使用该模块。
但为了稍微改进一下,我们可以使用动态导入或懒加载来克服之前方法中的性能瓶颈。以下代码块展示了动态/懒加载的实际应用:
// app.js
const getPhoto = () => import('./photo.js')
// later when some user action tasks place as hitting the
// route
getPhoto()
.then({ testPhoto } => testPhoto())
在 Vue.js 中实现懒加载是减少包大小和优化性能的最佳实践之一。Vue 建议包的大小不应超过 244 KiB,但您也可以尝试优化您的网页,以确保即使包的大小略高于推荐值,性能也不会非常慢。养成一个习惯,知道哪些模块您不需要,除非有明确用户操作,然后懒加载它们以获得更好的性能。
WebP 图像和图像压缩
大型包大小和缓慢的应用程序的一个主要原因是图像对应用程序包大小贡献很大。如果图像没有正确优化,当应用程序渲染较大尺寸的图像时,可能会增加应用程序的加载时间。
实现图像优化的方法有很多,我们将讨论两种流行的方法:
-
压缩图片
-
优化内容分发网络(CDN)图像
压缩图片
如果您的企业应用程序包含少量的小图像,这些图像将本地提供服务,同时应用不同的压缩算法以减小每个图像的大小。
有成千上万的在线工具可以压缩图像,以下是一些流行的工具列表:
-
TinyPNG
-
Compressnow
-
Image Compressor
-
Adobe Photoshop
此外,用于减少图像大小的最佳压缩算法是 Google 开发的 WebP 图像格式(developers.google.com/speed/webp
),它由 Google 开发和维护。
优化 CDN 图像
CDN 用于优化图像。它提供通过减少图像大小高达 70%的转换功能,而不会影响 UI 和像素化。当您的企业应用程序处理大量媒体使用时,也建议使用 CDN。
在图像优化方面最受欢迎的 CDN 工具是Cloudinary和ImageKit。
媒体在任何企业应用中都占用相当大的空间,因此如果不进行优化和适当提供,可能会导致延迟和缓慢的性能。
Code splitting
MDN 解释如下(developer.mozilla.org/en-US/docs/Glossary/Code_splitting
):
“代码拆分是将代码拆分为各种包或组件的过程,这些包或组件可以按需或并行加载。”
随着应用程序复杂性的增加或维护,CSS 和 JavaScript 文件或包的大小也会增加,特别是随着包含的第三方库的数量和大小增加。”
在创建企业应用时,总会存在许多路由、文件和包,这些都会增加企业应用的字节大小。代码拆分是分离和仅加载较小和按需文件的答案,从而提高您企业应用的加载时间。
让我们的企业应用有两个页面,我们使用流行的vue-router
库来实现,如下所示:
// routing.js
import Dashboard from './Dashboard.vue'
import Photo from './Photo.vue'
const routes = [
{ path: '/', component: Dashboard }
{ path: '/photo', component: Photo }
]
由于 Vue.js 的编码标准,当用户访问任何页面时,我们脚本中的所有组件都将被下载。由于页面的数量、每个页面的复杂性和大型的包大小,这种活动会导致性能缓慢。
为了避免这个问题,我们可以实现适当的路由代码拆分,将我们的大型包拆分成不同的路由包,这意味着当用户访问该页面时,每个页面都将有一个小的包进行下载。
使用动态导入技术,而不是像之前演示的那样直接导入组件,我们可以传递动态路由并按需加载组件,如下面的代码块所示:
// routing.js
const routes = [
{ path: '/', component: () => import('./Dashboard.vue') }
{ path: '/photo', component: () => import('./Photo.vue') }
]
通过遵循此方法,您可以减半您的包大小。同时,确定哪些组件可以使用动态导入也非常重要。
摘要
在本章中,我们深入探讨了扩展 Vue 应用的缩放。我们讨论了如何通过异步懒加载、图像压缩、代码拆分、摇树优化以及许多其他技巧来提高 Vue.js 3 企业级应用的性能。
我们还详细讨论了为什么需要性能优化,以及如果性能没有故意构建到应用程序中,您的企业应用可能会失去什么。我们还讨论了企业应用性能不佳的原因以及如何修复它们。
接下来,我们讨论了如何检查我们的 Vue.js 应用程序的包大小,通过简单的说明如何使用 Webpack 和命令生成包报告来演示这一点。我们还讨论了如何理解报告,并从生成的报告中发现如何改进应用程序,以进一步提升我们的企业应用程序的性能。
在下一章中,我们将学习如何处理一个大型企业级项目,从管理更大的文件结构到使用微前端架构。你还将了解如何处理你的 Vue.js 3 项目的国际化与本地化。
第四章:大型 Web 应用的架构
在上一章中,我们探讨了在 Vue 3 中构建和扩展大型应用。我们讨论了为什么我们需要 Vue.js 性能优化,导致 Vue 性能不佳的主要原因,如何检查你的 Vue.js 应用包的大小,以及使用异步/懒加载组件、WebP 图像、图像压缩和代码拆分等方法优化企业级 Vue 应用性能。
在本章中,我们将学习如何处理一个大型企业级项目,从管理更大的文件结构到使用微前端架构。我们还将学习如何处理我们 Vue.js 3 项目的国际化与本地化。
本章我们将涵盖以下关键主题:
-
文件架构和结构
-
微前端架构
-
国际化和本地化
到本章结束时,你将知道如何使用 Vue 3 构建大型 Web 应用,如何使用可预测性原则实现结构和文件架构,以及如何使用社区推荐的包来告知你的 Vue.js 3 企业级应用中的可预测性。
你还将学习如何利用微前端架构的优势,以及如何使用 Storybook 实现原子设计,以简化你的组件目录,使企业级项目更容易理解。
最后,你将学习如何将国际化正确地集成到你的 Vue 应用中,以及这将带来的好处。
技术要求
要开始本章的学习,我建议你阅读第三章,Vue.js 3 中的性能扩展,其中我们阐述了在 Vue 3 中构建和扩展大型应用。
本章的所有代码文件都可以在github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-4
找到。
理解文件架构和结构
项目结构完全取决于你组织的偏好以及修复错误和添加新功能时访问文件和文件夹的难易程度。
在本节中,我们将探讨不同的原则,这些原则将给你一个想法,了解你如何构建项目以融入最佳实践、标准和易于访问的文件。
什么是最有效的项目结构方式,以实现扩展并保持其可维护性和可扩展性?
这是在软件开发行业中常见的疑问,但没有一种适合所有情况的解决方案。这完全取决于可预测性的原则,正如本文所讨论的:vueschool.io/articles/vuejs-tutorials/how-to-structure-a-large-scale-vue-js-application/
。
可预测性的原则简单来说就是能够在任何代码库中从 A 点到 B 点的直觉性移动,从功能请求或错误报告到代码库中可以解决该任务的地点。此外,它还指的是根据社区或流行库和工具的标准使用,快速或轻松地理解特定的代码库的能力。
为了详细说明,当一个代码库使用标准、社区同意和流行的库或工具时,它为开发者提供了极佳的开发体验,因为开发者已经熟悉这些工具。
在下一节中,我们将更深入地讨论可预测性以及如何在 Vue 3 中实现它。
Vue 3 中的可预测性
如何在 Vue 3 中实现可预测性非常简单,如前所述;它归结为使用 Vue 3 标准和风格指南。
例如,只需想象购买一个不同尺寸的新 iPhone 13 ProMax;这将很尴尬,因为您肯定已经根据您的评论预测了尺寸将保持不变。
这种方法适用于开发者对新代码库的心态;我们期望大多数库、组件名称、文件和文件夹结构都遵循 Vue 3 社区标准和风格指南,并略作调整以适应组织的用例(如果有)。
那么,我们如何在 Vue 3 中实现可预测性呢?在接下来的小节中,我们将探讨几种在您的企业 Vue 3 应用程序中实现标准的方法。
可预测性的社区标准
如果您来自 Vue 2,您应该已经熟悉其中存在的标准。我们将从那里讨论添加更多 Vue 3 特定标准。
Vue 有以下页面,您可以查看社区标准:
-
首先,请查看官方 Vue.js 风格指南(
v3.vuejs.org/style-guide/#rule-categories
) -
在 Vue 3 中,始终使用由 Vue 命令行界面(CLI)或 Vite 生成的脚手架(
vuejs.org/guide/quick-start.html
) -
官方 Vue.js 库可以在 社区 指南 下找到(
vuejs.org/about/community-guide.html
) -
使用最流行的组件框架之一,如 Vuetify (
vuetifyjs.com/en/
) 或 Quasar (quasar.dev/
)
官方库和组件库
使用官方库和组件库不仅为您的项目带来功能,还强制执行标准,并允许您根据 Vue 社区的标准和通常可接受的模式构建应用程序。
例如,Vuex 是一个以实现模式和库相结合而自豪的状态管理系统,因为它在构建 Vue 应用程序时强制执行遵循的标准。
另一个很好的例子是 Vue Router,它允许开发者以适应其他项目的方式构建路由系统。
所有这些的好处是,当一个使用这些库构建的开发者加入到一个新的代码库中时,使用这些工具,它变得可预测。
标准文件结构
项目标准的一个重要方面是文件结构。文件结构是项目标准的一个有争议的方面,因为不同的组织和项目使用不同的结构,Vue 也没有提供详细的文档来指定一个结构。
然而,当你使用官方的 Vue CLI 时,它提供了一个创建标准文件夹和文件结构的起点,这在 Vue.js 界内被广泛使用,并且对于全球的 Vue 开发者来说最为熟悉。
下面的代码块展示了如何使用官方的 Vue 3 独立 CLI(称为 Vite)创建一个新的 Vue 3 项目:
npm create vite@latest
npm create vite@latest my-vue-app --template vue
下面的截图显示了使用 Vue 3 官方 CLI(称为 Vite)的官方项目脚手架:
图 4.1 – 官方 Vue CLI 文件结构
在前面的截图中所使用的初始结构应该对许多开发者来说已经很熟悉了,因此使其变得可预测。始终遵循 Vue 的初始结构,在此基础上构建,并且只有出于良好的理由才进行更改。
推荐的组件规则
Vue 组件目录是混乱开始的地方,因为可以创建成千上万的文件和 Vue 组件,随着时间的推移,管理起来变得非常困难。
将你的代码库调整为遵循官方 Vue 3 风格指南是创建可预测代码库的起点,并且你可以从那里学习很多关于如何使你的文件夹和文件结构对开发者来说更加可预测的知识。风格指南为 Vue 生态系统提供了许多社区标准和最佳实践。
这里列出了其中一些最重要的要点:
-
首先,我们有 单文件组件(SFC)风格指南,它陈述了许多需要遵循的点,其中重要的一点是,你的组件应该以 PascalCase 命名。
-
其次,SFC(
vuejs.org/guide/scaling-up/sfc.html
)应始终一致地排序<script>
、<template>
和<style>
标签,其中<style>
在最后。这是因为script
和template
标签总是必要的,而style
标签是可选的。 -
它还指出,在可能的情况下,每个组件都应该定义在其自己的专用文件(SFC)中。这就是 Storybook 或 Atomic Design 在这里发挥作用的地方,我们将在接下来的章节中看到。
-
此外,组件名称应始终为多词,以避免与任何现有或未来的 HTML 元素冲突。不要创建名为
Table
或Button
的组件,因为这些名称的 HTML 标签已经存在;您可以创建多词,例如以下DataTable
或CustomButton
。 -
最重要的是,紧密耦合的子组件应该以父组件的名称为前缀,例如在
TodoList
组件中的TodoListItem
。此方法也有助于调试,因为开发者可以轻松地识别出错误信息中的名称错误的组件。
Vue.js 在 vuejs.org/style-guide/
提供了一份完整的风格指南,其中包含许多其他标准,这些标准将帮助您的项目对社区范围内的开发者更加可预测。
推荐的社区通用标准以提高可预测性
几年来,Vue 社区已经就 Vue 开发者应使用的、旨在提高代码库可预测性的众多不同标准进行了发展和争论。
在以下小节中,我们将讨论一些这些标准以及如何在企业项目中实施它们。
平面组件目录
平面组件目录意味着为您的 Vue 组件和团队指定特定的命名约定,并在整个应用程序开发过程中坚持该约定。
您可以使用单一或嵌套的目录结构,但命名约定应保持一致。接下来的两个截图展示了实现平面组件目录的不同方式。下面的截图展示了单一平面组件目录:
图 4.2 – 单一平面组件目录
下面的截图展示了嵌套平面组件目录:
图 4.3 – 嵌套平面组件目录
标准化的路由/页面命名约定
可预测性原则的另一项重要改进是在整个团队和项目中拥有适当且定义明确的路由/页面命名约定。
例如,使用 Laravel 或 AdonisJS 中使用的路由/页面命名约定,对于熟悉这些框架的开发者来说,可以轻松预测代码库。如果您定义了自己的约定并坚持使用,这也适用。它允许新成员轻松预测和理解您的代码库。
下面的截图展示了如何根据 Laravel 和 AdonisJS 的路由标准来结构化您的路由,以实现可预测性:
图 4.4 – 展示可采用的模式
当您在路由链接中使用路由时,应始终正确引用其名称,并在程序中使用以获得更多一致性和灵活性。
例如,参见以下内容:
<router-link :to="{name: PhotosIndex}">Photos</router-link>
此外,请注意,并非所有路由都完全符合此模式,因为一些路由可能比其他路由更简单。如果发生这种情况,一个好的建议是继续使用 PascalCase 为你的路由名称保持一致性。
更全面的文件结构
使用 Vue CLI 的基本文件结构是预测性的良好起点,并可以从那里扩展以包括其他文件和目录,从而以标准化的方式使我们的企业项目更具可预测性。
以下截图展示了如何扩展文件结构以包括其他必要的文件和目录:
图 4.5 – 从默认 Vue CLI 结构扩展文件结构
额外的文件和文件夹将完全取决于你的团队、组织或项目,但图 4.5 中的额外文件夹是我们为正在构建的项目定义的结构,从默认 Vue CLI 结构扩展它使其更具可预测性。
此外,在标准目录的根目录中提供一个 README.md
文件(changelog.md/
),解释目录的目的以及应包含在其中的任何规则或如何使用目录文件,这非常有用。这对于那些不是社区标准的标准尤其有用。
尽管我们倾向于使代码库对开发者足够可预测,无论项目如何使用社区标准和 Vue 风格指南,仍有一些情况需要定义特定于我们项目或团队的通用文件和文件夹。
虽然创建一个可预测的代码库对于使用上一节中讨论的步骤和模式的大型项目和团队来说是个不错的选择,但仍有很多东西可以探索。在下一节中,我们将探讨可以用来构建更大规模企业项目的不同模式、架构和结构。
不同的前端架构模式
在本节中,我们将探讨我们可以用来构建我们的企业 Vue 3 应用程序的不同架构模式。
微前端架构
当谈到构建企业前端项目时,微前端架构是首先想到的架构。正如官方文档中所述,它将后端微服务概念扩展到前端世界。
微前端的概念源于在后台网络应用程序中用于将巨大块拆分为更小、更易于管理的代码库的流行词“微服务”(martinfowler.com/articles/microservices.html
)。
这种软件开发方法使团队更容易管理、维护和快速部署更大规模的企业应用程序。
这个概念已经改变了多年来后端应用的开发方式,现在以微前端的形式引入到前端项目中。
根据马丁·福勒(martinfowler.com/articles/micro-frontends.html
)的说法,“微前端是一种架构风格,其中独立交付的前端应用被组合成一个更大的整体。”
近年来,随着最初的应用,这一概念在大型项目中得到了巨大的采用,从而将微服务的好处引入到前端项目中。
实施微前端架构带来的以下是一些关键好处:
-
它带来了更可扩展的组织,具有解耦和自治的团队
-
它带来了更小、更紧密、更易于维护的代码库
-
它提供了以更渐进的方式升级、更新或甚至重写前端部分的能力
尽管在官方文档中概述了使用这种架构在您的企业项目中带来的巨大好处,但这种模式需要陡峭的学习曲线、更多的团队外派人员以及大量的团队成员。
以下图展示了团队使用微前端为 Pinterest 示例应用构建端到端示例:
图 4.6 – 团队使用微前端的端到端示例
这里展示了一个使用微前端架构构建 Pinterest 示例应用的架构图:
图 4.7 – 微前端在实际应用中的截图
从截图中,我们可以轻松地将每个功能分离成不同的服务,并为每个服务配备一个专门的前端工程师团队来处理。
微前端是企业应用中最佳架构模式之一,因为微前端的核心思想包括隔离的团队代码、技术无关性、所有权等。这些特性使得开发企业应用变得轻松。然而,其他模式也被广泛使用,我们将在下一节中探讨它们。
原子设计
原子设计(bradfrost.com/blog/post/atomic-web-design/
)是一种构建设计系统的方法论。布拉德·弗罗斯特首先将其用于使用化学思想创建可扩展的设计系统。
从化学课中,我们知道物质由原子组成,这些原子结合形成分子,进而结合形成更复杂的生物体,最终创造出宇宙中的所有物质。
同样,我们可以将我们的组件分解成基本构建块,并从这里开始工作。这些构建块可以从化学示例中分为五个组件,如下所示:
-
原子
-
分子
-
生物体
-
模板
-
页面
这张由 Rohan Kamath 提供的图表(blog.kamathrohan.com/atomic-design-methodology-for-building-design-systems-f912cf714f53
)清楚地展示了原子设计元素:
图 4.8 – 原子设计元素解释(来源:https://blog.kamathrohan.com/atomic-design-methodology-for-building-design-systems-f912cf714f53)
让我们探索这些组件中的每一个,以便理解它们。
原子
在科学课上,我们学习了原子是物质的基本构建块。但将它们应用于网络界面时,原子是像input
、label
这样的 HTML 标签。它们也可以定制,包括抽象元素,如调色板、字体或动画。
原子本身并不很有用,除非与其他元素结合形成分子。
分子
当我们开始组合原子时,事情开始变得有点有趣和具体。
化合物的最小单位称为分子,它由结合在一起的原子组成。在网络界面中,这些分子具有自己的属性,并成为任何设计系统的骨架。
例如,表单输入、标签或按钮作为独立的功能性并不十分有用,但结合成表单后,它们就变得非常有用,因为它们实际上可以完成某些事情。此外,通过组合原子形成有用的组件,这些组件变得可重用,也可以组合成生物体。
生物体
生物体是由用于形成组件相对复杂和独特部分的分子组合而成。
生物体被设计成由不同或相似类型的分子组成。例如,一个分子可以包含主要导航、社交媒体渠道列表、搜索表单和标志。
从分子构建生物体的奇妙之处在于,它鼓励创建独立、便携或可重复使用的组件。
模板
模板对网络开发世界来说已经非常熟悉;它们是由预定义的、拼接在一起的生物体组成的页面。在模板中,设计开始整合,页面的布局变得结构化和可见。
每个模板都包含所有相关的抽象分子、生物体和原子,在某些情况下。由于模板是可见的页面或页面的一部分,客户可以开始看到最终设计。
使用模板,你可以创建不同版本的设计,无论是高保真度、低保真度等。模板更像是 HTML 线框,也可以成为最终交付成果。
页面
页面是模板的具体实例;在某些情况下,一个复杂的页面可以包含多个模板组合成更大的页面。
页面提供了用户最终将看到的准确描述,它们是最高级别的保真度和最直观的。通常,大多数时间都花在这里,并且更多的审查都围绕它进行。
在 Vue.js 中,页面代表用户在导航应用程序时访问的不同路由。
使用原子设计原则使我们能够从抽象的页面或模板过渡到具体的页面。正因为如此,我们能够创建既促进一致性又具有可扩展性的系统,同时展示事物在其最终上下文中的样子。
在这本书中,我们将学习如何使用原子设计模式来构建我们的企业项目,并使用 Storybook 来设计系统。
小贴士
设计系统是一套相互关联的模式和标准,通过减少冗余,在各个页面和渠道之间创建共享语言和视觉一致性,以实现大规模的设计管理。
Storybook
Storybook 可以与任何架构模式结合使用,例如原子设计,以更快地构建以组件驱动的用户界面(UIs)。根据官方网站 (storybook.js.org/
),Storybook 是一个用于独立构建 UI 组件和页面的开源工具。它简化了 UI 开发、测试和文档。
Storybook 允许我们,作为开发者,在隔离的环境中创建和测试组件。
在下一节中,我们将学习如何将 Storybook 实现到我们的项目中,并开始使用原子设计原则来创建可维护的 Vue.js 3 项目。
在 Vue.js 3 中实现 Storybook
访问官方文档以了解 Vue 实现 (storybook.js.org/docs/vue/get-started/introduction
) 并跟随实现步骤。在 Storybook 中,一切围绕故事展开。一个故事描述了渲染组件的状态,并捕捉了组件在渲染时应该/可以执行的所有操作。
安装 Storybook
您可以使用 Storybook CLI 在单个命令中将其安装到现有 Vue.js 项目的根目录中。
在安装过程中,Storybook 会检查您的项目依赖项,并提供最佳的配置。
接下来,根据您的框架,首先构建您的应用程序,然后通过运行以下命令来检查一切是否正常工作:
npx sb init
npm run storybook
以下命令将启动一个新的开发服务器,并打开一个显示欢迎界面的浏览器窗口:
图 4.9 – 欢迎界面
创建一个故事就像告诉计算机一个特定组件将做什么,执行任务所需的属性,以及特定组件可以具有的不同设计。在下一节中,我们将探讨如何在 Storybook 中创建一个故事。
创建一个故事
在我们深入创建故事(组件)之前,让我们确保我们使用原子设计和 Storybook 为此项目同步文件夹结构。
以下屏幕截图显示了实现原子设计和 Storybook 的 stories
文件夹的完整文件夹结构:
图 4.10 – 展示 Vue 3 组件和 Storybook 结构的屏幕截图
从屏幕截图示例中,我们已经重构了我们的 Vue.js 项目以使用原子设计原则和文件夹结构(红色指示器),并且 Storybook 添加了 stories
文件夹(黄色指示器)以帮助我们了解如何编写自己的故事。我们可以删除 stories
文件夹,并按照 图 4.10 中的模式在 component
文件夹内创建我们的故事。
现在,我们可以开始创建故事;请记住,一个故事必须描绘特定的动作或一系列相关的动作。
这里是我们为将在整个项目中使用的 Button
组件创建的故事:
import MyButton from "./Button.vue";
// More on default export: https://storybook.js.org/docs/vue/writing-stories/introduction#default-export
export default {
title: "/Button",
component: MyButton,
// More on argTypes:
// https://storybook.js.org/docs/vue/api/argtypes
argTypes: {
backgroundColor: { control: "color" },
onClick: {},
size: {
control: { type: "select" },
options: ["small", "medium", "large"],
},
},
};
// More on component templates: https://storybook.js.org/docs/vue/writing-stories/introduction#using-args
const Template = (args) => ({
// Components used in your story `template` are defined
// in the `components` object
components: { MyButton },
// The story's `args` need to be mapped into the template
// through the `setup()` method
setup() {
return { args };
},
// And then the `args` are bound to your component with
// `v-bind="args"`
template: '<my-button v-bind="args" />',
});
export const Primary = Template.bind({});
// More on args: https://storybook.js.org/docs/vue/writing-
// stories/args
Primary.args = {
primary: true,
label: "Button",
};
export const Secondary = Template.bind({});
Secondary.args = {
label: "Button",
};
export const Large = Template.bind({});
Large.args = {
size: "large",
label: "Button",
};
export const Small = Template.bind({});
Small.args = {
size: "small",
label: "Button",
};
例如,按钮将在 atoms
目录中创建,因为它是一个单一元素,尽管它可以有不同的属性和动作,如蓝色按钮、白色按钮、可点击按钮、禁用按钮等。它仍然是一个按钮。
从故事中,我们可以看到 Button
组件将有两个尺寸(small
和 large
),此外,它还将有两个设计,即 primary
和 secondary
,并且它还将接受两个属性,即 primary
和 label
。
您可以通过官方文档了解如何编写故事来测试按钮的属性和动作(storybook.js.org/docs/vue/get-started/whats-a-story
)。
一旦您创建了所有组件和故事,您应该有一个如下截图所示的目录:
图 4.11 – 展示包括 Storybook 故事的完整目录的屏幕截图
此外,按照这样的项目结构,开发者可以轻松理解结构并快速找到组件。团队成员可以在实际项目中使用 Storybook 在使用组件之前轻松测试具有不同属性的组件。
在下一节中,我们将探讨如何在 Vue.js 3 中创建国际化应用程序,这使得您的应用程序不仅限于单一的语言和文化环境。
实施国际化和本地化
构建企业应用程序的本质是支持全球各地的本地市场,而要实现这一点,国际化就派上用场了。
准备软件以支持本地语言和其他地理区域的文化的过程被称为国际化(I18n)。
I18n 经常被误认为是本地化(L10n),有时甚至翻译。
虽然 Il8n 是一种产品开发方法,旨在使一个代码库能够支持全球语言和特定地区格式的格式和行为,但 L10n 使产品针对特定市场或地区(包括界面翻译和术语可能的调整等)。
在本节中,我们将首先探讨软件国际化的好处,并进一步探讨如何在 Vue 3 中实现 I18n。
软件国际化的好处
创建内部化软件的好处是巨大的,其中一些列在这里:
-
它创建出满足多个地区技术和文化需求的更高质量的软件
-
它提供了更高的国内客户接受度和满意度
-
它为产品的所有语言提供单一源代码
-
内部化软件减少了本地化所需的时间、成本和努力
-
内部化软件更简单,并支持产品未来迭代时更容易的维护
市场接受度是软件在发布前后没有完全内部化时出现的主要问题之一。
因此,我们将探讨如何在我们的企业 Vue 3 应用程序的开发阶段实现 I18n。
安装 Vue I18n
在 Vue 3 中,Vue-I18n 是一个优秀的兼容插件,用于实现 I18n,并且它能够轻松地将一些本地化功能集成到你的 Vue.js 应用中。
按照以下步骤国际化你的应用:
-
根据官方文档(
https://vue-i18n.intlify.dev/installation.html
),有不同方式来安装该包,但我们将使用npm
命令安装,如下所示:npm install vue-i18n@9
-
安装后,在 Vue 3 的
main.js
文件中,添加以下脚本:import { createApp } from 'vue' import { createI18n } from 'vue-i18n' const i18n = createI18n({ // something vue-i18n options here ... }) const app = createApp(App) app.use(i18n) app.mount('#app')
在前面的设置中,你应该已经将内部化添加到你的 Vue 项目中,但当开发者开始添加翻译时,它将很容易变得臃肿。因此,我们建议创建一个locales
文件夹,其中将添加所有与地区相关的配置。
-
让我们按照如下方式在根目录下创建文件夹和文件:
mkdir src/locales touch src/locales/index.js src/locales/en.json src/locales/fr.json src/locales/de.json
-
接下来,在每个翻译文件中,添加以下代码和其他翻译:
{ "welcomeMsg": "Welcome to Your Vue.js App", …. }
-
在
index.js
文件中,添加以下脚本以导入不同的地区:import en from «./en.json»; import fr from "./fr.json"; import de from «./de.json»; const messages = { en, fr, de, }; export default messages;
-
最后,将文件添加到你的
main.js
文件中的createI18n
配置中:import locales from "./locales/index.js"; const i18n = createI18n({ locale: "en", // set locale fallbackLocale: "en", // set fallback locale messages: locales, // set locale messages });
按照这种结构安排你的文件和文件夹,可以方便地采用和维护。让我们在以下屏幕截图中查看我们的项目最终结构,包括国际化:
图 4.12 – Vue 3 应用程序的最终结构
摘要
本章更深入地回顾了使用 Vue 3 架构大型 Web 应用。我们通过深入研究可预测性法则以及如何使用社区推荐的包来提高你的 Vue.js 3 企业级应用的可预测性,讨论了结构和文件架构。
我们还详细介绍了如何利用微前端架构的优势。此外,我们还讨论了如何使用 Storybook 实现 Atomic Design,以简化你的组件目录,并使你的企业项目更容易理解。
接下来,我们讨论了如何将 I18n 添加到你的 Vue 应用中。我们还讨论了其优势和如何正确地将 I18n 集成到你的 Vue 3 应用程序中。
在下一章中,我们将探讨 GraphQL、GraphQL Apollo Server 2、查询、突变,以及如何将这些技术集成到你的 Vue.js 3 应用程序中。此外,你还将学习如何利用 GraphQL 来提供可扩展且性能高的应用程序。
第五章:GraphQL、查询、突变和 RESTful API 简介
在前面的章节中,我们探讨了使用 Vue 3 开发大规模企业应用的不同库和方法。在本章中,我们将首先了解 GraphQL 是什么以及它与 REST 的不同之处。接下来,我们将探索 GraphQL、GraphQL Apollo Server 2、查询和突变,以及如何将这些技术集成到您的 Vue.js 3 应用程序中。此外,您还将学习如何利用 GraphQL 来提供可扩展且性能高的应用程序。
本章将涵盖以下关键主题:
-
GraphQL 简介
-
理解查询和突变
-
将 GraphQL Apollo 客户端与 Vue 3 集成
此外,在本章中,您将学习如何将 GraphQL 集成到 Vue 3 中,并按照可预测性原则正确地构建它,通过使用 GraphQL Apollo 客户端和 Vue 3 实现登录和注册认证系统。
技术要求
要开始本章的学习,我们建议您阅读 第四章,大规模 Web 应用程序的架构,其中我们探讨了使用不同的行业标准结构、架构和标准来构建大规模企业应用。
本章的所有代码文件可以在以下位置找到:github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-5
。
GraphQL 简介
GraphQL 是 API 开发行业中的新热点。虽然 REST 仍然是暴露服务器数据最流行的方式,但它带来了许多 GraphQL 倾向于解决的局限性。
GraphQL 是由 Facebook 创建和维护的一种查询语言。创建 GraphQL 的目的是构建基于直观和灵活语法的客户端应用程序,以描述它们的数据需求和交互。
GraphQL 的一个好处是,我们有一个单一的端点可以访问服务器上的所有数据,而不是在 REST 中有多个端点。
在本节中,我们将探讨您需要了解的所有关于 GraphQL 的内容,GraphQL 的不同独特特性,以及为什么您应该考虑使用 GraphQL 而不是 RESTful API 设计模式。最后,我们将指导您创建并设置您的第一个 GraphQL 服务器,使用 Express。
什么是 GraphQL?
根据官方文档(https://graphql.org/),
GraphQL 是一种用于 API 的查询语言,也是执行这些查询的运行时环境。GraphQL 为您的 API 中的数据提供了完整且易于理解的定义,赋予客户端请求所需数据的权力,而不需要更多,这使得 API 随时间演变变得更加容易,并使强大的开发者工具成为可能。
GraphQL 是一个服务器端运行时,用于执行使用您为数据定义的类型系统进行的查询。此外,GraphQL 不绑定到任何特定的数据库或存储引擎,而是由您现有的代码和数据支持。
图 5.1 – 解释 GraphQL 的图表(来源:https://www.wallarm.com/what/what-is-graphql-definition-with-example)
GraphQL 类型系统定义了可以在 GraphQL 应用程序中使用的各种数据类型。这个类型系统有助于定义将在 GraphQL 应用程序中使用的模式。
要创建 GraphQL 服务,你需要首先定义模式类型,并在这些类型上创建字段,然后提供在每个字段上执行的功能。
例如,我们可以在以下代码片段中定义一个新的模式类型 Photo
,以演示类型在 GraphQL 中的工作方式:
type Photo {
id: ID!
name: String!
url: String!
description: String
}
现在我们对 GraphQL 有了一定的了解,也看到了如何定义 GraphQL 模式类型。接下来,在我们深入创建 GraphQL 查询和解析器之前,让我们探索 GraphQL 的特性。
GraphQL 的特性
GraphQL 拥有出色的功能。我们将在接下来的小节中探索 GraphQL 的几个功能。
易于开始
GraphQL 的学习曲线很容易,尤其是对于熟悉使用 RESTful 设计模式构建 API 的开发者来说。
用户可以从使用小查询立即获取数据开始使用 GraphQL,稍后可以了解一些高级特性。
为交互式应用程序而构建
GraphQL 是为实时和交互式应用程序而构建的,因为客户端和服务器之间的变化几乎立即发生,从而提供快速响应。
小巧灵活
GraphQL 允许用户请求并接收请求的确切数据。此功能解决了 RESTful API 中过度和不足获取的问题。
兼容性强
GraphQL Apollo 是为了与任何构建设置、任何 GraphQL 服务器和任何 GraphQL 模式兼容而构建的。
可逐步采用
GraphQL 是为了使集成到新项目或现有项目中变得毫不费力而构建的,而不会破坏任何更改。它很容易适应。
为什么要使用 GraphQL 而不是 REST?
在本节中,我们将确定 GraphQL 的一些特性,并讨论为什么你应该在后续的 API 开发中使用 GraphQL 而不是 RESTful API。我们将讨论这些特性中的几个。此外,我们将在本节中详细比较这些技术与 GraphQL 与 RESTful API 指南。
以下小节将提到使用 GraphQL 而不是 RESTful API 的前五个原因。
强类型模式
在 GraphQL 中,模式定义语言(SDL)用于定义客户端和服务器之间的合同以及定义客户端如何访问服务器中的数据。GraphQL 使用强类型系统来定义 API 的功能。所有暴露给客户端的 API 都记录在一个称为 SDL 的架构中。
一旦定义了这些架构,前端和后端就可以在没有任何进一步更改或帮助的情况下单独通信,因为前端知道架构中的数据始终会在整个系统中保持同步或一致。
这解决了 REST 中的数据不一致问题。前端期望获取特定的数据集,但由于没有定义一致的架构,因此检索到了不同的数据。
无过度或不足获取数据
过度或不足获取数据的问题是 RESTful API 中已知的问题,其中客户端通过访问返回固定数据结构或获取过多或过少数据的端点来下载数据。
过度获取数据是获取比应用程序所需更多数据的问题。它也可以意味着获取比满足请求所需更多的数据。在使用 RESTful API 时,你必须获取所有用户的详细信息或创建一个新的端点,该端点仅返回应用程序中所有用户的名称,只是为了在您的前端应用程序中显示用户的名称。而在 GraphQL 中,你可以使用单个查询仅返回所有用户的名称或任何其他详细信息,通过创建单独的查询或端点。
不足获取数据很少见,但发生在特定端点没有提供所有所需信息的情况下。客户端必须根据需要发出附加请求以访问其他信息。
GraphQL 通过提供一个客户端指定所需信息的媒介来解决此问题,并返回所需的确切信息。
节省时间和带宽
过度获取数据的问题可能会导致客户端带宽消耗增加,这可能会随着时间的推移导致应用程序出现延迟。使用 RESTful API 设计模式,从大量负载中整理所需信息需要更多时间。
GraphQL 允许你只选择你需要的内容,从而减少通过网络传输的数据量。
多个端点
RESTful API 的重要问题之一是端点过多,难以访问信息。例如,如果客户端想要通过 ID 访问特定用户,你将看到 /users/1
。此外,如果你要访问该用户的照片,你必须向另一个端点 /users/1/photos
发送请求。
在 GraphQL 中,你只有一个端点,并且不需要发送多个请求来获取关于用户的不同信息。
如下所示,使用 GraphQL,你可以在单个请求中访问所有用户的信息:
{
me {
name,
photos {
title,
url
}
}
}
上述脚本将仅访问用户的name
以及所有用户的photos
的title
和url
。
版本控制不是必需的
版本控制是 RESTful API 的一个特性,当对 API 进行更改和更新时,会为 API 分配不同的版本。这样做是为了避免在生产中造成破坏性更改,可能会对用户造成问题。
如果我们希望用户使用最新版本的我们的新功能,我们必须强迫他们更新应用程序,这并不是一个好的用户体验。
在 GraphQL 中,无需担心版本控制,也不需要强迫用户更新他们的应用程序以使用新更改,因为它是自动发生的,客户端只实现 SDL 中可用的功能。
在这里,我们讨论了使用 GraphQL 的不同功能和好处,以及为什么你应该考虑使用 GraphQL 而不是 RESTful API。在下一节中,我们将进一步讨论 GraphQL 与 RESTful API 之间的区别。
GraphQL 与 RESTful API 的区别
GraphQL 与 REST 之间的核心区别在于,GraphQL 是一个规范和查询语言,而 REST API 是网络应用程序的架构概念。
这两个概念在创建和维护可扩展的微服务和大型企业应用中发挥着重要作用。
因此,选择特定的技术将取决于你在每种技术中的国际化程度,你团队对哪种技术更舒适,以及哪种技术使你的产品开发更容易、更快。
以下要点展示了你可能想要考虑的其他差异:
-
GraphQL 是一种查询语言,通过集成 API 来解决问题,而 REST API 是一种描述设计 API 的传统标准的架构风格
-
此外,使用 GraphQL,你可以使用单个端点访问服务器上的所有数据,无需多个端点,而 REST API 允许多个端点和一组 URL,每个 URL 都暴露一个单一的资源,这可能会让人难以理解
-
GraphQL 使用客户端驱动的架构,缺乏内置的缓存机制,而 REST API 使用服务器驱动的架构并自动使用缓存
-
在 GraphQL 中不需要 API 版本控制,其响应仅以 JSON 格式,而 REST API 支持多个 API 版本控制,并允许以 XML、JSON 或 YAML 格式输出响应
-
GraphQL 提供类型安全和自动生成的文档,它还允许模式拼接和远程数据获取,而 REST API 不提供类型安全或自动生成的文档,简化与多个端点的工作需要昂贵的自定义中间件
-
GraphQL 也是一种用于执行现有数据查询的应用层服务器端技术,而 REST 是一种用于定义创建网络服务约束的软件架构风格
-
GraphQL 可以根据模式组织,而 REST 则不是根据模式组织,而是根据端点组织
-
与 REST API 相比,GraphQL 的开发速度更快
-
GraphQL 突变的消息格式应该是一个字符串,而 REST API 的消息格式可以是任何内容
-
GraphQL 使用元数据来进行查询验证,而 REST 没有可缓存的机器可读元数据
我们已经探讨了 GraphQL 和 REST API 之间的区别,以便让你了解哪个更适合你的企业应用。我们将让你做出选择,但在接下来的章节中我们将更深入地探讨 GraphQL。在下一节中,我们将讨论查询和突变,进一步介绍如何创建你的第一个查询和突变。
理解 GraphQL 中的查询和突变
查询和突变在 GraphQL 中至关重要,因为它们是访问或从前端向 GraphQL 服务器发送数据的唯一方式。
使用查询
GraphQL 查询定义了客户端可以在 GraphQL API 上运行的查询。如果你熟悉 REST API,它与流行的 GET
请求同义。
你可以用多种方式定义 GraphQL 查询,但建议定义一个根查询来包装所有的查询。
以下代码片段展示了如何定义一个名为 RootQuery
的根查询:
type RootQuery {
user(id: ID): User # Corresponds to GET /api/users/:id
users: [User] # Corresponds to GET /api/users
photo(id: ID!): Photo #Corresponds to GET/api/photos/:id
photos: [Photo] # Corresponds to GET /api/photos
}
你也可以像这样为 User
和 Photo
定义单独的查询:
type User {
id: ID!
name:String!
email: String!
}
type Photo {
id: ID!
title:String!
description: String
url: String!
user: User
}
通过前面的查询,我们已经成功定义了端点来检索用户和照片,这与使用 REST API 的 GET
方法执行的方式相同。
接下来,我们将探讨如何使用突变在 GraphQL API 上创建或更新数据。
使用突变
GraphQL 中的突变用于从我们的 GraphQL API 创建、更新和删除数据,分别与 REST API 的 POST
、PUT
和 DELETE
方法同义。
在 GraphQL 中创建突变很简单;看看下面的代码片段:
type RootMutation {
createUser(input: UserInput!): User
# Corresponds to POST /api/users
updateUser(id: ID!, input: UserInput!): User
# Corresponds to PATCH /api/users
removeUser(id: ID!): User
# Corresponds to DELETE /api/users
createPhoto(input: PhotoInput!): Photo
updatePhoto(id: ID!, input: PhotoInput!): Photo
removePhoto(id: ID!): Photo
}
前面的代码片段展示了如何创建突变。此外,我们还创建了 createUser
、updateUser
和 removeUser
来从 GraphQL API 中创建、更新和删除用户。
最重要的是,为了让 GraphQL 连接到我们的数据库并在查询和突变操作中执行操作,我们需要定义一个解析器,这将在下一小节中介绍。
解析器
GraphQL 解析器将我们的查询和突变连接到执行的正确方法。它通知 GraphQL 在请求每个查询/突变时应该做什么。这是一个基本功能,它执行击中数据库层以执行 CRUD 操作、击中内部 REST 端点或调用微服务以满足客户端请求的繁重工作。
让我们将前面章节中创建的查询/突变映射到一个解析器上,该解析器将在请求任何查询/突变时被调用:
import sequelize from '../models';
export default function resolvers () {
const models = sequelize.models;
return {
// Resolvers for Queries
RootQuery: {
user (root, { id }, context) {
return models.User.findById(id, context);
},
users (root, args, context) {
return models.User.findAll({}, context);
}
},
// Resolvers for Mutations
RootMutation: {
createUser (root, { input }, context) {
return models.User.create(input, context);
},
updateUser (root, { id, input }, context) {
return models.User.update(input, { ...context,
where: { id } });
},
removeUser (root, { id }, context) {
return models.User.destroy(input, { ...context,
where: { id } });
},
// ... Resolvers for Photos
}
};
}
为了促进理解,我们导入了来自 sequelize
的模型,这是一个用于使用定义的方法操作数据库表的 对象关系映射(ORM)。
接下来,我们创建并导出了一个 resolver
函数,它返回一个包含 RootQuery
和 RootMutation
的对象。
接下来,在 RootQuery
和 RootMutation
对象内部,我们使用适当的企业逻辑解决每个方法。
例如,当客户端请求所有用户时,GraphQL 前端客户端将调用定义在 sequelize
ORM 中的用户查询。相同的逻辑适用于所有突变。
在本节中,我们解释了 GraphQL 的工作原理以及如何创建自己的 GraphQL API,以了解在前端为大型项目结构化 GraphQL 客户端的最佳方式。
在下一节中,我们将探讨如何以最佳方式结构化你的企业级 Vue.js 应用程序,以实现可扩展性和更快地被团队成员接受。记住我们在 第三章* 中讨论的团队的可预测性法律* Vue.js 3 中的性能扩展。
将 GraphQL Apollo 客户端与 Vue 3 集成
很容易想知道将 GraphQL 客户端集成到 Vue 3 的最佳方式以及如何在企业项目中结构化它以促进团队成员,包括新成员的快速采用。
在我的全栈软件工程师职业生涯中,我最近加入了一家使用 Vue 3 和 GraphQL 来颠覆德国金融科技行业的金融科技公司,我对这样一个大型代码库的安排以及我如何轻松地直接解决我的第一个任务印象深刻。
使用 GraphQL 结构你的 Vue 3 项目有许多方法,但我想要概述一下我见过的最适合小型或大型企业项目的方法,包括我参与的那个金融科技项目。
GraphQL Apollo 客户端是一个 JavaScript 库,用于连接到 GraphQL 服务器以交换数据。使用该库,您可以连接到服务器,发送请求,并从服务器接收响应。
首先,我们将开始列出并安装 Vue 3 中 GraphQL 和 GraphQL 客户端所需的必要包。
安装
按照以下步骤安装所有必要的包:
-
输入以下命令来安装
graphql
、graphql-tag
、apollo-composable
和apollo client
。这些是从官方文档中推荐的库,用于通过 Vue 3 与 GraphQL 服务器进行通信:npm install --save graphql graphql-tag @apollo/client @vue/apollo-composable
-
安装后,我们将在我们创建的文件夹结构中(在 第三章**,Vue.js 3* 中的性能扩展*)的
plugins
文件夹内创建一个名为apollo.config.js
的新文件,并添加以下脚本以配置graphql
客户端:```js import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core' // 与 API 的 HTTP 连接 const httpLink = createHttpLink({ // 这里应使用绝对 URL uri: 'http://localhost:3020/graphql', }) // 缓存实现 const cache = new InMemoryCache() // 创建 Apollo 客户端 const apolloClient = new ApolloClient({ link: httpLink, cache, }) export default apolloClient ```js
-
最后,在您的 Vue 3
main.js
文件中,向现有的脚本中添加以下脚本:```js import { createApp, provide, h } from 'vue' import apolloClient from "./plugins/apollo.config"; import { DefaultApolloClient } from '@vue/apollo-composable' … const app = createApp({ setup () { provide(DefaultApolloClient, apolloClient) }, render: () => h(App), }) … ```js
在前面的步骤中,我们展示了如何在 Vue 3 企业应用程序中安装 GraphQL 客户端,并与其他库完全设置。在下一节中,我们将讨论使用 GraphQL 结构化我们的 Vue 3 应用程序的最佳实践。
结构化 GraphQL
在成功安装和配置 Apollo Client 与 Vue 3 后,让我们根据可预测性原则来结构化我们的查询和突变,以便老成员和新成员能够轻松适应项目。
在 src
文件夹内创建一个名为 graphql
的新文件夹。这个新文件夹将包含所有我们的查询和突变,根据应用程序的功能分组到不同的目录中。
让我们从上一节中开发的模式中举一个例子。从模式中可以看出,我们的项目有 User
和 Photo
功能,因此我们将在通用的 graphql
文件夹内为这些特定功能创建不同的文件夹。
使用以下命令行或从您的代码编辑器手动创建新文件夹:
mkdir src/graphql
mkdir graphql/users graphql/photos
您应该有一个新的文件夹结构,如下面的截图所示:
图 5.2 – 安装和设置 GraphQL 后的文件夹结构
安装和设置 GraphQL 后,您的文件夹结构应该看起来像前面的截图,包含每个功能及其对应的查询和突变文件。
以这种方式结构化您的 GraphQL API,使您的团队能够自动理解在哪里找到与 GraphQL 查询或突变相关的内容,以及它们在寻找它们的功能中。
现在我们已经确定了预测性的文件夹结构,在下一节中,我们将通过一个实际练习来演示,使用我们在前几节中学到的结构,通过 GraphQL Apollo 客户端和安装在 GraphQL 服务器上的 JWT 来认证用户。
JWT 认证用于登录/注册
在地面上已有的结构基础上,添加新功能和编写查询/突变变得容易,而不会在代码库中分散。
让我们通过以下步骤演示如何使用 GraphQL 和 Vue 3 实现登录和注册认证过程:
-
首先,在
graphql
文件夹内创建一个名为auth
的新文件夹,并在其中添加mutations.js
:mkdir graphql/auth touch graphql/auth/mutations.js
-
在新创建的突变文件中,为注册和登录端点添加以下脚本:
import { gql } from "graphql-tag"; export const LOGIN_USER = gql` mutation loginUser($input: LoginUserInput!) { loginUser(data: $input) { id name email token } } `; export const REGISTER = gql` mutation register($input: RegisterUserInput!) { register(data: $input) { id name email } } `;
-
接下来,将我们之前创建的
graphql/index.js
文件中的突变导出,使其在 Vue 应用程序中可用:export * from "./auth/mutations";
export
脚本使得导入我们的 GraphQL 查询和突变变得容易得多。
接下来,我们将探讨如何在 Vue 3 组件中调用认证突变并检索用户信息。
使用我们之前安装的 vue-composable
库,我们可以使用不同的 GraphQL 钩子,例如 useMutation
和 useQuery
,来操作 GraphQL API。
-
在
organisms
文件夹内创建一个login
组件,并添加以下代码:<template> <form @submit.prevent="login"> <TextField v-model="email" required type="email" class="mb-6" name="email" label="Email Address" placeholder="Enter your email address" /> <TextField v-model="password" required minlength="6" class="mb-6" type="password" maxlength="50" placeholder="Enter your full password" label="Password" ></TextField> <div class="flex justify-between mb-6"> <CheckField id="remember" v-model="remember"> Remember me</CheckField> </div> <div class="flex justify-center my-9 w-full"> <Button>Sign In</Button> </div> </form> </template>
第一个代码片段显示了组件的模板和视图部分;它包含一个表单组件,其中包含四个子组件,包括 TextField
、CheckField
和 Button
:
<script>
import { LOGIN_USER } from "../../graphql";
export default {
setup(props) {
const email = ref("");
const password = ref("");
const remember = ref(false);
const { mutate: loginUser } =
useMutation(LOGIN_USER, () => ({
variables: {
email: email,
password: password,
remember,
},
}));
const login = () => {
const user = loginUser();
if (!user) return
// Save State and Redirect to Dashboard
};
return {
login,
email,
password,
remember,
};
},
};
</script>
组件的脚本部分执行业务逻辑;它有许多属性和一个名为 Login
的函数,该函数执行应用程序的认证过程。
前面的代码片段展示了如何实现我们项目的登录功能。您可以实现注册组件或查看仓库 (github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-5
) 以获取本章的完整代码库。
图 5.3 – 展示登录表单实现的截图
摘要
本章深入探讨了 GraphQL、GraphQL Apollo Server 2、查询、突变,以及如何将这些技术集成到您的 Vue.js 3 应用程序中。此外,我们还学习了如何利用 GraphQL 来交付可扩展且性能高的应用程序。
我们还详细介绍了如何将 GraphQL 集成到 Vue 3 中,并按照可预测性原则正确地构建其结构。
最后,我们展示了如何使用 GraphQL Apollo Client 和 Vue 3 实现登录和注册认证系统。
在下一章中,您将学习如何使用 Vue 3 和 GraphQL 构建一个完整的 Pinterest 克隆。您将利用对 GraphQL 的了解,使用 Vue 3 和 GraphQL 开发和交付一个如 Pinterest 一样的企业级应用程序。
第六章:使用 GraphQL 构建完整的 Pinterest 克隆
在上一章中,我们探讨了 GraphQL、GraphQL Apollo Server 2、查询、变更以及如何将这些技术集成到你的 Vue.js 3 应用程序中。此外,我们还学习了如何利用 GraphQL 来提供可扩展和性能高的应用程序。本章将演示如何使用 Vue 3 和 GraphQL 构建一个完整的 Pinterest 克隆应用。你将利用你对 GraphQL 的知识来开发和交付一个如 Pinterest 这样的企业级应用程序,使用 Vue 3 和 GraphQL。此外,你将学习如何使用一个流行的无头内容管理系统(CMS)Strapi 来创建和管理你的后端 API。
本章我们将涵盖以下关键主题:
-
Strapi 简介
-
搭建 Strapi 项目
-
构建集合
-
构建 Vue 3 Pinterest 应用
-
连接前端和后端
-
测试应用
到本章结束时,你将学会如何使用 GraphQL 创建一个可扩展和性能高的 Pinterest 克隆应用程序。
技术要求
首先,我建议你阅读第5 章,《GraphQL、查询、变更和 RESTful API 简介》,在那里我们探讨了 GraphQL、GraphQL Apollo Server 2、查询、变更以及如何将这些技术集成到你的 Vue.js 3 应用程序中。
本章所需的所有代码文件都可以在github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-6
找到。
Strapi 简介
Strapi 是一个基于 Node.js 的开源无头 CMS,用于通过 RESTful API 和 GraphQL 创建和管理不同形式的内容。
此外,Strapi 使开发、部署和维护 API 更快,并且可以配置为通过任何 HTTP 客户端或 GraphQL 启用的前端使用 API 来消费内容。
这些优势是我们选择使用 Strapi 来创建 Pinterest 克隆应用程序后端的原因,这样我们就可以更多地关注前端,而不必过多关注后端的扩展。
在下一节中,我们将通过搭建 Strapi 项目,构建我们项目中需要的所有集合,并使用模拟数据初始化 Strapi 账户。
搭建 Strapi 项目
开始一个新的 Strapi 后端项目非常简单,就像使用 CLI 工具安装一个新的框架一样。
我们可以通过运行以下任何简单命令来搭建一个完整的后端应用程序,并在我们的默认浏览器中测试它:
```bash
npx create-Strapi-app strapi-pinterest-api --quickstart
# OR
yarn create straps-app strapi-pinterest-api --quickstart
```js
之前的命令在指定的文件夹中搭建了一个新的 Strapi API。接下来,运行以下命令来使用 Strapi 构建和部署你新创建的后端 API:
```bash
npm run build
npm run deploy
```js
前两个命令应该会构建并部署您的应用,这样您就可以通过在默认浏览器中输入以下 URL(localhost:
)来轻松测试它,如果它没有自动打开。
最重要的是,最后一个命令还会打开一个新标签页,显示一个页面,用于注册系统的新管理员用户。请继续填写表格并点击开始按钮来创建一个新的管理员用户。
页面看起来如下:
图 6.1 – Strapi 注册页面
在作为平台管理员注册后,您将拥有创建不同收藏夹以及为您的 API 设置不同用户和权限级别的管理员访问权限。
在下一节中,我们将探讨如何创建不同的收藏夹,这些收藏夹将对应于我们的 Pinterest API 资源。我们还将设置不同的权限级别,以限制我们 Pinterest 应用程序中的每个用户可以访问的内容。
构建收藏夹
Strapi 使用收藏夹来表示资源;例如,如果您的应用程序是一个新闻应用程序,并且您想要创建一个后端来处理帖子、评论等,您将在 Strapi 中创建为帖子、评论收藏夹。
然而,由于我们正在构建一个 Pinterest 克隆版,我们将创建以下收藏夹:照片(图钉)、版块和用户,并且每个收藏夹都将包含它们各自的字段,如下面的步骤所示。
为了演示,我们将创建一个简单的照片(图钉)收藏夹,用于存储我们应用中特定照片的详细信息。在 Pinterest 中,它被称为PIN,但我们将更喜欢称之为PHOTO,因为我们从上一章开始就是这样称呼的。
现在为了存储我们照片的详细信息,我们将在 Strapi 仪表板中创建一个新的photos
。
photos
收藏夹将包含以下字段:标题、url、user_id和描述。这些字段是富有想象力的,并且在我们将其处理成书籍的过程中可能会发生变化:
- 要创建我们的第一个
photos
作为显示名称。
图 6.2 – Strapi 收藏夹仪表板
- 接下来,选择您字段的类型,如下面的屏幕截图所示:
图 6.3 – Strapi 照片收藏夹
- 接下来,输入您的照片收藏夹的字段名称,然后点击+ 添加另一个字段来输入另一个字段:
图 6.4 – Strapi | 添加新的文本字段
- 重复此过程,直到耗尽您收藏夹的字段列表,然后点击完成。
图 6.5 – Strapi 照片仪表板
- 最后,点击保存,并重复为用户、版块和其他你可能想要添加的集合执行此过程。你应该会看到所有集合都列在这里,如图所示:
图 6.6 – Strapi | 用户字段
重要提示
你可以在这里了解更多关于 Strapi 集合的信息 – docs.strapi.io/user-docs/latest/content-types-builder/introduction-to-content-types-builder.html
– 或者观看这个视频 – www.youtube.com/watch?v=bStlyMB0NEw
– 了解我们如何创建项目中将要使用的所有集合。
在下一节中,我们将探讨如何将假数据填充到 Strapi 集合中,以便我们在开始添加新照片之前显示一些照片。
添加数据
在成功创建集合后,我们将填充一些数据,以便我们有足够的照片、版块和用户来工作。
执行以下步骤将一些数据填充到我们创建的集合中。首先,我们将填充一些照片信息,包括照片,并创建一个将存放一些这些照片和执行这些动作的用户:
- 要为我们的 Pinterest 项目添加虚拟数据,我们将点击我们创建的每个集合,并点击+ 添加新用户,如图所示:
图 6.7 – Strapi | 添加新用户
- 接下来,填写创建 Pinterest 应用单个用户所需的信息,并点击保存,如图所示:
图 6.8 – Strapi | 创建条目
- 点击发布,你应该会看到你的新用户已添加到用户集合中,如图所示:
图 6.9 – 列出所有用户
通过重复对所有其他集合(如照片和用户)进行相同的过程来创建更多虚拟数据,以进行测试。在下一节的后面,我们将学习如何在 Strapi 集合中编程创建数据,并使用 Vue 3 构建我们的 Pinterest 克隆应用。
构建 Vue 3 Pinterest 应用
在上一节中,我们探讨了使用 Strapi 创建 Pinterest 应用的后端。在本节中,我们将使用 Vue 3 创建前端。
然而,需要注意的是,由于这是一个演示,我们只会抽象出 Pinterest 的极小部分来表示应用。开发完整的 Pinterest 应用需要努力、团队和资源。
我们将继续使用为本书创建的官方项目。在前面的章节中,我们添加了国际化、结构化项目并构建了登录表单,我们将继续添加其他必要的文件,以形成一个完整的 Pinterest 克隆应用。
最重要的是,我将使用 Tailwind CSS 作为这个项目的 CSS 框架,由于它超出了本书的范围,您可以访问官方文档以使用 Vue 3 进行设置。
您可以从这个仓库克隆项目 – github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications
– 直接开始。
下面是我们正在构建的演示:
图 6.10 – Pinterest 预览
我们已经将所有内容正确地分离并按可扩展的格式进行了结构化,以适应我们的应用程序。以下展示了我们如何结构化代表主页的 HomeOverview
组件:
<template>
<main>
<Header></Header>
<section style="mt-20">
<Cards />
</section>
</main>
</template>
<script setup>
import Header from '../organisms/Header.vue'
import Cards from '../organisms/Cards.vue'
</script>
它包含 Header
和 Cards
组件。我们可以使用 alias
来导入这些组件,使导入的 URL 更短,但我更喜欢向您展示相对路径。
在下一节中,我们将通过创建 Cards
组件并实现显示我们之前创建的所有照片的逻辑来开始构建前端项目。
生成 Cards 组件
让我们先看看 Cards
组件,以探索其中的内容。Cards
组件包含了在我们 Strapi 实例中创建和存储的图片显示逻辑,您可以在下面的代码片段中看到这一点:
<template>
<div class="pin_container sm:justify-center">
<div class="card card_small h-[16.25rem] relative">
<!-- // Medium -->
<Card />
</div>
<div class="card card_medium h-[20.625rem] relative">
<!-- // Small -->
<Card />
</div>
<div class="card card_large h-[28.125rem] relative">
<!-- // Smaller -->
<Card />
</div>
<div class="card card_smaller h-[11.063rem] relative">
<!-- // Medium -->
<Card />
</div>
<div class="card card_small h-[16.25rem] relative">
<!-- // Large -->
<Card />
</div>
</div>
</template>
<script setup>
import Card from '../molecules/Card.vue';
</script>
Cards
组件是魔法发生的地方,因为它代表了应用中每个照片(Pin)的集合。
下面是代码的预览:
图 6.11 – 卡片预览
首先,我们根据卡片的大小(小、小、中、大)来显示卡片。这有助于我们获得与 Pinterest 相同的预览效果。
您可以从这个仓库的 第六章
分支克隆完整的 frontend 代码和 Strapi 后端 – github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-6
。
在上一节中,我们展示了如何通过创建不同的 Card
组件来代表 Pin 信息,从而构建一个简单的 Pinterest 复制品。在下一节中,我们将学习如何将我们的 Strapi 后端连接到我们使用 Vue 3 创建的 Pinterest 前端。
连接前端和后端
最有趣的部分是我们如何结构化我们的 API 请求,以适应可维护性和易于适应性,遵循我们从上一章中学到的最佳实践。
以下截图显示了我们的文件夹结构,其中包含根据我们当前在应用程序和为该项目开发的 Strapi 后端中拥有的功能构建的 GraphQL 端点:
图 6.12 – 带有 GraphQL 和 Strapi 的新文件夹结构的截图
在 graphql
文件夹中,我们定义了三个文件夹,即 auth
、photos
和 users
,它们代表我们当前项目将拥有的功能。让我们看看这些文件夹包含什么。
auth
文件夹
auth
文件夹只包含一个突变,它将处理所有身份验证和授权功能。它将包含诸如 register
、login
、forgotPassword
、sendForgotPasswordEmail
等突变。
photos
文件夹
photos
文件夹是最复杂的,因为它包含 Pinterest 应用程序演示中所有功能。它包含突变和查询,这就是为什么我们为它创建了不同的文件。
在此文件中找到的一些突变包括 createPin
、createBoard
、updatePin
、updateBoard
、deletePin
等。这些突变将不同的操作发送到我们的 Strapi 后端服务器以执行不同的操作。
此外,我们还有一个 queries.js
文件,它包含从我们的 Strapi 后端服务器检索不同类型数据的所有查询。
在此文件中找到的一些查询包括 getPin
、getBoard
、getBoards
、getPins
、getUserPins
、getUserBoards
等。
用户文件夹
users
文件夹包含 Pinterest 应用程序演示中所有与用户相关的功能。它包含突变和查询。
一些突变包括 createUser
、updateUser
、deleteUser
等。这些突变将不同的操作发送到我们的 Strapi 后端服务器以执行不同的用户相关操作。
此外,我们还有一个 queries.js
文件,它包含从我们的 Strapi 后端服务器检索不同类型用户相关信息的所有查询。
在此文件中找到的一些查询包括 getUser
、getUsers
等。
使用 GraphQL 实现登录示例
每个突变和查询中每个方法的相关代码片段可以在相应章节的官方 GitHub 仓库中找到。
然而,以下是如何使用我们定义在 auth
文件夹内的 GraphQL 突变登录我们应用程序的示例。
首先,我们导入任何组件或页面中想要使用的相应突变,如下所示:
```js
import { LOGIN_USER } from '../../graphql';
```js
Because of the `index.js` file inside the `graphql` folder, the folder location is shortened a little; you can reduce it more depending on your use case.
This is why it is important to always add an export inside the `index.js` file for any GraphQL mutation or query created.
The following steps show you how to implement logging authentication with GraphQL in our Pinterest clone application example.
**Step 1**
When a user tries to log in, we execute the `loginUser` function to retrieve some user-specific data:
const login = () => {
const user = loginUser();
if (user) {
// 保存状态并重定向到仪表板
logged.value = true;
}
};
**Step 2**
The `loginUser` function executes the `LOGIN_USER` mutation we imported using the `useMutation` hook imported from the Apollo Composable library as follows:
import { useMutation } from '@vue/apollo-composable';
const { mutate: loginUser } = useMutation(LOGIN_USER, () => ({
变量:{
邮箱:邮箱,
密码:密码,
remember,
},
}));
**Code walkthrough**
If you haven’t used GraphQL with Vue.js before, here is a quick walkthrough.
`useMutation` executes any mutation using the information passed in the `variables` object:
变量:{
邮箱:邮箱,
密码:密码,
remember,
},
Every `useMutation` execution returns the `mutate` function, which we rename to the name of the executed mutation called when our users try to log in. The `loginUser` function executes the `LOGIN_USER` mutation and returns the data.
This example demonstrates how we execute a single mutation; we will use this approach throughout the project to execute all the mutations.
### Implementing queries with a Photo example
Next, we are going to learn how to implement a query operation, and we will look at how to handle GraphQL queries in Vue 3\. To do this, follow these steps:
1. First, let’s define the `GET_PINS` query inside the `/graphql/photos/queries.js` file to retrieve all the pins for a particular board:
```
export const GET_PINS = gql`
mutation getPins($size: Int, $skip: Int, $filters:
PinFiltersInput) {
getPins(size: $size, skip: $skip,
filters: $filters) {
id
标题
url
}
}
`;
```js
2. Next, we will use the `useQuery` hook to execute this GraphQL query and return the data to a variable. As usual, we imported the `GET_PINS` query and the `useQuery` hook from their respective locations:
```
<script setup>
import { useQuery } from '@vue/apollo-composable';
import { GET_PINS } from '../../graphql/photos/queries';
```js
3. Next, we created a user-facing function called `getBoardPins`, which executes our query to retrieve and return the respective queries:
```
const getBoardPins = () => {
return getPins();
};
```js
4. Lastly, the `getPins` function executes the GraphQL query with the required variables and returns the result, as shown in the following snippet:
```
const { query: getPins } = useQuery(GET_PINS, () => ({
variables: {
size: 20,
skip: 0,
filters: {
boardId: board.id,
},
},
}));
```
这是我们如何在 Pinterest 示例应用程序的代码库中实现 GraphQL 查询的典型示例。
如果您跟随着每一章的内容,您将已经从上一节提供的 URL 中克隆了仓库,并在本地设置了它,并将一些数据种入 Strapi 后端服务器。
您应该会看到一个类似 Pinterest 的演示应用程序,如下所示:

图 6.13 – 最终 Pinterest 预览
重要提示
图像可能会根据您种入 Strapi 后端数据库中的数据而有所不同。然而,仓库中包含了获取虚拟数据并将其种入您的 Strapi 后端的方法说明。
您可以在官方 GitHub 仓库中查看完整的实现[方案:https://github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-6](https://github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-6)。此外,您还可以在此仓库中学习如何使用仓库模式通过 RESTful API 实现相同的 API 模式:[`medium.com/backenders-club/consuming-apis-using-the-repository-pattern-in-vue-js-e64671b27b09`](https://medium.com/backenders-club/consuming-apis-using-the-repository-pattern-in-vue-js-e64671b27b09)。
总之,我们使用 Vue 3 和 Composition API 创建了 Pinterest 克隆的前端;我们还使用一个非常流行的无头 CMS(Strapi)来存储我们的数据,并使用它构建了后端。最后,我们使用 GraphQL 将其集成到一个单一的企业应用程序中。
# 摘要
本章深入探讨了如何利用 GraphQL 提供可扩展和高性能的应用程序,以及如何使用 Vue 3 和 GraphQL 构建一个完整的 Pinterest 克隆。此外,我们还利用 GraphQL 的知识开发并交付了如 Pinterest 这样的企业应用程序。
我们探讨了 Strapi – 管理我们后端 API 和数据的无头 CMS,我们还搭建了一个新的 Strapi 项目,学习了如何创建 Strapi 集合,并也种入了一些虚拟数据以简化开发过程。
我们还详细介绍了如何集成 Strapi CMS,并使用 GraphQL 在 Vue 3 中轻松启动我们的 Pinterest 示例应用程序的后端服务器。
在下一章中,您将学习如何将您的 Vue 3 项目 docker 化的细节。此外,您还将了解 docker 化和部署企业级 Vue.js 3 网络应用程序的最佳实践和行业标准。
本章还将通过将全栈 Web 应用 Docker 化并在使用 Docker Compose 的云平台上部署容器来更加实用。最后,你将学习如何使用 Docker Compose 处理更大的项目。
# 第三部分:Vue.js 3 企业工具
在本部分,你将学习 DevOps 和 Docker。你将使用 CLI 工具将你的 Web 应用容器化,并将容器部署到 Google Cloud Run。然后,你将利用高级 CI 技术构建基于容器的 CI 环境,利用多阶段 Dockerfile。
本部分还将探讨 GraphQL 以及它是如何实现并与 Vue.js 3 集成,以提供企业级 Web 应用。此外,我们将构建一个企业级 Pinterest 克隆来展示我们在企业级 GraphQL 知识方面的理解。
本部分包括以下章节:
+ *第五章*, *《GraphQL、查询、突变和 RESTful API 入门》*([`epic.packtpub.services/index.php?module=oss_Chapters&action=DetailView&record=77e2136f-e753-5cdf-9030-61d6c9075a4b`](https://epic.packtpub.services/index.php?module=oss_Chapters&action=DetailView&record=77e2136f-e753-5cdf-9030-61d6c9075a4b))
+ *第六章*, *使用 GraphQL 构建完整的 Pinterest 克隆*
+ *第七章*, *将 Vue 3 应用 Docker 化*
# 第七章:将 Vue 3 应用程序 Docker 化
在上一章中,我们展示了如何使用 Vue.js 3、GraphQL 和 Strapi 后端构建一个完整的 Pinterest 克隆。你还利用了你对 GraphQL 的知识来开发一个企业级 Pinterest 克隆应用程序。在本章中,你将学习 Docker 化你的 Vue.js 3 项目所涉及的步骤的细节。此外,你还将了解 Docker 化和部署企业级 Vue.js 3 网络应用程序的最佳实践和行业标准。本章还将通过介绍如何 Docker 化全栈网络应用程序并将其部署到云平台(使用 Docker Compose)采取更实际的方法。
在本章中,我们将涵盖以下关键主题:
+ Docker 概述
+ Docker 化应用程序
+ 在 Docker 上运行应用程序
+ 使用 Docker Compose Docker 化 Vue.js 3 和 Node.js
+ 在 Docker Compose 上运行应用程序
到本章结束时,你将了解将企业级 Vue.js 3 网络应用程序 Docker 化和部署的最佳实践和行业标准。你还将通过将全栈网络应用程序 Docker 化并将其部署到云平台(使用 Docker Compose)获得实践经验。
# 技术要求
要开始本章,我建议你首先阅读 *第六章**,使用 GraphQL 构建 Pinterest 完整克隆*,在那里我们使用 Vue.js 3、GraphQL 和 Strapi CRM 后端构建了一个完整的 Pinterest 克隆。我们将在本章中大量使用该应用程序来学习 Docker 和 Docker Compose。
本章的所有代码文件可以在 [`github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-7`](https://github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-7) 找到。
# Docker 概述
Docker 几年来一直在发展,了解如何使用它已经成为对 DevOps 感兴趣的任何人最关键和最受欢迎的技能之一。因此,无论你是经验丰富的 DevOps 工程师还是初学者,你绝对需要将 Docker 添加到你的技能集合中。
Docker 是 DevOps 和容器编排行业的新热门词汇。它于 2013 年创建,并由其母公司 Docker, Inc. 开发。
Docker 可以将应用程序及其依赖项打包在一个虚拟容器中,该容器可以在任何 Linux、Windows 或 macOS 计算机上运行。容器是指一个隔离或捆绑的应用程序,其中包含执行应用程序所需的工具、库和配置文件。
Docker 的一个好处是它是一个工具包,使开发者能够通过单个 API 在不同的操作系统和平台上使用简单的命令和工作节省的自动化来构建、部署、运行、更新和停止容器。
本章探讨了您需要了解的所有关于 Docker 的知识,Docker 的不同独特功能,以及为什么您应该考虑将应用程序 docker 化。我们还将通过创建和设置您的第一个 Docker 应用程序来操作。
在下一节中,我们将探讨 Docker 及其优势,以了解为什么我们需要在开发流程中使用它。
## 什么是 Docker?
Docker 是一个开源平台,允许开发者快速构建、测试和部署应用程序。Docker 通过将您的应用程序打包成标准化的单元(容器)来实现这一点。这些容器包含软件运行所需的一切,包括库、系统工具、代码和运行时环境。它还虚拟化了安装和运行的计算机的操作系统。
为了进一步解释这一点,让我们假设我们已开发了两个不同的应用程序实例,即前端和后端。
后端使用 Node.js 堆栈开发,包括 PostgreSQL 数据库和其他使 Node.js 后端在本地服务器上正确执行的工具。
接下来,您的前端使用 Vue.js 3 以及使 Vue.js 3 应用程序顺利运行的必要工具和配置创建。
如果您在团队中或个人工作时不使用 Docker,可能会出现以下问题:
+ 如果新团队成员加入,由于成员需要安装和配置项目的正确版本以及下载所需文件的精确版本,因此将成员引入代码库可能会很繁琐。
+ 在部署应用程序时,为应用程序使用的所有服务配置不同的服务器将是一项大量工作。例如,您将不得不为数据库、前端和后端配置不同的服务器。您可能还需要为不同的环境(如测试、测试和生产)配置不同的服务器,或者每次使用一个配置繁多的服务器。
使用 Docker,您可以通过配置、提供和打包所有这些服务,使用一个简单的配置文件(Dockerfile 或 YAML 文件)来定义和运行多容器 Docker 应用程序,从而解决这些问题。
要了解 Docker 如何解决这些问题,请查看以下截图:
![图 7.1 – Docker 主机和层的截图(来源:freeCodeCamp [freecodecamp.org/news/docker-simplified-96639a35ff36/])](https://github.com/OpenDocCN/freelearn-fe-framework-zh/raw/master/docs/arch-vue3-entp-rdy-webapp/img/Figure_7.01_B17237.jpg)
图 7.1 – Docker 主机和层的截图(来源:freeCodeCamp [freecodecamp.org/news/docker-simplified-96639a35ff36/])
前面的截图显示了 Docker 的内部层和结构,其中您的应用程序被打包进容器中,并包含所有运行所需的资源,以确保顺利运行。此外,每个容器都使用您共享的系统资源。
这使得每个容器都可以与同一主机上存在的其他容器隔离。因此,只要它们具有相同的操作系统要求,就可以在同一主机上运行具有不同应用需求和依赖关系的多个容器。
因此,使用 Docker,您可以以容器形式运行多个应用程序,并使用命令和单个配置文件来控制一切。在下一节中,我们将仔细检查 Docker 对我们开发流程的好处。
### Docker 的好处
使用 Docker 的一些关键好处如下:
+ **优化的存储系统**:容器通常只有几兆字节大小,消耗的磁盘空间非常少。因此,可以在同一主机上托管大量应用程序。
+ **经济高效**:在运行 Docker 所需的硬件方面,Docker 的要求较低。因此,它大大降低了获取不同配置的昂贵硬件的成本。
+ **健壮性**:与虚拟机相比,Docker 由于没有安装操作系统,因此消耗的内存非常少,具有更快的启动时间。
+ **多个容器**:Docker 支持具有相同操作要求的不同应用程序,这些应用程序具有不同的应用需求和依赖关系,可以在同一主机上共同托管。
这些是使用 Docker 管理和运输企业应用程序的好处。接下来,我们将探讨为什么你应该在企业级应用程序中使用 Docker。
### 为什么使用 Docker
Docker 使您能够快速高效地发布代码。它标准化了应用程序的操作,允许您无缝地移动代码,并通过提高资源利用率来节省收入。
在可扩展的企业应用程序中开始使用 Docker 的一些原因如下:
+ **快速运输更多软件**:根据亚马逊([`aws.amazon.com/docker/`](https://aws.amazon.com/docker/))的数据,Docker 用户的产品运输速度是非 Docker 用户的 7 倍。Docker 使您能够根据需要频繁地运输隔离的服务。在构建企业级和可扩展的应用程序时,功能和错误修复可以在数小时甚至数分钟内完成。因此,紧急构建、测试和部署是必需的,而 Docker 在这一领域非常有用。
+ **标准化操作**:Docker 遵循行业标准的应用程序开发实践。被称为容器的隔离标准化单元使得部署、识别问题以及进行修复变得容易。
+ **无缝迁移**:开发者可以在不同的环境和系统之间迁移应用程序,无需担心安装任何库或缺少配置文件。基于 Docker 的应用程序可以无缝地从本地开发迁移到生产环境。
+ **节省资金**:基于 Docker 的应用程序具有成本效益,因为您可以在一个服务器上以容器形式运行多个应用程序。Docker 容器使您在每个服务器上运行更多代码变得更容易,从而提高 CPU 资源的利用率并节省资金。
现在您知道了为什么应该在您的企业应用程序中使用 Docker 以及您可以从使用 Docker 中获得的好处。有了 Docker,您可以更快、更有效地交付产品。当与其他行业标准工具结合使用时,您可以通过采用自动化部署而不是手动部署来完全消除手动部署的麻烦。在下一节中,我们将探讨如何 Docker 化您的第一个应用程序。
# 使用 Vue.js 3 实现 Docker
Docker 是一个企业级容器平台,它使组织能够无缝地构建、共享和运行任何应用程序,无论在何处。几乎所有的企业级公司都将他们的应用程序容器化,以便更快地处理生产负载,这样他们就可以随时部署,有时一天内可以部署几次。
构建企业级应用程序的一种方法是从一开始就 Docker 化项目。因此,我们将使用我们在 *第六章**,使用 GraphQL 构建完整的 Pinterest 模拟* 中开发的 Strapi 后端来 Docker 化 Pinterest Vue.js 3 应用程序,并创建一个 Docker 镜像,以便我们可以在任何时间或有时一天内多次部署该镜像。
## 前提条件
最重要的是,您必须在本地开发系统中下载并安装 Docker 以进行本地测试。您可以通过此链接在不同操作系统上下载和安装它:[`docs.docker.com/install/`](https://docs.docker.com/install/).
## 示例项目
在 *第六章**,使用 GraphQL 构建完整的 Pinterest 模拟* 中,我们使用 Vue.js 3 和 Strapi 作为后端开发了一个 Pinterest 模拟。在本节中,我们将学习如何从头开始 Docker 化项目。以下是应用程序的演示:

图 7.2 – Pinterest 模拟演示的截图
该应用程序根据我们存储在 Strapi 数据库中的图片数量,以马赛克网格布局显示图片。
Strapi 后端允许您管理并控制应用程序的整个后端,从添加图钉和看板到创建新用户。
我们将使用单独的 Dockerfile 和多阶段构建来 Docker 化 Vue.js 3 Pinterest 应用程序和 Strapi 后端,以创建高效的 Docker 镜像。
### Docker 化 Pinterest 应用
我们将首先将 Pinterest Vue.js 3 应用程序进行 Docker 化。在这个多阶段构建中,构建 Vue.js 3 项目并将那些静态资产放入 `dist` 文件夹是第一步。因此,让我们创建一个 Dockerfile 并配置我们的 Vue.js 3 应用程序。
在您的 Pinterest 克隆 Vue.js 3 应用程序的根目录中创建一个 Dockerfile。如果从 `第六章` 仓库 ([`github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-6`](https://github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-6)) 克隆时 Strapi 后端仍然位于前端文件夹中,您可以在前端文件夹旁边创建一个父文件夹,并将 Strapi 后端文件夹移动到那里,如下面的截图所示:

图 7.3 – 当前文件夹结构的截图
此外,您还可以从以下链接克隆完成的 `第七章` ([`github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-7`](https://github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-7)) 仓库,其中包含完整的代码库。
最后,让我们探索配置文件以将 Vue.js 3 企业应用程序 docker 化。打开您的 Dockerfile 并添加以下代码:
```js
# Use the official Node.js 14 Alpine image from https://hub.docker.com/_/node.
# Using an image with specific version tags allows deterministic builds.
FROM node:16.17.0-alpine
# Create and change to the app directory.
WORKDIR /usr/src/frontend
# Copy important root files to the builder image.
COPY package*.json ./
# Install production dependencies.
RUN npm install
# Copy the Vue 3 source to the container image.
COPY . .
# Expose container port
EXPOSE 3000
# Run the Vue service on container startup.
CMD ["npm", "run", "dev"]
代码片段自解释,注释解释了我们使用 Dockerfile 中的每个命令。
使用以下命令构建镜像:
// build the image
docker build -t pinterest-vue-frontend .
// check the images
docker images
In summary, we have successfully dockerized our Vue.js 3 Pinterest application. In the next section, we will dockerize the Strapi backend application separately.
### Dockerizing the Strapi backend app
In this section, we will follow the same approach used in dockerizing the Vue.js 3 frontend project to create a Docker instance for the Strapi backend.
Therefore, create a Dockerfile inside the Strapi backend folder of your project and add the following configuration code:
使用来自 https://hub.docker.com/_/node 的官方 Node.js 14 Alpine 镜像。
使用具有特定版本标签的镜像允许确定性构建。
FROM node:14.16.1 AS builder
创建并切换到应用程序目录。
WORKDIR /usr/src/backend
将重要的根文件复制到构建镜像中。
COPY package*.json ./
安装生产依赖项。
RUN npm install
将后端源代码复制到容器镜像中。
COPY . .
build app for production with minification
RUN npm run build
EXPOSE 1337
初始化最终镜像生成。
FROM node:14.16.1
在容器启动时运行 Strapi 服务。
CMD ["npm", "start"]
We copied the previous configuration and changed the building process for the backend app. The code snippet is self-explanatory with comments explaining every command we used in the Dockerfile. In the next section, we are going to run the applications on Docker and test them separately.
### Running the images on Docker
After building the Docker image, next, we need to run the image on Docker using the following command:
// run the Frontend image
docker run -d -p 3000:3001 --name pinterest-frontend pinterest-vue-frontend
// run the Strapi Backend Image
docker run -d -p 1337:3002 --name pinterest-backend pinterest-strapi-backend
// check the container
docker ps
The `ps` command checks the container for the list of images currently running in your Docker engine. You should see two images with the names specified in the preceding Docker `run` command.
If the `run` command is successful, you can access the frontend application on the web at the address `http://localhost:3001` and the backend instance at port `3002`. This port change is possible because the `-p` option exposes our internal frontend Vue.js 3 server port `3000` to the external port `3001`, which makes it possible to access our internal Docker application in our browser.
At this point, if everything is successful, you should be greeted with your demo Vue.js 3 application. However, following this approach poses a problem. Developers need to build, test, and deploy applications in isolation, which can be avoided with Docker Compose.
In this section, we explored how to dockerize the Pinterest clone application we have developed in this book. We learned how to create, build, and run the Dockerfile we used in dockerizing the project using different Docker commands. In the next section, we will explore how to use Docker Compose to build, test, and deploy multiple applications at once.
# Dockerizing Vue.js and Node.js with Docker Compose
In the previous section, we explored how to dockerize Vue.js 3 applications and how to dockerize a Node.js application using Strapi, which was done separately. In this section, we are going to explore how to build, test, and deploy bundled applications. Furthermore, we are going to build and dockerize both applications as a single unit.
## Overview of Docker Compose
Docker Compose is a tool designed to enable users to easily define and share multi-container applications. By creating a YAML file, Compose allows us to quickly launch or shut down all services with a single command.
With Docker Compose, developers can build, test, and deploy multiple containers and images bundled together to form a single application.
In the next section, we are going to explore how to bundle the frontend and backend applications that we demonstrated in the previous section.
### Dockerizing the Pinterest clone app
To bundle a deployable application, we are going to start by creating a central Dockerfile and Docker Compose YAML file inside the root directory that contains the different configurations to bundle our application with Docker Compose.
Before you start, rename your `strapi-pinterest-api` folder to `backend`. The following screenshot shows the current folder structure:

Figure 7.4 – A screenshot of the current folder structure with Docker and Docker Compose files
Next, create a Dockerfile inside the root directory and add the following script:
FROM node:14.15.0
ARG PACKAGE_PATH=
ARG WORKING_DIR=
WORKDIR ${WORKING_DIR}
COPY ${PACKAGE_PATH}/package*.json ${WORKING_DIR}
RUN npm install --silent
COPY ${PACKAGE_PATH} ${WORKING_DIR}
VOLUME $WORKING_DIR/node_modules
CMD [ "npm", "start" ]
#### Code walk-through
Let’s walk through the code together and understand the nitty-gritty of it.
**Step 1:** **Import Node.js**
The first step in every Dockerfile is to specify the build image. In this case, we specify Node.js as our image.
This will install Node.js with the specified version number and set up the environment to run Node.js properly.
**Step 2: Create the** **required arguments**
The second step is to create the arguments required by Docker Compose when building individual images of our frontend and backend applications:
ARG PACKAGE_PATH=
ARG WORKING_DIR=
WORKDIR ${WORKING_DIR}
Additionally, we create a working directory specifying the argument we created earlier. This will auto-inject the specified working directory in the Docker Compose YAML file.
**Step 3: Copy, install, and** **run commands**
Lastly, we copy files from the specified working directory into the Docker virtual working directory. We start by copying `package*.json` files, running the `install` command, and copying the remaining files later. This approach utilizes the Docker caching system:
COPY ${PACKAGE_PATH}/package*.json ${WORKING_DIR}
RUN npm install --silent
COPY ${PACKAGE_PATH} ${WORKING_DIR}
VOLUME $WORKING_DIR/node_modules
CMD [ "npm", "start" ]
Furthermore, after mounting the `node_modules` folder of the specified working directory, Docker will execute the `npm start` command to start the application.
Now that we have created a central Dockerfile for both the frontend and backend, let’s continue by creating a YAML Docker Compose file to bundle our separate applications together.
Create a `docker-compose.yaml` file inside the root directory and add the following script:
版本: "3.5"
- 服务:
api:
build:
context: .
dockerfile: Dockerfile
args:
PACKAGE_PATH: backend
WORKING_DIR: /usr/src/
expose:
- 1337
端口:
- 1337:1337
environment:
-
NODE_ENV=development
-
HOST=0.0.0.0
-
PORT=1337
-
BASE_URL=http://api:1337
env_file:
- ./.env
卷:
- ./backend:/usr/src
command: >
sh -c "npm install"
frontend:
build:
context: .
dockerfile: Dockerfile
args:
PACKAGE_PATH: frontend
WORKING_DIR: /usr/src/
expose:
- 3000
端口:
- 3000:3000
环境:
-
APP_ENV=production
-
APP_BACKEND=http://0.0.0.0:1337/api
-
NODE_PATH=/usr/src/
-
APP_TOKEN=eyJhbGciOiJIUzI1NiJ9.c29sb[STRAPI_TOKEN]
env_file:
- ./common.env
volumes:
- ./frontend:/usr/src
depends_on:
- api
command: ["npm", "start"]
#### Code walk-through
Let’s walk through the code together and understand the nitty-gritty of it.
**Step 1: Versioning** **and services**
Every Docker Compose file always starts with a version number of the version of Docker Compose you intend to use when building and bundling the application. In this demo, we specify version 3.5.
Furthermore, every Docker Compose YAML is always split into different services. You can add as many services as required that each application depends on. For instance, if the backend of your project depends on a database (PostgreSQL), you can specify that as a service.
In this demo, we have specified only two services, namely the following:
* Backend
* Frontend
Each of the services contains configurations that enable them to run smoothly. Let’s explore the configurations we have added to the frontend service.
**Step 2: The** **build section**
The `build` section includes configurations that help in building the application. It contains commands such as the context, working directory, and defined arguments:
build:
context: .
dockerfile: Dockerfile
args:
PACKAGE_PATH: frontend
WORKING_DIR: /usr/src/
The `context` command specifies the part of the directory where the Dockerfile we created earlier is stored. In our case, it was stored in the root directory.
Next, we call the Dockerfile with the `dockerfile` command and specify the required parameters with the `args` command. Lastly, we specify the `PACKAGE_PATH` and `WORKING_DIR` values.
**Step 3: Exposing** **the port**
In this step, we exposed the internal Docker port used to run the application to the outside world:
expose:
- 3000
ports:
- 3000:3000
**Step 4: Creating** **environment variables**
In this step, we use the `environment` command to add the required environment variables, such as `APP_BACKEND` and `APP_ENV`:
environment:
-
APP_ENV=production
-
APP_BACKEND=http://0.0.0.0:1337/api
-
NODE_PATH=/usr/src/
-
APP_TOKEN=eyJhbGciOiJIUzI1NiJ9.c29sb[STRAPI_TOKEN]
env_file:
- ./.env
Then, we create an `env` file in the root directory using the `env_file` command to store the details we specified previously.
**Step 5: Running** **the app**
Lastly, we mount the `frontend` directory and specify that the frontend project depends on our backend API service, which is our Strapi backend. This allows Docker to execute the project in sequence from the backend first before the frontend:
volumes:
- ./frontend:/usr/src
depends_on:
- api
command: ["npm", "start"]
最后,我们调用命令来执行项目。同样的方法也适用于后端服务。在下一节中,我们将学习如何使用 Docker Compose 运行项目。
## 在 Docker Compose 上运行应用程序
在成功创建 Docker Compose YAML 配置文件后,让我们使用 Docker Compose 运行我们的 Pinterest 模拟项目。
在开始运行项目之前,请确保设置并安装 Docker 和 Docker Compose。接下来,在终端根目录中输入 `docker compose up` 以部署项目。或者,直接输入 `docker-compose up`。应用程序将在 `http://localhost:3000/` 上提供服务。
如果一切配置正确,你应该会看到一个全栈 Pinterest 应用程序,如下面的截图所示:

图 7.5 – Pinterest 应用程序演示预览
如果你对任何内容不确定,请参考本章代码库([`github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-7`](https://github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-7))以查看工作状态和完整的项目设置。
在本节中,我们探讨了如何使用 Docker Compose 创建和管理许多服务,这些服务是我们上一节中创建的 Dockerfile。我们还学习了如何使用 Docker Compose 打包我们的全栈应用程序,包括前端、后端和数据库。
# 摘要
本章深入探讨了将 Vue.js 3 项目 docker 化所涉及的繁琐步骤。此外,我们还探讨了 docker 化和部署企业级 Vue.js 3 网络应用程序的最佳实践和行业标准。我们还学习了如何使用 Docker Compose docker 化一个全栈网络应用程序。
通过 Dockerfile,我们能够将我们的 Pinterest 模拟演示应用程序 docker 化,使其可以轻松地由其他团队成员或任何云服务提供商部署和管理。此外,我们还学习了如何使用 Docker Compose 打包和管理一个包含后端、前端、数据库服务以及更多功能的全栈应用程序,所有这些都在一个文件中。
在下一章中,你将探索测试的概念。你将学习从一系列可用的组件和方法中测试什么。此外,你还将了解与测试库相关的最佳实践和行业标准,以及如何将它们与 Vue.js 3 集成。
# 第四部分:测试企业级 Vue.js 3 应用
测试企业级项目可能会令人感到 daunting 和不必要地复杂。这部分将探讨与企业测试相关的一切,以及精确测试哪些内容以消除在测试错误代码上浪费的时间。
本部分包括以下章节:
+ *第八章*, [*在 Vue.js 3 中测试和要测试的内容*](https://epic.packtpub.services/index.php?module=oss_Chapters&action=DetailView&record=b958045c-cbca-b3c1-9404-61d6c95961a4)
+ *第九章*, [*单元测试的最佳实践*](https://epic.packtpub.services/index.php?module=oss_Chapters&action=DetailView&record=b5bd1ffc-0280-c217-aa63-61d6c9ee0198)
+ *第十章*, [*Vue.js 3 中的集成测试*](https://epic.packtpub.services/index.php?module=oss_Chapters&action=DetailView&record=907b43f6-1d49-47b3-a4ac-63835de51e8f)
+ *第十一章*, *行业标准的端到端测试*
# 第八章:Vue.js 3 中的测试及其测试内容
在上一章中,你学习了将你的 Vue.js 3 项目 docker 化的详细步骤。此外,你还学习了将企业级 Vue.js 3 网络应用程序 docker 化的最佳实践和行业标准。
在本章中,你将探索软件测试的概念。你将学习从一系列可用的组件和方法中要测试什么。此外,你还将了解与测试库相关的最佳实践和行业标准,以及如何将它们与 Vue.js 3 集成。
在本章中,我们将涵盖以下关键主题:
+ 测试概述
+ 软件工程中的测试
+ 要测试什么
+ 测试基本的 Vue.js 3 应用程序
+ Vue.js 3 的组件测试
# 技术要求
要开始本章的学习,我建议你首先阅读 *第七章*,*Dockerizing a Vue 3 App*,在那里我们通过使用 Docker Compose docker 化了一个全栈网络应用程序,采取了一种更实际的方法。在本章中,我们将大量使用该应用程序来学习 Vue.js 3 企业级测试。
本章的所有代码文件可以在 [`github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-8`](https://github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-8) 找到。
# 测试概述
任何学习过计算机科学的人都应该熟悉 SDLC 的概念。如果你不知道,**SDLC** 代表 **软件开发生命周期**。
Synopsys ([`www.synopsys.com/glossary/what-is-sdlc.html`](https://www.synopsys.com/glossary/what-is-sdlc.html)) 提供以下定义:
软件开发生命周期(SDLC)是一个结构化的过程,它能够在尽可能短的生产时间内生产出高质量、低成本软件。SDLC 的目标是生产出满足并超越所有客户期望和需求的优秀软件。
如果你进一步探索 SDLC,你会发现它定义并概述了八个详细计划,包括阶段或阶段,这些阶段或阶段是质量和企业级软件必须通过的,以生产出满足并超越所有客户期望和需求的软件。
每个阶段都至关重要,包括规划、编码、构建和测试。然而,测试阶段尤为重要,尤其是在你需要构建一个无错误或缺陷的企业级应用程序时。
为了进一步阐述,测试阶段评估创建的软件与任何错误、任何潜在错误以及从规划阶段开始的软件需求。
接下来,我们将了解我们所说的软件测试是什么意思。
## 什么是软件测试?
软件测试是检查生产中的软件是否符合预期需求的方法,最重要的是,它是否无缺陷。执行软件测试所使用的方法因组织而异。然而,方法被分为手动和自动化过程。
个人和组织对软件测试有不同的称呼。它也可以被归类为白盒测试或黑盒测试。然而,任何方法的最终结果总是相同的,即识别与实际需求相比的错误、差距或缺失的需求。
黑盒测试涉及在不了解系统内部工作原理的情况下测试系统,而白盒测试是一种允许测试人员检查和验证系统内部工作原理的测试方法。
无论在提及软件测试时使用什么名称、术语或类别,简单来说,软件测试就是验证**被测试的应用程序**(**AUT**),这是生产高质量企业级软件的关键和关键阶段。
在下一节中,我们将讨论软件测试的重要性。
## 为什么软件测试很重要
将软件测试纳入您的应用程序开发流程中的必要性不容忽视。它和 SDLC(软件开发生命周期)中的规划和开发阶段一样重要。实际上,如果没有适当的软件测试策略,开发中的软件最终产品很可能充满缺陷、错误和未满足的软件需求。
软件测试之所以重要,是因为软件缺陷可能代价高昂,对企业和整个组织来说也非常危险。多年来,有许多潜在的软件缺陷和货币损失的事例。
例如,2015 年 4 月,伦敦的彭博终端因软件故障而崩溃,影响了超过 30 万金融市场交易员。据《卫报》([`www.theguardian.com/business/2015/apr/17/uk-halts-bond-sale-bloomberg-terminals-crash-worldwide`](https://www.theguardian.com/business/2015/apr/17/uk-halts-bond-sale-bloomberg-terminals-crash-worldwide))报道,这迫使英国政府推迟了 30 亿英镑的债务销售。
此外,根据 Windows 报告([`windowsreport.com/windows-10-vulnerability/`](https://windowsreport.com/windows-10-vulnerability/)),Windows 10 存在一个漏洞,允许用户通过 win32k 系统中的漏洞逃离安全沙箱。
已有许多关于不同恶意攻击商业活动导致收入或货币价值受损的漏洞报告,其中一些可以通过适当的软件测试避免。这表明软件测试是 SDLC(软件开发生命周期)中非常重要的一个阶段。
虽然软件测试需要资金,但我相信您会同意我的观点,与如果没有良好的测试技术和 QA 流程,开发和支持公司每年要花费的数百万美元相比,这个成本微不足道。
此外,在产品上市之前进行早期软件测试可以揭示问题。早期测试还可以揭示不同的缺陷,包括但不限于以下内容:
+ 架构缺陷
+ 设计决策不当
+ 无效或不正确的功能
+ 安全漏洞
+ 可扩展性问题
在软件开发流程中实施适当的测试流程可以提高软件可靠性,意味着高质量的应用程序在错误很少的情况下交付。在下一节中,我们将进一步探讨软件测试的好处。
## 软件测试的好处
在上一节中,我们解释了为什么企业应用需要在他们的开发流程中包含测试。在本节中,我们将探讨拥有适当的软件测试流程的好处。我们将详细讨论以下点:
+ 帮助节省资金
+ 满足客户需求
+ 增强开发流程
+ 产品质量
+ 安全性
### 帮助节省资金
将有缺陷的软件推向市场可能比创建整个软件更昂贵。如前所述,由于软件缺陷和易出错的软件,许多公司的货币价值已经减少。
如果在开发流程中构建适当的软件测试过程来检测和纠正这些错误,那么这个问题可以在一定程度上得到缓解。
### 满足客户需求
为了让您的企业应用用户满意,软件必须正常工作,并符合要求。
因此,在将软件推向市场之前,必须进行验收测试,以确保产品符合要求,并了解用户将如何日常访问和使用产品。
在测试阶段,如果发现任何问题或错误,测试中的软件可以轻松地退回到开发阶段,而不是在生产阶段发现问题,那时真实用户正在与应用程序交互。
### 增强开发流程
将软件测试纳入开发流程可以创建一个增强的开发流程。此外,将软件测试纳入流程是行业标准做法。另外,开发者修复开发阶段的错误比在生产阶段修复要简单。
因此,将软件测试过程纳入开发流程可以降低将存在缺陷的软件推向市场的风险,并增强开发流程。
### 产品质量
当产品质量下降时,公司可能会失去客户,从而导致收入损失。然而,低质量软件的一个属性是未经测试且易于出现错误的软件应用程序。
此外,如果将适当的软件测试流程集成到开发管道中,大多数错误、缺陷和缺陷将在生产前被发现和修复,从而生产出高质量的软件。
### 安全性
根据 OWASP([`owasp.org/www-project-top-ten/`](https://owasp.org/www-project-top-ten/)),安全性应该是每个软件的组成部分。不考虑它可能会导致企业货币价值的降低。
软件测试是检测安全漏洞并在开发阶段修复它们的一种方法。如果一个产品经过测试,用户可以确信他们正在接收一个可靠的产品。他们可以确信他们的个人信息是安全的。借助软件测试,用户可以接收到更可能没有漏洞的产品。
在本小节中,我们介绍了一些您可以从将软件测试实施到构建企业级应用程序的管道中获得的益处。
在下一节中,我们将探讨不同的测试类型以及您可以集成到您的开发管道中的不同策略。
# 软件工程中的测试
如前所述,软件测试是 SDLC(软件开发生命周期)的一个组成部分,因此,根据 ANSI/IEEE 1059,软件工程中的测试是评估待测试软件以确定其是否满足要求,以及是否没有错误、缺陷和缺陷的方法。
该过程涉及评估待测试软件的功能,以检查任何缺失的需求、错误或错误、安全性、可靠性和性能。
在本节中,我们探讨了测试的好处以及为什么软件测试很重要,在下一节中,我们将了解不同类型的软件测试。我们将探讨要测试什么以及如何编写基本的单元和集成测试。
## 软件测试类型
软件测试有不同的名称。根据 Guru99,有超过 150 种软件测试类型([`www.guru99.com/types-of-software-testing.html`](https://www.guru99.com/types-of-software-testing.html))。
然而,我们将软件测试分为两大类,然后探索每一类及其中的不同类型。以下两大类是:
+ 功能性软件测试
+ 性能软件测试(非功能性)
下图显示了软件测试类型的高级分类:

图 8.1 – 软件测试的高级分类(来源:softwaretestinghelp)
您可以在 YouTube 上了解更多关于不同软件测试类别的信息。然而,我们将只关注以下三个主要的功能软件测试类别:
+ 单元测试
+ 集成测试
+ 端到端测试
### 单元测试
这种基本的软件测试方法由程序员执行,以测试程序的单位或最小部分。这有助于开发者了解单个代码单元是否正常工作。
### 集成测试
这种类型的测试侧重于软件的构建和设计。您需要查看集成单元是否在没有错误的情况下工作。
### 端到端测试
端到端测试是一种评估复杂产品从开始到结束的工作顺序的方法论。
在接下来的章节中,我们将专注于单独探索这些不同类型的测试。然而,您可以从 Guru99([`www.guru99.com/types-of-software-testing.html`](https://www.guru99.com/types-of-software-testing.html))探索超过 150 种不同的软件测试类型。
总结来说,既然我们知道软件测试的重要性以及不同的软件测试类型,我们如何知道在一个大型企业应用程序中要测试什么?在下一节中,我们将探讨要测试什么以及如何将测试管道集成到开发工作流程中。
# 要测试什么
在软件团队中,一个常见的问题是*我们应该测试什么,不应该测试什么?*在本节中,我们将探讨在考虑软件测试时,您应该测试和不应该测试的不同事项。
我们将首先探讨在将软件测试集成到您的开发工作流程时可以采用的不同测试策略。
## 测试策略
在您的企业应用程序中实施的最佳测试策略是正常(手动)测试和自动化测试的结合。此外,正常测试应由**质量保证**(**QA**)团队更广泛地进行。
为了进一步解释,当自动化测试被成功编写和实施时,我们通常编程它来查找基本错误和边缘情况,这些错误和边缘情况可能没有正确地吸收真实客户如何与应用程序交互。
## 您应该测试的内容
软件测试对于企业级应用程序的效率至关重要,了解要测试什么至关重要,这样开发者就不会浪费时间测试错误的事情。
以下是在测试您的企业项目以查找错误、缺陷和缺陷时可以查找的一些事项列表:
+ **传递的参数**:确保方法或函数没有改变而传递的参数或参数集合。在某些情况下,参数的数据类型保持不变。
+ **算法引擎**:每个方法都有一个目的,这个目的是通过逻辑或算法实现的。你的测试用例应该测试算法,以确保它是正确的,并且根据方法输入产生正确的输出。
+ **简单的数据库查询检查谓词**:如果你的工作作为开发者与查询和操作数据库相关,你真的需要测试你的数据库查询以确保它执行正确的操作和查询。
+ **实用方法**:实用方法是项目中的辅助工具,为特定任务创建。它们通常在你需要执行不需要类实例的任务时使用。这一组方法需要得到适当的测试,以确保在使用时产生正确的输出。
+ **测试不那么关键的代码**:测试一些你认为可能存在错误的异常复杂代码的边缘情况。此外,当有人有时间时,对不那么关键的代码进行边缘情况测试。
上述是一些你可以考虑用于测试用例的内容。然而,重要的是要注意,编写测试和拥有 100%的代码覆盖率并不一定意味着你的代码没有错误。在下一节中,我们将探讨在你的项目中不应该测试的内容。
## 你不应该测试的内容
以下是在你的项目中不应该测试的内容:
+ 构造函数或属性(如果它们只是返回变量)。只有当它们包含验证时才测试它们。
+ 调用另一个公共方法的函数。
+ 如果代码需要与其他已部署的系统交互,则应使用集成测试。
+ 如常量、只读字段、配置和枚举之类的配置。
+ 你不应该测试 POJO 类或模型;相反,你可以测试类中的每个方法。
总结来说,我们已经探讨了软件测试策略,测试什么,以及你不应该测试什么,以帮助你理解软件测试在你企业应用中的相关性。在下一节中,我们将探讨如何测试一个基本的 Vue.js 应用。
# 测试基本的 Vue.js 3 应用
在上一章中,我们使用 Strapi 作为后端和 Vue.js 3 作为前端创建了一个 Pinterest 应用程序。
之前,我们添加了国际化,结构化了项目,并构建了一个完整的 Pinterest 克隆。在本节中,我们将继续使用为本书创建的官方项目来设置软件测试,从而得到一个完整的、企业级就绪的 Pinterest 克隆应用程序。
你可以从这个仓库克隆项目,[`github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications`](https://github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications),直接开始。
## 创建测试文件夹
当涉及到创建企业应用程序时,知道在哪里添加你的测试文件和文件夹总是令人困惑。根据你在企业项目中使用的方法,有两种结构测试文件的方法。
### 方法 1 – 在每个组件内添加测试文件
首先,你可以在每个 `component` 文件夹内创建一个特定的测试文件。例如,在我们的 Vue.js 3 项目中,我们将在每个组件文件夹内创建一个文件夹,并将每个组件的文件移动到该文件夹中,包括每个组件的测试文件。
下图显示了我们可以如何安排我们的组件文件夹以容纳我们的测试文件和其他与特定组件相关的文件:

图 8.2 – 展示我们的组件结构的屏幕截图
在前面的图中,你可以看到如何添加与任何组件相关的任何文件。例如,如果你想为每个组件添加端到端测试或集成测试文件,你只需在每个特定的组件文件夹内创建该文件即可。
此外,由于我们的实践项目结构和原子模式的引入,我们可以轻松地看到每个组件将创建多少文件。同样,这也适用于项目中的不同测试区域。
然而,我们可以使用下一个方法来将所有与测试相关的内容安排在单独的文件夹中,并在特定的 `tests` 文件夹内创建所有文件和文件夹。
### 方法 2 – 创建测试文件夹
在本节中,我们将在 `src` 文件夹内创建一个名为 `tests` 的文件夹,该文件夹将包含所有与测试和测试配置相关的文件和文件夹。
下图显示了使用此方法实现测试的文件夹结构:

图 8.3 – 实现测试的文件夹结构屏幕截图
此方法有一个包含所有与测试相关的文件和文件夹(包括所有配置)的单个文件夹。它是所有软件测试文件和文件夹的中心位置和单一事实来源。你可以根据你的用例以不同的结构安排此文件夹。
此外,你的测试文件夹或与测试相关的文件和文件夹的结构方法并不重要。最重要的是正确实施软件测试,并确切知道要测试什么,以避免生产中的错误和故障。
在这个演示中,我们将使用第二种方法来编写一些基本的单元测试以进行演示。这是因为我们不希望测试文件分散在不同的文件夹中,因为我们正在使用原子模式。
## 编写基本的单元测试
首先,我们将开始安装 Vue.js 3 的新测试库。由于我们在项目中使用 Vite,我们还将安装 Vitest 库作为我们的测试运行器。你可以在 [`vitest.dev/guide/`](https://vitest.dev/guide/) 上了解更多关于新 Vue.js 3 测试库的信息。
### 安装测试库
Vitest 是由 Vite 驱动的闪电般的单元测试框架。使用以下任何命令安装库:
```js
```bash
# 使用 npm
npm install -D vitest
# 或者使用 yarn
yarn add -D vitest
# 或者使用 pnpm
pnpm add -D vitest
```js
现在我们已经设置了测试库,让我们创建一个简单的辅助文件来测试我们的配置。
创建辅助文件
在我们的演示中,我们将在 src/helpers
文件夹内创建一个辅助文件,并添加一个简单的增加值的函数。以下代码片段显示了添加到新创建的文件中的代码示例:
// src/helpers/index.js
export function increment(current, max = 10) {
if (current < max) {
return current + 1;
}
return current;
}
之前编写的增量函数通过增加 1
来增加一个值,直到达到最大值。如果达到最大值,它将简单地返回当前值。接下来,让我们为它编写一个基本的单元测试。
编写基本测试
在本节中,我们将为这个函数编写一个简单的单元测试。你可以在此基础上为你的企业项目中的所有函数和方法编写单元测试:
import { describe, it, expect } from 'vitest';
import { increment } from '../../helpers';
describe('increment', () => {
it('increments the current number by 1', () => {
expect(increment(0, 10)).toBe(1);
});
it('does not increment the current number over the max', () => {
expect(increment(10, 10)).toBe(10);
});
it('has a default max of 10', () => {
expect(increment(10)).toBe(10);
});
});
我们将在下一章深入探讨单元测试。
让我们一起来分析前面的代码,并理解它的细节:
- 步骤 1:添加 所需的包
首先,我们需要 vitest
包和 helper
文件来测试。接下来,我们使用导出的函数创建一个 describe
块,等等:
import { describe, it, expect } from 'vitest';
import { increment } from '../../helpers';
describe('increment', () => {
….
});
describe
块用于将相关的测试案例分组,如前一个代码片段所示。
- 步骤 2:使用 it 函数
接下来,我们使用 it
函数来测试我们函数的特定用例。例如,我们测试确保每次调用函数时数字实际上增加 1
:
it('increments the current number by 1', () => {
expect(increment(0, 10)).toBe(1);
});
- 步骤 3:使用 except 函数
最后,expect
函数用于测试用例。你传入一个值并期望该值等于另一个值,如示例所示。
除了 toBe()
函数外,你还可以从 expect
对象访问不同的方法(vitest.dev/api/#expect
)。
在下一节中,我们将介绍使用 Vitest 运行测试以及创建你的第一个组件/集成测试示例的过程。
使用 Vitest 运行测试
现在,我们将运行测试以查看它是否通过。在你的根终端中输入以下命令:
yarn test
如果你的测试成功,你应该会看到三个案例都通过了,如下面的图所示:
图 8.4 – 显示测试结果的截图
在本节中,我们展示了如何使用 Vue.js 3 的最新 Vitest 测试库配置和构建软件测试。我们还学习了如何编写基本的单元测试。在下一节中,我们将学习如何创建基于组件的基本测试。
Vue.js 3 中的组件测试
在 Vue.js 中,组件是 UI 的主要构建块,指的是应用中可共享、可测试和可重用的单个单元。因此,组件测试位于单元测试和端到端测试之间。它也可以被称为集成测试。
在之前的章节中,我们使用 Storybook 集成原子模式时,我们讨论了创建组件故事以及如何创建它们。如果您为所有组件创建了故事,并配置它们按演示正常工作,那么您已经使用 Storybook 故事实现了组件测试。
然而,第十章,“在 Vue.js 3 中集成测试”专门探讨了组件测试。尽管如此,我们将在本章中简要说明如何实现简单的基于组件的测试,以帮助我们理解下一章。
编写基本的组件测试
我们将首先安装 Vue.js 3 的新测试库。由于我们在项目中使用 Vite,我们还将安装 Vitest 库作为我们的测试运行器。您可以阅读更多关于新 Vue.js 3 测试库的信息(vuejs.org/guide/scaling-up/testing.html
)。
安装测试库
截至撰写本文时,@testing-library/vue
(github.com/testing-library/vue-testing-library
) 被推荐用于组件测试,我们将在示例中安装它。运行以下命令来安装它:
```bash
npm install -D vitest happy-dom @testing-library/vue
```js
接下来,打开 vite.config.js
文件并添加以下配置:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
test: {
environment: 'happy-dom',
globals: true,
},
});
此配置应允许我们同时运行单元测试和组件测试。接下来,让我们创建一个简单的组件测试。
创建基本的组件测试
这里有一个简单的组件测试代码片段,用于演示过程:
import { render } from '@testing-library/vue';
import Button from '../../components/atoms/Button.vue';
test('mounted a button with custom label', async () => {
// The render method returns a collection of utilities to
// query your component.
const { getByText } = render(Button, {
props: {
label: 'Test',
},
});
// getByText returns the first matching node for the
// provided text, and Check if button is render with Label
// from props
const button = getByText('Test');
});
测试简单地渲染带有自定义标签值的 Button
,并检查我们是否可以在渲染过程中检索到添加的自定义标签。
运行测试
运行测试将导致四个通过测试案例,包括我们之前创建的单元测试:
图 8.5 – 一个显示集成测试最终测试结果的截图
在本节中,我们展示了如何使用最新的 Vitest 测试库配置和构建 Vue.js 3 的组件测试,也称为集成测试。我们还编写了基本的组件测试,以帮助我们理解这个过程。在接下来的章节中,我们将深入探讨在构建 Vue.js 3 企业项目时可以执行的不同类型的测试。
您可以在此处克隆本章的最新仓库:github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-8
。
摘要
本章深入探讨了软件测试的概念,以交付可扩展、高性能且无错误的软件应用。我们还探讨了从一系列可用组件和方法中应该测试什么。此外,我们还利用我们对软件测试的知识,在 Vue.js 3 中使用 Vitest 创建基本的单元和组件测试用例。
在下一章中,我们将探讨与单元测试相关的所有内容。我们将学习如何对 Vue.js 3 组件及其页面方法进行单元测试。我们还将了解单元测试工具,如 Vitest,并使用它们有效地对企业项目进行单元测试。
第九章:单元测试的最佳实践
在上一章中,我们学习了软件测试的概念。我们学习了从一系列可用的组件和方法中测试什么。此外,我们还学习了最佳实践和行业标准测试库,以及如何将它们与 Vue.js 3 集成。
在本章中,我们将探讨与单元测试相关的所有内容。我们将学习如何对 Vue.js 3 组件和组件及页面方法进行单元测试。我们还将了解单元测试工具,如 Jest 和 Mocha,以及如何使用它们有效地对企业项目进行单元测试。
在本章中,我们将涵盖以下关键主题:
-
单元测试简介
-
什么是单元测试?
-
单元测试的重要性
-
单元测试的好处
-
单元测试的最佳实践
-
JavaScript 单元测试
-
测试基本的 Vue.js 应用程序
技术要求
要开始本章的学习,我建议您首先阅读第八章,Vue.js 3 中的测试和要测试的内容,在那里我们探讨了软件测试的概念以及从一系列可用的组件和方法中测试什么。在本章学习 Vue.js 3 企业级单元测试时,我们将大量依赖该章节的知识。
本章的所有代码文件都可以在github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-9
找到。
单元测试简介
如果正确执行,单元测试是开发阶段的一个重要步骤,因为它有助于在开发阶段发现错误和缺陷。
单元测试是一种软件测试方法,其中被测试软件的最小可测试部分,称为单元,被单独或独立地测试以确保其正确操作,并确保输出与所需输出相符。
单元可以被认为是软件中被测试的个体函数、对象、方法、过程或模块。
这种软件测试方法是由软件工程师开发的,用于测试程序的单元。它帮助软件工程师了解代码的各个单元是否正常工作。
在本节中,我们将探讨单元测试的定义,并探讨开发人员如何在他们的企业级 Vue.js 3 应用程序中创建和管理单元测试。
什么是单元测试?
单元测试是一种验证可测试代码最小部分是否符合其目的或要求的方法。这种方法确保了代码库中最小的部分被测试,并使其符合要求。
在开发阶段发现和修复错误非常重要。单元测试是开发人员的责任,因为它是开发阶段由开发人员执行的,并且是开发人员为他们的生产代码创建单元测试。
软件开发者可以通过两种不同的方式来处理单元测试,要么在编写单元测试之前编写代码,要么在编写实际代码之前,开发者首先创建一个失败的单元测试。第二种方法被称为测试驱动开发(TDD)。
在探索软件工程和开发的软件开发生命周期(SDLC)时,单元测试属于开发阶段,因为它是开发者的责任,并作为基础和第一层测试,以确保软件无错误和无缺陷。
图 9.1 – SDLC
在下一节中,我们将探讨创建和管理单元测试的不同最佳实践。
单元测试的重要性和好处
在开发流程中实施单元测试的重要性不容忽视。它已被证明具有巨大的好处。它还帮助在开发阶段早期发现错误。在本节中,我们将探讨单元测试的一些主要好处及其重要性。
单元测试用于设计稳健且企业级软件组件,有助于维护代码并消除代码单元中的问题。与在生产中发现错误相比,在开发过程中发现和修复错误更重要。单元测试有助于在开发阶段早期修复这些错误。
它是敏捷软件开发过程的一个基本组成部分。在构建过程和部署期间,单元测试套件被自动化以运行并生成报告。如果任何单元测试失败,那么 QA 团队不应接受该构建进行验证,而应将其退回给开发团队进行更多检查和验证。
单元测试为 QA 和软件测试团队节省了大量时间。如果企业应用程序有一个适当的标准和配置良好的自动化软件测试流程,错误和缺陷将在开发过程中自动被发现。
由于时间限制和紧迫的截止日期,开发者可能会避免编写单元测试。在大多数情况下,他们可能会选择编写质量较差的单元测试,只是为了达到 100%的通过率。这是一种非常不好的做法;与其编写质量差的单元测试以通过,不如完全不编写测试。
下面是编写良好的单元测试用例的一些好处:
-
提高代码质量:如果单元测试被严格地实施到开发流程中,并且做得正确,那么交付到生产环境的代码质量将自动提高。单元测试是测试的最早形式;因此,在此测试期间发现的任何错误都将在它们被发送到集成测试阶段之前得到修复。这种方法的成果是稳健的设计和开发,因为开发者首先通过理解规格和手头的任务来编写测试用例。
-
早期检测错误:单元测试是软件开发中的第一级测试。因此,它有助于早期识别和修复错误。这包括软件需求规格说明中的缺陷、缺失部分或开发者实现中的错误。
-
节省开发时间:代码补全需要时间,尤其是在使用适当的软件开发实践时。因此,当系统中的错误和缺陷由于单元测试的有效性而减少时,整体开发时间就会减少。
-
易于调试过程:单元测试有助于简化企业级应用程序的测试和调试过程,因为如果在任何阶段测试失败,则需要调试代码;否则,过程可以继续而没有任何障碍。
-
降低成本:当在开发过程中而不是在生产过程中检测和解决错误时,包括开发时间在内的开发成本会大幅降低。如果没有这种测试,如果在代码集成后较晚的阶段检测到相同的错误,它们就变得更加难以追踪和解决,从而使开发更加昂贵并延长开发时间。
单元测试的好处和重要性是无穷无尽的。因此,将单元测试纳入您企业级 Vue.js 3 应用程序开发流程中并实施它是良好的实践。在下一节中,我们将探讨创建单元测试用例的最佳实践。
单元测试创建的最佳实践
在创建单元测试用例时,您应遵循最佳实践,以产生一致的单元测试用例,正确地测试每个可能的案例。考虑以下要点来创建良好的测试用例:
-
安排、行动和断言(AAA)
-
编写确定性测试
-
编写好的测试名称和描述
-
在开发前或开发过程中编写测试(TDD)
-
利用自动化测试
-
使用模拟和存根
让我们在下一小节中更详细地理解这些要点。
安排、行动和断言
当为面向企业的应用程序构建单元测试套件时,建议遵循 AAA 方法以提高可读性和单元测试套件的易于理解。它通过提供逻辑流程来提高测试的可读性。它也可以被称为 给定/当/然后(GWT)策略。
GWT 是一种半结构化的编写测试用例的方式。这些测试用例可以是手动测试,也可以使用 LambdaTest 自动化测试(www.lambdatest.com/automation-testing?fp_ref=solomon26
)。
您可以使用以下步骤使用 AAA 协议来结构化您的单元测试用例:
-
安排:为测试安排设置和初始化。
-
行动:对给定测试的单元进行操作。
-
断言:断言或验证结果。
以下代码片段展示了使用 AAA 风格创建简单单元测试用例的基本示例:
it('test for positive number', async () => {
// Arrange
const positive = 6;
// Act
const answer = Math.abs(positive);
// Assert
assert.equal(answer, positive);
});
前面的代码片段显示了初始化变量和为给定测试创建初始设置的位置,然后是我们对给定测试采取行动的位置,最后是我们断言所采取行动的测试的结果。
编写确定性测试
单元测试应该在任何时间和任何地点都有一致的输出,以验证期望的功能。确定性测试应该在测试代码没有更改的情况下,每次运行测试时都有一致的行为。
测试中的不一致性可以称为测试的“不可靠性”。如果你的测试在开发中工作或通过,但在持续集成或 QA 测试期间失败,这会阻碍开发并减缓进度。
如果编写了确定性的测试用例,可以避免测试中的“不可靠性”,因为它有助于快速理解每个测试用例的输出,并减少新团队成员的调试时间。
编写好的测试名称和描述
在软件工程中,编写清晰代码的最佳实践之一是始终具备良好的命名能力。作为一名开发者,你的变量、函数、方法或类应该有良好且描述性的名称。
这种最佳实践也扩展到编写测试用例名称。拥有一个清晰且明确的测试用例描述对于准确捕捉测试应该实施的时间和期望的输出非常重要。
例如,你的测试用例名称应该描述测试用例的目的,如下面的示例所示:
describe("Test Names", () => {
it("is a Vue instance", () => {});
it("initializes with correct elements", () => {});
it("test for positive number", async () => {});
it('has a default message', async () => {});
it('can change message', async () => {});
)};
在开发前或开发过程中编写测试(TDD)
作为一名专业开发者,你需要将 TDD 的概念嵌入到你的开发过程和工作流程中。
TDD 是一种软件开发过程,它并行增强我们的测试用例和软件代码。
TDD 的概念与传统开发过程相矛盾,因为在 TDD 中,开发者必须首先编写测试代码,然后编写实际的软件代码以通过所编写的测试用例。这种方法确保了当编写生产代码时,它始终与测试代码相辅相成。
此外,行为驱动开发(BDD)是另一种流行的测试方法。这种方法在快速开发环境中效果良好,并鼓励更多的团队合作,以共同理解问题。
无论你决定在项目中采用哪种方法,你仍然可以将持续集成集成到你的开发流程中,以自动化你的软件测试过程。
使用模拟和存根
在创建测试用例时,你可能会有在真实代码上执行操作的冲动。例如,如果你对一个外部 API 进行了 API 调用,你可能希望在测试期间进行这样的调用,以确保一切按计划进行。但这并不被认为是最佳实践。你可以做的是使用任何测试框架的模拟和存根功能。
模拟(stub)是一段虚拟的代码,允许测试运行而不必担心它会发生什么,而存根(mock)是一段虚拟的代码,你验证它在测试中是否被正确调用。简而言之,它们是真实工作代码的替代品。
这种方法的优点在于,您可以设置并使用它们来测试和验证您的实际代码是否正常工作,而无需进行昂贵的 API 调用或执行数据库操作。
利用自动化测试
作为开发者,将自动化测试集成到您的开发流程中,与每次部署前手动执行测试用例相比,可以节省大量时间。
您可以使用不同的自动化测试框架来设置它,但在本章中,我们将了解如何使用 Selenium Cloud Grid 自动化单元测试(www.lambdatest.com/selenium-grid-online?fp_ref=solomon26
)。
在我们深入自动化测试用例之前,让我们探索如何使用 JavaScript 编写干净且合适的单元测试。
JavaScript 单元测试
作为开发者,为您的任务编写单元测试是你的责任。它应该成为您日常编码活动的一部分。在 JavaScript 中,您可以使用不同的测试库以与编写真实代码相同的方式编写单元测试。
使用这些测试库,测试您项目的功能性和特性变得非常容易,因为这些库包含了不同的断言方法来执行您的检查。
让我们探索一些最流行的 JavaScript 测试框架,您可以使用它们编写单元测试、集成测试,甚至是端到端测试。
流行的 JavaScript 测试框架
不同的框架对 JavaScript 的单元测试很有帮助。具体如下:
-
Jest
-
Mocha
-
Jasmine
-
Cypress
-
Vitest.js
让我们在下一小节中更详细地探索这些框架。
Jest
Jest 是最受欢迎的 JavaScript 测试框架之一。它被设计主要用于与 React 和 React Native 应用程序一起工作。它是开源的,易于上手。Jest 减少了在 JavaScript 前端进行软件测试所需的大量耗时配置。
它是一个在 Node.js 和浏览器上运行的 JavaScript 断言库。Jest 可以配置与任何测试运行器和单元测试框架一起工作,例如 Mocha 或 Jasmine。
根据 GitHub 的数据,截至撰写本文时,Jest 库的增长统计包括超过40k GitHub stars和大约6.3 百万 GitHub 使用量,累计达到6.4 百万积分,使 Jest 成为最受欢迎的测试框架之一。
Mocha
Mocha 是一个用于 JavaScript 和 Node.js 的服务器端和客户端测试框架。Mocha 的关键特性是简单性、灵活性和趣味性。它使得 JavaScript 中的异步测试变得简单且有趣。Mocha 被设计为串行运行,允许灵活且精确的测试报告和覆盖率。
根据 GitHub 的数据,截至编写本文时,Mocha 库的增长统计包括超过 21.6k GitHub 星标 和大约 160 万 GitHub 使用量,累计达到 166 万 点,使 Mocha 成为非常受欢迎的测试框架。
Jasmine
Jasmine 是一个流行的 JavaScript BDD 框架,用于单元测试 JavaScript 应用程序。它结合了速度和 Node.js 以及浏览器的支持,成为 BDD 的强大测试框架。
根据 GitHub 的数据,截至编写本文时,Jasmine 库的增长统计包括超过 15.4k GitHub 星标 和大约 240 万 GitHub 使用量,累计达到 250 万 点,使 Jasmine 成为最受欢迎的测试框架之一。
Cypress
Cypress 是一个基于 JavaScript 的端到端测试框架,它改变了开发者对待软件测试的方式。它建立在 Mocha 之上,使得异步测试变得简单方便。在 Cypress 中,单元测试可以配置为执行,甚至无需运行 web 服务器。
这个特性使 Cypress 成为测试旨在在浏览器中使用的 JavaScript/TypeScript 库的理想工具,并在企业项目中设置自动化测试变得轻而易举。
根据 GitHub 的数据,截至编写本文时,Cypress 库的增长统计包括超过 40.2k GitHub 星标 和大约 535k GitHub 使用量,累计达到 575k 点,使 Cypress 成为最受欢迎的测试框架之一。
Vitest
Vitest 是一个由 Vite 驱动的闪电般的单元测试框架。它是一个原生 Vite 单元测试框架,包括 Vite 可重用的配置、转换器、解析器和插件。它还与 Jest 兼容,并默认使用 ESM、TypeScript 和 JSX。
它相对较新,在 Vue.js 和 Vite CLI 的开发者中获得了人气。根据 GitHub 的数据,截至编写本文时,Vitest 库的增长统计包括超过 6.4k GitHub 星标 和大约 24.3k GitHub 使用量,累计达到 30.7k 点。
在下一节中,我们将探讨如何使用 JavaScript 编写单元测试,以及如何手动运行测试。此外,我们还将探讨如何在部署管道中自动化测试套件。
单元测试 Vue.js 3 应用
在上一章中,我们使用 Strapi 作为后端和 Vue.js 3 作为前端创建了一个 Pinterest 应用程序。
之前,我们添加了国际化,整理了项目结构,并构建了一个完整的 Pinterest 克隆应用。在本节中,我们将继续使用为本书创建的官方项目来设置单元测试,从而得到一个完全的企业级 Pinterest 克隆应用。
您可以从这个仓库克隆项目,github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications
,直接开始。
在上一章中,我们使用 Vitest 设置了基本的单元测试,并演示了如何使用辅助文件实现基本的单元测试。
在本章中,我们将探讨测试我们作为本书示例使用的 Pinterest 克隆应用程序单元的更高级方法。
要测试的内容
在上一章中,我们详细探讨了在设置测试套件时应该测试什么。在本节中,我们将检查我们的测试软件示例,并指定应该进行单元测试的内容。
通常,在 Vue.js 组件中,我们可以测试以下两个方面:表现(presentation)和(可选)行为。
表现
当使用 Apollo Client 获取数据时,组件可能处于loading
、success
或error
状态之一。对于这些状态中的每一个,测试表现是否符合预期都是一个不错的想法。
例如,考虑有一个组件,它展示有关特定照片的详细信息(执行GET_PIN
查询)来自我们的 Pinterest 克隆应用程序。
我们可以有一个简单的组件,如下所示:
<template>
<div v-if="status === 'loading'">Loading photo...</div>
<div v-else-if="status === 'error'">An error occurred
</div>
<div v-else>
<img src="img/kunal-img.jpg" alt=""
data-testid="pin"
class="w-full h-full object-cover rounded-2xl" />
</div>
</template>
<script setup>
import Button from '../atoms/Button.vue';
defineProps({
photo: { type: Object, default: () => { } },
status: { type: String, default: "Loading" }
});
</script>
在此场景中,我们可能希望测试以下内容:
-
加载:组件在获取引脚(照片)时的渲染方式
-
成功:组件在成功获取引脚(照片)后的渲染方式
-
错误:如果组件无法获取引脚(照片),其渲染方式如何
为了演示这一点,让我们针对前面提到的每个状态实现单元测试。我们将使用Pin(Photo)
组件:
import { render } from '@testing-library/vue';
import Card from '../../components/molecules/Card.vue';
test('displays a card with success status', async () => {
const { getByTestId } = render(Card, {
props: {
status: 'success',
},
});
const card = getByTestId('pin');
expect(card).toBeDefined();
});
test('displays a card with error status', async () => {
const { getByText } = render(Card, {
props: {
status: 'error',
},
});
const card = getByText('An error occurred');
expect(card.textContent).toEqual('An error occurred');
});
test('displays a card with loading status', async () => {
const { getByText } = render(Card, {
props: {
status: 'loading',
},
});
const card = getByText('Loading photo...');
expect(card.textContent).toEqual('Loading photo...');
});
行为(可选)
我们还可以选择在我们的 Vue.js 组件中放置行为。在客户端架构中,我们称之为交互逻辑——一种在用户以某种方式与页面交互后执行的决策逻辑——例如按键或按钮点击。
您还可以通过测试对组件执行的不同操作以及确保组件相应地做出反应来对单个组件的行为进行单元测试。
让我们测试这个Photo
组件的click
事件,以确保它对适当的操作做出响应:
test('clicks a create pin button', async () => {
const { getByTestId, emitted } = render(Card);
await fireEvent.click(getByTestId('create_pin'));
expect(emitted()).toHaveProperty('click');
});
您可以根据前面的示例代码片段编写对您在 Vue.js 3 企业级应用程序中创建的所有组件的单元测试。
在本章的存储库(github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-9
)中,我们创建了不同的组件,并为它们编写了单元测试。您可以通过克隆存储库来获取这些内容。
在下一节中,我们将手动运行单元测试,并展示如何使用 LambdaTest Cloud Grid 自动化此过程。(www.lambdatest.com/automation-testing/?fp_ref=solomon26
)
手动运行单元测试
要运行您的测试,请在您的根终端中输入以下命令:
npm run test:unit
// or
yarn test:unit
在测试成功运行后,你应该会看到绿色的通过标志,就像以下截图所示:
图 9.2 – 单元测试通过示例
在本节中,我们探讨了单元测试 Vue.js 3 组件的最佳实践。我们讨论了最重要的内容,即要测试什么以及如何在 Vue.js 3 中实现单元测试。我们演示了如何使用 Vitest (vitest.dev/
) 和 Vue.js 测试库对 Vue.js 应用进行单元测试。(github.com/testing-library/vue-testing-library
)
摘要
本章探讨了与单元测试相关的所有内容,包括如何对 Vue.js 3 组件和组件及页面方法进行单元测试。我们还了解了单元测试工具,如 Jest、Mocha 和 Vitest,以及如何使用它们有效地对企业的项目进行单元测试。
在本章中,我们探讨了编写和执行有效的单元测试策略的好处、重要性和最佳实践。我们还学习了如何根据被测试软件的不同单元的展示和行为来编写单元测试用例。
本章向您展示了如何在构建和部署过程中手动创建、实现和运行您的单元测试用例。
在下一章中,我们将探讨与集成测试相关的所有内容。我们将深入介绍如何在 Vue.js 3 组件和页面上执行集成测试。我们还将了解集成测试工具,如 Vue.js Testing Library,以及如何有效地使用它们来测试企业项目。
第十章:Vue.js 3 中的集成测试
在上一章中,我们学习了与单元测试相关的所有内容。我们学习了如何对 Vue.js 3 组件及其组件和页面的方法进行单元测试。我们还学习了单元测试工具,如 Jest 和 Mocha,并使用它们有效地对企业的项目进行单元测试。
在本章中,我们将探讨与集成测试相关的所有内容。我们将深入了解如何在 Vue.js 3 组件和页面上执行集成测试。我们还将了解集成测试工具,如 Vue Test Library (github.com/testing-library/vue-testing-library
),以及如何有效地使用它们来测试企业项目。
在本章中,我们将涵盖以下关键主题:
-
集成测试简介
-
什么是集成测试?
-
集成测试的重要性
-
集成测试的好处
-
创建集成测试的最佳实践
-
JavaScript 集成测试
-
测试基本的 Vue 应用
技术要求
要开始本章的学习,我建议您阅读第九章,单元测试的最佳实践,其中我们探讨了编写和执行有效的单元测试策略的好处、重要性和最佳实践。我们将在这个章节中大量依赖从那一章中获得的知识,来学习 Vue 3 企业集成测试。
本章的所有代码文件都可以在github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-10
找到。
集成测试简介
团队中的工程师在开发过程中独立开发应用程序,并在开发过程中对每个单元进行开发和单元测试后,软件测试阶段的下一阶段是集成测试。这种测试形式涉及在模块/组件组合/集成时测试它们,以确保它们符合要求。
它是一种测试类型,其中软件模块的单元逻辑上集成并作为一个整体进行完全测试。
在本节中,我们将检查集成测试的定义,并探讨开发者在他们的企业级 Vue 3 应用程序中如何创建和管理集成测试用例。
什么是集成测试?
集成测试是一种测试类型,其中被测试软件(SUT)的不同单元、模块或组件被组合并作为一个单一实体进行测试。此外,这些模块或单元独立于开发人员或团队,可以由不同的程序员编写。
它也被称为组件测试或集成与测试(I&T)。
集成测试是将单个模块组合成组件或组合实体的第一阶段,其目的是测试模块之间的接口,以暴露在组件集成和交互时可能出现的任何缺陷。
在下一节中,我们将探讨集成测试对敏捷和企业管理团队的重要性及好处。
集成测试的重要性
集成测试是软件测试过程中的一个关键阶段。它是测试系统不同组件或模块之间交互和接口的过程。它有几个重要原因:
-
它有助于确保不同的系统组件能够无缝且按预期协同工作
-
它有助于识别和解决不同系统组件之间的冲突
-
它有助于识别和解决在单元测试中可能未发现的错误
-
它有助于识别和解决在单元测试中可能未被发现的表现瓶颈
-
它有助于确保系统可以处理预期的负载和使用模式
-
它有助于确保系统安全并能保护敏感数据
-
它有助于确保系统可以与其他系统或外部组件集成
-
它有助于确保系统满足设计阶段定义的要求和规范
-
它有助于识别和解决不同系统组件之间数据流和数据完整性的问题
-
它可以帮助识别和解决不同系统组件之间用户界面(UIs)和用户交互的问题
-
它可以帮助识别和解决系统可能需要交互的第三方 API 和服务的问题
-
它可以帮助确保系统可以处理不同的环境和配置,例如不同的操作系统或不同的硬件配置
-
它可以帮助确保系统与其可能需要交互的其他系统兼容,例如数据库或外部服务
总体而言,集成测试是软件开发过程中的一个关键步骤,有助于确保最终产品的质量和可靠性。在开发早期进行集成测试很重要,以便尽快识别和解决问题。
还需要注意的是,集成测试不仅限于测试系统不同组件之间的交互。它还包括测试系统作为一个整体与其他将与之集成的外部系统。在将系统投入生产之前测试其与其他系统的集成是一个好的实践。
在下一节中,我们将探讨集成测试的一些好处。
集成测试的好处
集成测试对于企业软件系统尤为重要,因为它们通常是复杂、多方面的系统,需要与其他系统集成并处理大量数据和交易。集成测试对企业软件的一些益处包括以下内容。
提高系统可靠性
通过测试系统不同组件之间的交互和接口,集成测试有助于确保系统可靠,并能处理预期的负载和使用模式。这有助于减少停机时间并提高系统的整体性能。
降低数据丢失风险
企业系统通常处理大量敏感数据,集成测试有助于确保数据得到适当保护,并在不同系统组件之间保持数据完整性。
提高可扩展性
集成测试有助于识别和解决单元测试期间可能未被发现的表现瓶颈,使得根据需要扩展系统变得更加容易。
提高安全性
集成测试有助于确保系统安全并能保护敏感数据,降低数据泄露和其他安全事件的风险。
更好地与其他系统集成
在系统发布到生产之前对其进行与其他系统的集成测试,可以帮助确保系统兼容,并能与其他系统进行通信,这些系统可能是它需要交互的。
更好的合规性
通过进行集成测试,你可以确保系统满足设计阶段定义的要求和规范,这有助于确保符合行业标准法规。
更好的客户满意度和 ROI
通过确保系统可靠、安全且易于使用,集成测试有助于提高客户满意度和增加系统采用率。确保所有系统组件无缝且无错误地工作,有助于降低开发成本,提高系统性能,并增加系统的整体投资回报率(ROI)。
提高测试效率
集成测试可以在开发早期阶段识别问题,这有助于减少测试的整体时间和成本。
提高软件质量
通过彻底测试系统不同组件之间的交互和接口,集成测试有助于确保软件质量高且无缺陷。
提高团队协作
集成测试通常涉及不同团队之间的协作,如开发、测试和运维团队。这有助于改善团队间的沟通和协作,并确保系统开发和测试满足所有利益相关者的需求。
提高文档质量
集成测试过程可以导致更好的测试用例和测试结果记录,这些记录可用于改进系统并供未来参考。
改进业务连续性
通过确保系统可靠并能处理预期的负载和用法模式,集成测试可以帮助确保系统在意外事件(如硬件故障或停电)发生时能够继续运行,这有助于提高业务连续性和最小化中断。
改进数据治理
通过彻底测试不同系统组件之间的交互和接口,集成测试可以帮助确保数据得到适当保护,并在系统的不同组件之间保持数据完整性,这有助于提高数据治理和符合数据保护法规。
在本节中,我们探讨了集成测试对企业项目的益处以及它是如何改善开发团队工作流程的。在下一节中,我们将了解在集成测试期间应实施的一些最佳实践。
创建集成测试的最佳实践
现在让我们看看创建有效集成测试的一些最佳实践。
早期开始
集成测试应尽可能早地在开发过程中开始,理想情况下在设计阶段。这将有助于尽早识别和解决问题,并确保系统开发和测试满足所有利益相关者的需求。
明确定义目标
在开始集成测试之前,明确测试目标是至关重要的。这包括确定系统应该做什么,系统的接口和交互,以及预期的结果。
制定测试计划
制定一个测试计划,概述测试范围、要执行的测试用例以及所需资源和工具。测试计划还应包括测试时间表,包括何时完成测试以及何时审查结果。
采用模块化方法
将系统划分为更小、更易于管理的模块,并分别进行测试。这将有助于更快、更有效地识别和解决问题。
使用自动化测试
自动化测试可以帮助提高测试效率,并减少测试的时间和成本。自动化测试还可以用于在不同条件下测试系统,如不同的环境和配置。
对整个系统进行测试
将系统作为一个整体与其他将与之集成的外部系统进行测试;这将确保系统兼容,并能与其他可能需要交互的系统进行通信。
测试安全漏洞
测试系统是否存在安全漏洞,如 SQL 注入或跨站脚本攻击。这将有助于确保系统安全并保护敏感数据。
测试性能瓶颈
测试系统以发现性能瓶颈,并识别和解决可能出现的任何问题。这将有助于确保系统可以处理预期的负载和用法模式。
记录和审查结果
记录测试结果,包括识别出的问题及其解决方法。审查结果并利用它们来改进系统和测试过程。
持续测试和监控
在系统发布到生产后,持续测试和监控系统;这将有助于确保系统的可靠性,并快速识别和解决任何问题。
在本节中,我们探讨了在企业中使用集成测试时应实施的最佳实践。在下一节中,我们将学习如何在演示项目中使用 JavaScript 实现集成测试。
JavaScript 集成测试
有几种工具可以用于在 JavaScript 中执行集成测试。以下是一些流行的选择。
Mocha
Mocha 是一个广泛使用的 JavaScript 测试框架,非常适合集成测试。它高度可定制,可以与其他库一起使用,例如 Chai 和 Sinon,以执行各种类型的测试。
Cypress
Cypress 是一个基于 JavaScript 的端到端测试框架,可用于集成测试。它允许开发者从用户的角度测试应用程序的整个流程,并具有内置的实时调试、自动等待和时间旅行支持。
TestCafe
TestCafe 是一个运行在 Node.js 之上的端到端测试工具。它允许您在真实浏览器中运行测试,易于设置和使用。它还提供了测试应用程序 UI 的能力,这在集成测试中非常有用。
Selenium
Selenium 是一个浏览器自动化工具,可用于 Web 应用程序的集成测试。Selenium WebDriver 允许您与网络浏览器交互并执行诸如点击按钮、填写表单和浏览页面等任务。
Vue Test Utils
Vue Test Utils 是 Vue.js 团队提供的官方测试库。它是一个轻量级的库,提供了一组用于测试 Vue 组件的实用工具。它可以与其他测试框架一起使用,例如 Jest 或 Mocha,以执行集成测试。
Avoriaz
Avoriaz 是一个专门为 Vue.js 组件设计的测试库。它提供了一套用于测试 Vue 组件的工具,并允许您在测试环境中轻松挂载和交互您的组件。
Vue Testing Library
这是一个为测试 Vue 3 应用程序而构建的库。它提供了一套用于测试 Vue 3 组件的实用工具,并允许您在测试环境中轻松挂载和交互您的组件。
Nightwatch
Nightwatch 是一个用于 Web 应用程序和网站的自动化测试框架。它可以用于 Vue 3 应用程序的集成测试,并允许您编写模拟用户与应用程序交互的集成测试。
与任何其他工具一样,您可以选择最适合您需求和应用程序结构的工具。请记住,某些工具可能更适合某些类型的测试或某些类型的应用程序。
这些是一些最受欢迎的工具;您可以根据您的需求和要测试的内容选择使用任何一种。
还需要注意的是,这些工具中的大多数都可以与其他库和框架集成,以扩展其功能。
在下一节中,我们将探讨如何使用 JavaScript 编写集成测试,以及如何手动运行测试。我们还将探讨如何在部署管道中自动化测试套件。
测试基本的 Vue 应用
在上一章中,我们使用 Strapi 作为后端和 Vue 3 作为前端创建了一个 Pinterest 应用程序。
此外,我们还添加了国际化,结构化了项目,实现了单元和集成测试,并构建了一个完整的 Pinterest 克隆。在本节中,我们将继续使用为本书创建的官方项目来设置集成测试,以构建一个完整的、企业级就绪的 Pinterest 克隆应用程序。
您可以从技术要求部分提到的 GitHub 链接克隆项目。
在本章中,我们将探讨在本书中使用的 Pinterest 克隆应用程序中实现集成测试的更高级方法。
编写基本集成测试
首先,我们将开始安装新的 Vue 3 测试库。由于我们在项目中使用 Vite,我们还将安装 Vitest 库作为我们的测试运行器。您可以在vitest.dev/guide/
上了解更多关于新 Vue 3 测试库的信息。
安装测试库
到本文写作时,@testing-library/vue
(github.com/testing-library/vue-testing-library
) 和 vitest
(vitest.dev/guide/
) 被推荐用于集成测试,我们将使用以下命令安装测试库:
npm install -D vitest happy-dom @testing-library/vue
接下来,打开您的 vite.config.js
文件并添加以下配置。请注意,happy-dom
是一个没有图形 UI 的 Web 浏览器的 JavaScript 实现:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
test: {
environment: 'happy-dom',
globals: true,
},
});
在前面的代码中,我们配置了测试库以适应使用 Vite 库进行的单元和集成测试。这意味着如果我们想运行单元和集成测试,我们只需要使用一个命令,就像运行测试时所示。
接下来,让我们创建一个简单的集成测试。
创建基本组件测试
这里有一个简单的集成测试代码片段来演示。这是一个通用的集成测试示例,用于测试 Pinterest 演示应用内部的Button
组件:
import { render } from '@testing-library/vue';
import Button from '../../components/atoms/Button.vue';
test('mounted a button with custom label', async () => {
// The render method returns a collection of utilities to
// query your component.
const { getByText } = render(Button, {
props: {
label: 'Test',
},
});
// getByText returns the first matching node for the
// provided text, and if button is rendered with Label from
// props
const button = getByText('Test');
});
测试简单地渲染带有自定义标签值的Button
,并检查我们是否可以在渲染过程中检索添加的自定义标签。
运行测试
运行测试将导致四个通过测试案例,包括我们之前创建的单元测试。以下是运行此测试样本的命令:
```bash
yarn test
```js
图 10.1 – 一般测试结果的截图
在本节中,我们展示了如何使用 Vue 3 和最新的 Vitest 测试库配置和构建集成测试,也称为组件测试。我们还编写了基本的组件测试,以帮助我们理解编写集成测试的过程。在下一节中,我们将探索使用 Vue 3 和 Vitest 测试集成组件。
测试集成组件
在前面的示例中,我们测试了一个简单的Button
组件,以确保它以所需的属性正确渲染。在本节中,我们将测试一个完全集成的组件,该组件结合了不同的单个组件。让我们按照以下步骤开始:
-
在
tests/components
目录下创建一个名为HomeOverivew.vue
的文件,因为我们想测试主页的集成。 -
打开文件并添加以下测试代码,或者使用技术要求部分中提到的 GitHub 链接克隆仓库:
import { fireEvent, render } from "@testing-library/vue"; import { describe, expect, it } from "vitest"; import HomeOverview from "../../components/templates/HomeOverview.vue"; describe("HomeOverview.vue", () => { it("renders component", async () => { const { getByText } = render(HomeOverview); getByText("Home"); }); it("creates pin on button click", async () => { const { getByTestId, emitted } = render(HomeOverview); await fireEvent.click(getByTestId("create")); // Fireevent is from "@testing-library/vue" for // calling different events such as click expect(emitted()).toHaveProperty("click"); }); it("dismisses notification", async () => { const { getByTestId, emitted } = render(HomeOverview); await fireEvent.click(getByTestId("dismissed")); expect(emitted()).toHaveProperty("click"); }); it("displays first 14 pins", async () => { const { getAllByText } = render(HomeOverview); const card = getAllByText("Quick save and organize later"); expect(card.length).toBe(14); }); it("renders Search component", async () => { const { getByTestId } = render(HomeOverview); getByTestId("search"); }); });
在每个测试案例中,我们正在测试添加到HomeOverview
组件中的组件,以展示我们如何使用集成测试来测试集成组件作为一个整体。
-
接下来,确保在测试其他测试案例之前,
HomeOverivew
页面已经正确渲染:it("renders component", async () => { const { getByText } = render(HomeOverview); getByText("Home"); });
-
接下来,测试
Button
组件是否正确渲染,并检查我们是否可以对其进行一些操作。例如,当按钮被点击时,应用应该创建一个新的带有照片的 pin。我们将测试确保即使在集成之后,此功能也正确实现:it("creates pin on button click", async () => { const { getByTestId, emitted } = render(HomeOverview); await fireEvent.click(getByTestId("create")); expect(emitted()).toHaveProperty("click"); });
-
接下来,测试
Header
组件内部的通知显示
组件。我们正在测试它以确保用户可以取消通知,并且它也在Header
组件和HomeOverview
组件内部正确渲染:it("dismisses notification", async () => { const { getByTestId, emitted } = render(HomeOverview); await fireEvent.click(getByTestId("dismissed")); expect(emitted()).toHaveProperty("click"); });
-
接下来,也测试
Card
组件以确保它在主页上显示 pin 的总数,并且正确渲染组件:it("displays first 14 pins", async () => { const { getAllByText } = render(HomeOverview); const card = getAllByText("Quick save and organize later"); expect(card.length).toBe(14); });
-
最后,我们将测试
Search
组件以确保它已正确渲染供用户使用,并且用户可以使用它来搜索 pins:it("renders Search component", async () => { const { getByTestId } = render(HomeOverview); getByTestId("search"); });
-
现在,让我们在根终端中运行以下命令来运行测试:
```bash yarn test:component ```js
在成功运行测试后,你应该会看到绿色的通过测试结果,如下面的截图所示:
图 10.2 – 集成测试结果的截图
所有这些测试用例都成功后,我们可以轻松地看到集成测试如何帮助开发者测试集成和组合的组件,而不是单独测试这些组件。
摘要
本章探讨了与集成测试相关的所有内容。你还学习了集成测试工具,如 Cypress、Mocha 和 Vue Testing Library,并使用 Vue Testing Library 有效地测试了企业项目。
在本章中,我们探讨了编写和执行有效的集成测试策略的好处、重要性和最佳实践。此外,你还学习了如何编写集成测试用例。
在下一章中,你将了解与端到端测试相关的所有内容。你将深入了解如何在 Vue.js 3 组件和页面上执行端到端测试。此外,你还将了解端到端测试工具,如 Cypress 和 Puppeteer,以及如何使用它们从端到端有效地测试企业项目。
第十一章:行业标准的端到端测试
在上一章中,我们学习了与集成测试相关的所有内容。我们深入了解了如何在 Vue.js 3 组件和页面上执行集成测试。此外,我们还学习了集成测试工具,如 Vue Test Library,以及如何有效地使用它们来测试企业级项目。
本章我们将探讨与端到端测试(E2E)相关的所有内容。我们将学习如何在 Vue.js 3 组件和页面上执行 E2E 测试。此外,我们还将学习 E2E 测试工具,如 Cypress 和 Puppeteer,以及如何有效地使用它们在企业项目中执行 E2E 测试。
本章我们将涵盖以下关键主题:
-
E2E 测试简介
-
什么是 E2E 测试?
-
E2E 测试的重要性
-
E2E 测试的好处
-
创建 E2E 测试的最佳实践
-
JavaScript E2E 测试
-
对 Vue 应用进行 E2E 测试
技术要求
要开始本章的学习,我建议您阅读第十章,Vue.js 3 中的集成测试,其中我们探讨了与集成测试相关的所有内容。我们还深入了解了如何在 Vue.js 3 组件和页面上执行集成测试。在本章中,我们将大量依赖该章节的知识来学习 Vue.js 3 企业级 E2E 测试。
本章所有代码文件均可在github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-11
找到。
E2E 测试简介
E2E 是一个从开始到结束评估复杂应用程序工作顺序的复杂测试过程。此外,您可以使用 E2E 测试来按照您希望最终用户使用产品的确切方式工作,并在将代码推送到真实用户之前发现任何错误。
在大多数组织中,E2E 测试是在与团队中不同开发者独立开发后,用于访问应用程序完整功能的标准。
通过拥有一个用于构建和组合代码库的中心存储库系统,在大团队中进行 E2E 测试是可能的。接下来,E2E 测试将运行通过完成的功能,确保它们按预期工作,然后在批准并推送到生产阶段之前。
在本节中,我们将探讨 E2E 测试的定义,并探讨开发者在企业级 Vue.js 3 应用程序中如何创建和管理 E2E 测试用例。
什么是 E2E 测试?
E2E 测试是一种评估复杂产品从开始到结束的工作顺序的方法。它确保应用程序按预期行为,并且数据在每个任务和过程中都保持并按照预期方向流动。
这种测试旨在复制真实用户场景,以验证系统的集成和数据完整性。测试将遍历应用程序可以执行的所有操作,包括与外部设备的通信,以确保最终用户的行为得到复制和测试。
在下一节中,我们将探讨 E2E 测试对敏捷和企业管理团队的重要性和好处。
E2E 测试的重要性
E2E 测试使得在软件发布给最终用户之前捕捉问题变得更加简单。此外,它通过确定工作流程对最终用户的重要性,帮助管理者优先处理开发待办事项。
此外,对于企业级应用程序,E2E 测试通过提高多个应用程序接口(如 Web、桌面和移动应用)的用户体验,因为用户期望是测试用例的基础。
E2E 测试已被广泛采用,因为它通过减少测试软件所需的时间来帮助降低构建和维护软件的整体成本。
它通过添加比单元测试和功能测试等其他测试方法更详细的测试用例,帮助团队扩展其测试覆盖率。
它通过基于最终用户行为的测试用例运行,确保应用程序能够正确执行。
E2E 测试的好处
E2E 测试是一种非常复杂的测试形式,因为它测试了最终用户的行为。因此,遵循以下小节中概述的实践至关重要,以确保测试顺利。
降低风险
E2E 测试过程确保在每个迭代结束时对测试软件进行严格的测试,从而降低了生产中未来失败的风险。
一致的用户体验
E2E 测试涉及前端测试。它确保测试软件在多个设备、平台和环境上提供一致的用户体验。
降低成本和时间
通过自动化 E2E 测试,可以减少测试企业应用程序的成本和时间,从而减少维护和改进应用程序所需的时间和金钱。
增加信心
E2E 测试不仅确保应用程序正确运行,而且因为它在多个设备和平台上进行了测试,所以增加了对其性能的信心。
减少重复工作
它减少了频繁故障的可能性,并最终由于更彻底和严格的 E2E 测试,减少了重复测试的努力。
确保应用程序的正确性
E2E 是一种基本的软件测试方法,因为它在所有层——数据、业务规则、集成和展示——上验证应用程序。因此,它有助于确保应用程序的正确性和健康。
在本节中,我们讨论了 E2E 测试的重要性和好处,说明了为什么公司需要将其集成到他们的开发流程中。在下一节中,我们将探索创建 E2E 测试的最佳实践。
E2E 测试的最佳实践
E2E 测试模仿了真实用户使用应用程序的动作、活动和体验。在创建 E2E 测试用例时,你应该遵循以下最佳实践,以产生一致的 E2E 测试用例,正确测试每个可能的案例。考虑以下要点来创建良好的测试用例。
优先考虑最终用户
在创建测试用例时,要像用户一样测试,并进入一个首次使用该应用的心态。此外,询问并回答一些用户的问题,例如是否容易找到所有选项?功能是否已标记?用户能否在 更少的步骤中获取他们想要的东西?
优先考虑正确的方面
优先考虑你要测试的内容非常重要,因为这很容易变得繁琐和复杂。因此,在处理其他不那么重要的边缘情况之前,优先考虑受业务影响的特性是很重要的。
使测试更真实
有时,你希望使 E2E 测试更加真实。在大多数情况下,真实用户会停下来查看图片,或者暂停并观看一些视频,然后再继续他们的动作。E2E 测试应尽可能多地反映现实生活中的交互。
测试重复的用户场景
E2E 测试非常复杂,需要时间来完全测试所有可能的边缘情况。避免测试所有可能的边缘情况,只关注最常见和重要的场景。
错误监控
E2E 测试是一个非常复杂的过程,因为它涵盖了整个应用程序的遍历,有时只是新添加的功能。然而,通过确保在 E2E 测试之前解决许多错误,可以减少复杂性。
优化测试环境
你可以通过创建一个最佳测试环境来简化测试过程。创建最佳测试环境可以在测试时减少设置,并在进行下一次测试之前清理数据。
我们已经探讨了实施 E2E 测试的最佳实践,并讨论了在构建 E2E 测试解决方案时必须考虑的一些要点。在下一节中,我们将了解有关不同的 JavaScript E2E 测试库的更多信息。
JavaScript E2E 测试
以下是一些有助于 JavaScript 单元测试的框架:
-
Selenium WebDriver
-
Cypress
-
Playwright
-
Puppeteer
-
Karma
接下来,我们将探索每个库,讨论它们的流行度、相似之处和不同之处,并探讨为什么你应该选择这些库中的任何一个来作为你的 E2E 测试解决方案。
Selenium WebDriver
Selenium WebDriver 是最受欢迎的端到端测试软件。它是一个允许你通过自动化基于 Web 的应用程序测试来执行跨浏览器测试的 Web 框架,以验证其是否按预期执行:
图 11.1 – 展示 Selenium 统计的图表
根据 GitHub(github.com/seleniumhq/selenium
)在编写时的数据,Selenium 库的一些增长统计数据包括超过 25.4k GitHub Stars 和大约 172k GitHub 使用量,使 Selenium 成为最受欢迎的测试框架之一。
Cypress
Cypress 是一个基于 JavaScript 的端到端测试框架,它改变了开发者对待软件测试的方式。它是一个不使用 Selenium 或 WebDriver 的测试框架,这使得它在企业级测试中更快且易于设置。
这个特性使 Cypress 成为测试旨在在浏览器中使用的 JavaScript/TypeScript 库的理想工具,并在你的企业项目中使用它设置自动化测试变得轻而易举:
图 11.2 – 展示 Cypress 统计的图表
根据 GitHub(github.com/cypress-io/cypress
)在编写时的数据,Cypress 库的一些增长统计数据包括超过 42.1k GitHub Stars 和大约 648k GitHub 使用量,使 Cypress 成为最受欢迎的测试框架之一。
Playwright
Playwright 允许现代 Web 应用进行可靠的端到端测试。它支持所有现代渲染引擎,包括 Chromium、Webkit 和 Firefox。此外,它支持 Windows、Linux 和 macOS 的跨平台测试,包括本地、CI、无头或带头的测试。最重要的是,你可以使用移动网页和不同的编程语言进行测试:
图 11.3 – 展示 Playwright 统计的图表
根据 GitHub(github.com/microsoft/playwright
)在编写时的数据,Playwright 库的一些增长统计数据包括超过 46k GitHub Stars,使 Playwright 成为最受欢迎的测试框架之一。
Puppeteer
Puppeteer 是由 Google 开发的 Node.js 库,允许你通过编程方式控制无头 Chrome。你可以自动化你的 Web 应用程序的测试,在浏览器中运行测试,并在你的终端会话中实时查看结果:
图 11.4 – 展示 Puppeteer 统计的图表
根据 GitHub(github.com/puppeteer/puppeteer
)在编写时的数据,Puppeteer 库的一些增长统计数据包括超过 81.3k GitHub Stars 和大约 271k GitHub 使用量,使 Puppeteer 成为最受欢迎的测试框架之一。
Karma
Karma 是一个端到端测试框架,它为每个连接的浏览器启动一个 Web 服务器,执行针对测试代码的源代码。结果将显示给开发者,以便查看测试用例是否失败或通过。
Angular 团队创建了 Karma 测试库,以满足他们不断变化的测试需求,使生活更加轻松:
图 11.5 – 显示 Karma 统计的图表
根据 GitHub(github.com/karma-runner/karma
)在撰写本文时的增长统计数据,Karma 库包括超过11.8k GitHub Stars和大约2.8m GitHub Usage,使 Karma 成为最受欢迎的测试框架之一。
在下一节中,我们将探讨如何使用 JavaScript 编写端到端测试以及如何手动运行测试。此外,我们还将探讨如何在部署管道中自动化测试套件。
端到端测试 Vue.js 3 应用
在上一章中,我们使用 Strapi 作为后端和 Vue.js 3 作为前端创建了一个 Pinterest 应用。
在前面的章节中,我们添加了国际化,对项目进行了结构化,实现了单元和集成测试,并构建了一个完整的 Pinterest 克隆应用。在本节中,我们将继续使用为本书创建的官方项目来设置端到端测试,以构建一个完整的、企业级就绪的 Pinterest 克隆应用。
您可以从github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications
克隆项目并立即开始。
在上一章中,我们使用 Vitest 设置了基本的单元测试,并演示了如何使用辅助文件实现基本的单元测试。
在本节中,我们将探讨在本书中使用的 Pinterest 克隆应用上实现端到端测试的更高级方法。我们将使用 Cypress 进行端到端测试。
设置 Cypress
使用之前列出的某些流行测试框架,编写端到端测试比您想象的要简单。在本节 JavaScript 端到端测试教程中,我们将使用 Cypress 框架编写端到端测试用例。
我们将编写一些端到端测试用例,以记录用户登录到我们的 Pinterest 克隆应用,并检查用户详情是否正确,但在那之前,让我们安装和配置 Cypress。
安装和配置 Cypress
您可以通过遵循本章内容来设置一个新项目并安装和配置 Cypress。然而,您也可以从官方仓库克隆第十一章
文件夹来跟随操作。在撰写本文时,以下库需要安装:
```bash
yarn add cypress @cypress/vue @cypress/webpack-dev-server
```js
接下来,将以下脚本添加到您的package.json
文件中:
```bash
"test:e2e": "cypress open --e2e",
"test:e2e:ci": "cypress run --e2e"
```js
新的脚本将允许你仅运行端到端测试并查看输出,而无需运行其他测试用例。
最后,在你的项目根目录中创建一个cypress.config.js
文件,并添加以下配置:
const { defineConfig } = require("cypress");
module.exports = defineConfig({
component: {},
env: {
// HINT: here we read these keys from the .env file,
// feel free to remove the items that you don't need
baseUrl: "http://localhost:3000",
apiUrl: "http://localhost:1337",
email: "admin@test.com",
password: "Admin111",
},
e2e: {
supportFolder: false,
supportFile: false,
specPattern: "src/tests/e2e/**/*.spec.js",
// eslint-disable-next-line no-unused-vars
setupNodeEvents(on, config) {
// implement node event listeners here
},
baseUrl: "http://localhost:3000",
},
});
env
内部的变量是可选的,并且只包含特定于我的环境变量的变量;你应该更新它以反映你的环境变量。
接下来,我们将配置我们的 E2E 实例以读取特定模式的文件,并将supportFolder
和supportFile
设置为false
,因为我们不希望在这个演示中包含任何支持文件或文件夹。
在下一节中,我们将创建第一个e2e
测试文件,并使用端到端测试测试我们的登录功能。
创建测试文件
要创建测试文件,请在e2e
测试文件夹内打开或创建一个Login.spec.js
文件,并添加以下代码:
/* eslint-disable no-under */
const loginFunction = () => {
cy.visit(`${Cypress.env("baseUrl")}/login`);
};
describe("Login tests", () => {
beforeEach(() => {
loginFunction();
cy.wait(5000);
});
it("Should show an success message if the email address
and password is valid", () => {
cy.get("#passwordField").type(`${Cypress.env("password")}`);
cy.get("#emailField").type(`${Cypress.env("email")}`);
cy.get("#loginForm").then(() => {
cy.get("#submitButton").click();
cy.wait(1000);
cy.get("#loggedIn").should("be.visible");
});
});
it("Should show an error message if the email address and
password is not valid", () => {
cy.get("#emailField").type("test@test.com");
cy.get("#passwordField").type("test");
cy.get("#loginForm").then(() => {
cy.get("#submitButton").click();
cy.wait(1000);
cy.get("#failed").should("be.visible");
});
});
});
代码分析:
让我们一起来逐步分析代码,理解其细节:
- 步骤 1:在
beforeEach
中加载登录页面:
首先,我们创建一个函数,使用 Cypress 的visit()
方法在beforeEach
钩子内部访问我们的登录页面:
const loginFunction = () => {
cy.visit(`${Cypress.env("baseUrl")}/login`);
};
- 步骤 2:创建
beforeEach
块:
在beforeEach
块内部,我们执行loginFunction
以为每个测试用例打开登录页面:
describe("Login tests", () => {
beforeEach(() => {
afterLoginFunction();
cy.wait(5000);
});
- 步骤 3:编写每个测试用例:
最后,我们开始编写每个测试用例,并定义我们期望测试的内容。以下是一个使用端到端测试提交登录表单中的按钮的示例,以模拟用户如何与登录表单交互:
it("Should show a success message if the email address
and password is valid", () => {
cy.get("#passwordField").type(`${Cypress.env("password")}`);
cy.get("#emailField").type(`${Cypress.env("email")}`);
cy.get("#loginForm").then(() => {
cy.get("#submitButton").click();
cy.wait(1000);
cy.get("#loggedIn").should("be.visible");
});
});
在编写完所有测试用例后,你可以执行测试。在你运行测试之前,请确保你的开发服务器正在运行。
运行测试
要运行测试,请在你的根终端会话中输入以下命令:
```bash
yarn test:e2e
yarn test:e2e:ci
```js
第一个命令将使用无头浏览器展示用户如何与你的应用程序交互,而最后一个命令将只显示测试结果,如单元测试。
在测试成功运行后,你应该会看到绿色的通过标志,如下面的截图所示:
图 11.6 – 端到端测试结果的截图
概述
本章探讨了与端到端测试(E2E testing)相关的所有内容。我们学习了端到端测试工具,如 Cypress、Karma 和 Selenium,并使用 Cypress 有效地测试了一个企业级项目。
此外,我们还探讨了编写和执行有效的端到端测试策略的好处、重要性和最佳实践。我们还学习了如何编写端到端测试用例。
在下一章中,我们将学习如何将 Vue.js 3 项目部署到 AWS 云。我们将学习部署到 AWS 的最佳实践。此外,我们还将了解企业公司如何部署他们的企业级 Vue 应用程序。
此外,我们还将了解并探索不同的部署选项,并掌握将您的 Vue.js 3 项目部署到各种云提供商的最佳实践。我们将学习如何部署到 AWS 和 Azure。
第五部分:部署企业级 Vue.js 3
在本部分,您将学习和探索不同的部署选项,并掌握将您的 Vue.js 3 项目部署到各种云提供商的最佳实践。您将学习如何部署到 AWS 和 Azure。
我们将探索 Nuxt.JS 来构建和交付企业级、服务器端渲染的 Vue.js 3 网络应用程序。我们还将探索 Gridsome 来构建和部署高性能、客户端渲染的 Vue.js 3 应用程序。
在本部分,我们将涵盖以下章节:
-
第十二章, 将 Vue.js 3 部署到云端
-
第十三章, 高级 Vue.js 框架
第十二章:部署企业级 Vue.js 3
在上一章中,我们探讨了与端到端(e2e)测试相关的所有内容。我们深入学习了如何在 Vue.js 3 组件和页面上执行 e2e 测试。此外,我们还了解了端到端测试工具,如 Cypress 和 Puppeteer,以及如何有效地使用它们来测试企业级项目。
本章,我们将学习如何将 Vue.js 3 项目部署到 AWS 云。我们将学习部署到 AWS 的最佳实践。此外,我们还将了解企业公司如何部署他们的企业级 Vue.js 3 应用程序。
此外,我们还将了解并探索不同的部署选项和最佳实践,以便将您的 Vue.js 3 项目部署到各种云服务提供商。我们将学习如何将应用程序部署到 AWS 和 Azure。
本章将涵盖以下关键主题:
-
CI/CD 简介
-
CI/CD 概述
-
什么是部署管道?
-
GitHub Actions 概述
-
部署到 AWS
技术要求
要开始,我建议阅读第十一章,行业标准端到端测试,其中我们探讨了 e2e 测试的概念以及从一系列组件和方法中选择要测试的内容。我们将在这个章节中大量依赖该章节的知识来学习 Vue.js 3 企业级单元测试。
本章的所有代码文件都可以在github.com/PacktPublishing/Architecting-Vue.js-3-Enterprise-Ready-Web-Applications/tree/chapter-12
找到。
CI/CD 简介
开发一个企业级应用程序很容易,但不断地将新开发的更改、错误修复或功能部署给用户是一个令人畏惧的过程,尤其是如果频繁进行,特别是对于企业级应用程序。此外,随着您的应用程序、团队和部署基础设施的复杂性增加,持续发布和部署新更改、功能和产品给客户可能是一个复杂的过程。
为了快速且一致地解决开发、测试和发布软件的复杂过程,开发者和组织已经创建了三个相关但不同的策略来管理和自动化这些流程。
在下一节中,我们将探讨这三个被称为 CI/CD 的支柱,并解释这些策略以及它们是如何相互关联的。最重要的是,我们将探讨如何构建并将这些策略纳入我们的企业应用程序生命周期,以便它能够改变我们的软件开发和发布实践。
CI/CD 概述
CI/CD代表持续集成/持续交付。这是一种策略,允许企业团队更快、更高效地发布软件。它使产品能够以前所未有的速度进入市场,允许代码持续流入生产环境,并通过最有效的交付方式提供持续的新功能和错误修复。
CI/CD 管道被编写用于自动化从开发阶段到生产环境的软件交付过程。它构建、测试并安全地部署应用程序的新版本。
自动化管道的主要优势在于它消除了在部署过程中可能检测到的手动错误,并为开发者提供标准化的反馈循环,以加快产品迭代。
CI/CD 是不同策略和支柱的组合,共同构成了强大的企业软件交付管道;我们将在本节中详细探讨这些策略。
图 12.1 – CI/CD
持续集成
CI 是一种过程,允许团队中的开发者频繁地将他们的代码集成到一个共享的仓库中。这些开发者可以在隔离的环境中编写代码,并使用持续集成过程进行集成。这种做法鼓励每个开发者独立构建,并在一天中多次将代码与共享仓库集成。
当代码在开发周期的早期阶段集成时,开发者可以及早发现新旧代码之间的冲突。这个过程通过使其成为早期考虑的事项来最小化集成成本。
通过实施适当的持续集成策略,开发团队能够降低集成成本并及早应对缺陷。
为了使企业团队能够在稳健、自动化和快速的企业软件集成、部署和交付中取得成功,必须培养频繁迭代和对构建问题的快速响应的文化。
持续交付
CD(持续交付)是持续集成的扩展,旨在简化软件交付流程,并使团队能够轻松且放心地将代码部署到生产环境中。它通过自动化部署构建所需的步骤来降低部署或发布过程的难度,从而使得代码能够在任何时候安全地发布。
此外,持续交付是一个允许将完成的代码自动传输到各种设置的过程,例如测试和开发。它为代码被发送到这些区域提供了一个可靠和自动的方法。
此外,持续交付还包括基础设施的配置和部署,这可以手动完成并涉及多个步骤。这种类型的交付通常涉及整个团队的参与,并自动化这些流程。
持续交付依赖于部署管道来自动化对构建运行越来越全面的测试套件的过程,每个阶段都是一个连续的步骤。如果构建未通过测试,团队会收到通知,但如果通过,它将自动进入下一阶段。
对于企业软件团队来说,实施持续交付是至关重要的,因为它自动化了从提交代码到确定是否将经过良好测试、功能完善的建设部署到生产环境之间的过程。这一步骤有助于确保代码的质量和准确性自动化,而最终发布什么由工程团队决定。
持续部署
持续部署是持续交付的扩展,它将每个通过完整测试周期的构建部署出去,而不需要人工干预。这是有益的,因为手动部署可能导致延迟和不规则的部署。持续部署系统将部署在持续交付阶段设置的部署管道中通过的任何构建。
此外,自动部署代码并不意味着新功能不能有条件地激活或停用。事实上,持续部署可以配置为仅将特定功能部署到用户子集,或在稍后时间有条件地激活。
围绕持续部署的辩论通常集中在自动化部署的安全性以及它所承担的风险是否值得回报。然而,对于组织来说,这也有利可图,因为它们可以持续收到关于新功能的反馈,并在浪费过多时间和精力之前快速检测到任何错误。
我们已经探讨了 CI/CD 的概念以及如何自动化部署和发布企业项目。在下一节中,我们将探讨部署管道以及如何为企业 Vue.js 3 应用程序创建一个企业级部署管道。
什么是部署管道?
部署管道简化了企业应用程序的部署和交付。它编译代码,执行所有测试,并安全地部署应用程序的新版本。
使用部署管道自动化部署和交付流程可以消除手动错误,为开发者提供标准化的反馈循环,并使产品迭代更快。
此外,在构建企业产品时,你的组织结构、开发团队和模式将决定创建部署管道所使用的策略,因为它们可能因项目而异。
然而,企业项目中已经采用的不同策略可以根据需要采纳和修改。
在部署管道中,有一些必需的阶段(或元素)构成了 CI/CD 管道。在下一节中,我们将探讨这些元素,并学习如何为我们的演示企业项目设置部署管道。
部署管道的元素
部署管道由任何开发者在发布软件产品的新版本时必须遵循的可执行指令组成。
自动部署管道的美丽之处在于,它通过自动化流程来取代企业软件部署和交付的精确规范的手动过程。
下图显示了大多数企业软件中典型的软件发布阶段:
图 12.2 – 部署管道的元素
这些阶段可以手动执行,前提是每个步骤都按照相应的方式进行。然而,缺点是巨大的,正如你所看到的:
-
耗时:手动部署可能需要很长时间才能完成,尤其是当有多个组件需要部署时
-
易出错:手动部署容易出错,这可能导致代价高昂的错误和停机时间
-
缺乏可扩展性:手动部署不易扩展,因为它需要对每个需要部署的组件进行手动干预
为了避免这种情况,已经创建了一个自动部署管道来执行各个阶段,并在出现任何错误时通知负责的开发者,或者通过电子邮件、Slack 等发送通知。此外,当成功完成生产部署时,管道还可以通知整个团队。
现在,让我们逐一检查每个阶段,以了解其中包含的内容。这将帮助我们了解如何为我们的企业 Vue.js 3 应用程序开发一个良好的部署管道。
源阶段
在源阶段,管道通常由源代码仓库启动。每当代码发生变化时,它会通知 CI/CD 流程执行相关的管道。此外,其他常见的触发器包括用户触发的流程和自动计划的流程。
构建阶段
在构建阶段,我们将源代码及其所有依赖项组合在一起,构建一个可运行的实例,该实例打算发送给用户。在这个阶段,软件被编译或与其依赖项捆绑在一起。
构建阶段试图打包项目以使其可部署。如果构建阶段遇到任何问题,这表明项目设置或配置存在潜在问题,应立即处理。
测试阶段
一旦构建阶段成功完成,下一步就是进行测试阶段。这个阶段涉及运行自动化测试,以确保代码准确无误,项目运行正常。这个阶段作为保障措施,确保任何可以轻松复制的错误不会发送给最终用户或通过管道传递。
在这个阶段,所有由开发者编写的测试用例(包括但不限于单元测试、集成测试、端到端测试等)都将被测试和检查,以确保它们全部通过,然后才允许当前构建进入部署阶段。
测试阶段对于识别开发者可能忽略的代码问题至关重要。这种反馈对开发者来说很重要,因为它是在问题仍然在他们脑海中新鲜时提供的。如果在测试阶段发生任何失败,它们可以揭示代码中的问题。
部署阶段
在这个阶段之前,管道已创建了一个新的代码或修改的功能版本,它已经通过了所有预定的测试,现在准备部署。
通常,为开发团队建立了多个部署环境,例如产品团队的“预发布”环境、开发团队的“开发”环境以及最终用户的“生产”环境。
根据团队、组织选择的模型,可以建立各种部署环境。采用基于测试和实时观察的敏捷开发模型的团队,通常在将接受修改推送到最终用户的生产环境之前,将代码部署到预发布环境进行进一步的手动测试和审查。
部署管道概述
在本节中,我们将探讨一个部署管道的实际示例。管道是项目复杂性的反映。因此,为每次代码更改配置一个管道将节省团队未来的许多痛苦和重复性任务。
下图清晰地展示了部署管道的示例以及需要执行的不同作业。
当更改推送到 CI/CD 激活的特定分支时,source
阶段被触发,然后它进入构建阶段,使用编译器(如果有)编译代码,或者使用docker build
过程构建项目的镜像。
接下来,测试阶段运行所有必要的和激活的测试周期,例如单元测试、集成测试和端到端测试。
测试成功后,管道进入部署阶段,将代码部署到实时预发布环境进行进一步测试,最终部署到生产环境。
图 12.3 – 部署管道示例
在前面的图中,我们探讨了部署管道的概述、不同阶段以及每个不同阶段内部的情况。在本节中,我们探讨了部署管道及其涉及的不同阶段。接下来,我们将讨论如何使用 GitHub Actions 将我们的 Pinterest 演示部署到 AWS 云。
GitHub Actions 概述
在 CI/CD 的世界中,已经创建了众多工具来自动化构建、测试和部署项目的流程。GitHub Actions 恰好是这些工具之一,并且已经获得了极大的普及。
GitHub Actions 是一个 CI/CD 平台,允许开发者自动化构建、测试和运行部署管道的过程。
GitHub Actions 之所以受欢迎,是因为它直接集成到 GitHub 中,可以配置以创建工作流程,构建和测试您存储库中的每个拉取请求,或将合并的拉取请求部署到生产环境。
关于 GitHub Actions,有许多概念需要学习:不同的术语、概念、使用 GitHub Actions 相较于其他 CI/CD 平台的益处和优势。您可以从官方文档中学习所有这些内容,文档链接为docs.github.com/en/actions/learn-github-actions/understanding-github-actions
。
尽管如此,我们仍将向您展示如何为我们在前几章中开发的 Pinterest 演示项目创建部署管道。
部署企业级项目是繁琐的,需要大量检查以确保频繁的 bug 不会在生产环境中出现。
在部署企业级项目之前,有许多因素需要检查,从 linting、格式化和风格到测试。清单无穷无尽,有时还取决于您的团队以及开发工作流程的设置。
在下一节中,我们将探讨项目在部署到生产之前需要通过的不同阶段或检查。
部署管道中的作业
部署管道中的作业因项目而异,也因团队而异。在以下子节中,我们将探讨一些您可以在部署管道中构建的重要作业,以确保在部署到生产之前完全检查您的项目。
Linting(Eslint、Stylelint、Prettier)
Linting 是一个过程,其中 linting 程序会审查特定编程语言或代码库的源代码,以检测任何潜在问题,如错误、bug、风格错误和可疑结构。这有助于识别在编码过程中可能犯下的常见和罕见错误。此外,linting 会遍历您的源代码,以识别任何格式差异,检查是否符合编码标准和约定,并指出程序中可能存在的潜在逻辑错误。
此外,linting 通过在整个开发团队中创建一致的代码库,有助于提高团队的开发者体验。
我们将在我们的管道中设置 linting,以确保在部署管道中的风格指南、格式和命名约定之间的一致性,如下所示:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: |
yarn
yarn lint
Lighthouse 预算检查
Lighthouse 是一个开源的自动化工具,用于提高网页质量。此工具允许您对网页(公开或需要认证)运行测试。它帮助开发者审计网页的性能、可访问性、SEO 等方面。
此外,您可以通过将其添加到您的部署管道中来自动化此过程,在将网页部署给用户之前测试网页的性能。这个过程允许企业级应用程序开发者自动化在实时测试应用程序性能的过程。
该操作允许我们设置多个选项,包括以下内容:
-
对多个路径进行测试
-
提供预算路径
-
运行次数(CI 应该审计 URL 的次数)
我们将设置 Lighthouse 机器人(github.com/ebidel/lighthousebot
)与 GitHub Actions 一起,以审计我们的部署和用户体验(UX)完整性。
这里是 Lighthouse 作业设置的片段:
lighthouse:
runs-on: ubuntu-latest
needs: deploy
steps:
- uses: actions/checkout@v2
- name: Run Lighthouse on urls and validate with
lighthouserc
uses: treosh/lighthouse-ci-action@v7
with:
urls: |
${{ needs.deploy.outputs.preview-url }}
budgetPath: ./budget.json
runs: 3
上述代码用于将 Lighthouse 插件添加到部署管道中,该插件使用actions/checkout
插件来访问仓库工作区,以便访问包含 Lighthouse 应执行的任务的budget.json
文件。这个 Lighthouse GitHub Action 对于依赖于谷歌搜索流量的网站来说非常有用。如果不尽早解决,随着网站的发展,捆绑包的大小通常会变得更大,从而导致 Lighthouse 评分降低。此操作允许我们监控每次提交中的任何差异。
自动化软件测试
软件测试是确定您的企业应用程序状态并确保其符合项目要求的一个关键因素。正如前几章所探讨的,我们已经开发了三种主要的软件测试类型,并实践了如何为我们的 Pinterest 应用程序演示创建更好的测试套件。
因此,我们将设置三个任务来运行我们演示应用程序的整个软件测试套件。在我们的演示中,软件测试套件包括以下内容:
-
单元测试
-
集成(组件)测试
-
端到端测试(E2e testing)
作业将运行这些测试中的每一个,并相应地做出反应。如果测试失败,它将暂停部署并通过 Slack 通知或电子邮件向开发团队报告问题。否则,如果测试通过,它将继续到下一阶段。
这里是所有测试设置的片段:
unit_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: |
yarn
yarn test:unit
component_test:
runs-on: ubuntu-latest
needs: unit_test
steps:
- uses: actions/checkout@v3
- run: |
yarn
yarn test:component
e2e_test:
runs-on: ubuntu-latest
needs: component_test
steps:
- uses: actions/checkout@v3
- run: |
yarn
yarn test:e2e
脚本设置了测试阶段,其中包含运行不同测试周期(如单元测试、集成测试和端到端测试)的脚本。在每个管道任务中,我们使用actions/checkout
来检出工作区仓库,然后运行yarn
命令安装所有包,然后再运行test
命令。
净土部署(Netlify deployment for staging)
Netlify 是一个综合平台,它使您能够集成您首选的工具和 API,以构建最快的网站、商店和适用于可组合网络的 应用程序。它允许您使用任何前端框架构建、预览并将内容部署到全球网络,从 Git 开始。
您可以将企业应用程序部署到多个环境,例如开发、预发布和生产,具体取决于您的团队设置。
GitHub Actions 允许您为这些不同的环境创建多个工作流程。在每个环境中,您可以设置不同的作业来执行。例如,您可能不想再次检查 Lighthouse 性能,因为当部署到预发布环境时已经测试过了。
这里有一个设置作业以部署到 Netlify 的代码片段:
deploy:
runs-on: ubuntu-latest
needs: e2e_test
steps:
- uses: actions/checkout@v2
- name: Deploy to Netlify
uses: nwtgck/actions-netlify@v1.2
id: deploy-to-netlify
with:
publish-dir: './dist'
production-branch: master
github-token: ${{ secrets.GITHUB_TOKEN }}
deploy-message: "Deploy from GitHub Actions"
enable-pull-request-comment: false
enable-commit-comment: true
overwrites-pull-request-comment: true
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN
}}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
timeout-minutes: 1
outputs:
preview-url:
${{ steps.deploy-to-netlify.outputs.deploy-url }}
之前的脚本使用 Netlify GitHub Action 插件将 Vue.js 3 应用程序部署到 Netlify。它需要一个 Netlify 令牌和密钥(这些在 GitHub 仓库的“密钥”部分添加),最后,在部署后提供预览 URL。
在下一节中,我们将使用 GitHub Actions 创建一个完整的部署管道,以在将应用程序推送到主分支之前进行更多手动测试,这将触发生产部署管道。
使用 GitHub Actions 创建部署管道
要使用 GitHub Actions 创建部署管道,我们需要为每个管道配置环境创建配置文件。
按照以下步骤创建您的第一个使用 GitHub Actions 的预发布环境部署管道。
打开 Pinterest 示例应用程序或从本章的官方仓库克隆它,以查看 GitHub Actions 的完整设置。
如果您正在跟随,请创建一个名为 staging.yml
的新文件,位于 .github/workflows
文件夹内。
重要的一点是,文件夹的名称必须完全相同,GitHub Actions 才能在您向仓库推送时选择配置。
预发布环境的管道
打开 staging.yml
文件,并添加以下脚本以创建预发布环境的部署管道:
on:
pull_request:
branches:
- chapter-12
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: |
yarn
yarn lint
unit_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: |
yarn
yarn test:unit
component_test:
runs-on: ubuntu-latest
needs: unit_test
steps:
- uses: actions/checkout@v3
- run: |
yarn
yarn test:component
e2e_test:
runs-on: ubuntu-latest
needs: component_test
steps:
- uses: actions/checkout@v3
- run: |
yarn
yarn test:e2e
deploy:
runs-on: ubuntu-latest
needs: e2e_test
steps:
- uses: actions/checkout@v2
- name: Deploy to Netlify
uses: nwtgck/actions-netlify@v1.2
id: deploy-to-netlify
with:
publish-dir: './dist'
production-branch: master
github-token: ${{ secrets.GITHUB_TOKEN }}
deploy-message: "Deploy from GitHub Actions"
enable-pull-request-comment: false
enable-commit-comment: true
overwrites-pull-request-comment: true
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
timeout-minutes: 1
outputs:
preview-url:
${{ steps.deploy-to-netlify.outputs.deploy-url }}
lighthouse:
runs-on: ubuntu-latest
needs: deploy
steps:
- uses: actions/checkout@v2
- name: Run Lighthouse on urls and validate with
lighthouserc
uses: treosh/lighthouse-ci-action@v7
with:
urls: |
${{ needs.deploy.outputs.preview-url }}
budgetPath: ./budget.json
runs: 3
在本节中,我们详细讨论了如何自动化部署企业应用程序的过程。我们学习了部署管道以及如何使用 GitHub Actions 创建一个。在下一节中,我们将学习如何通过自动化部署管道的过程将我们的应用程序部署到 AWS(亚马逊网络服务)生产环境。
部署到 AWS
在本节中,我们将使用 GitHub Actions 和 AWS App Runner 实现对 Vue.js 3 应用程序的持续部署。
在将应用程序推送到生产之前,经过彻底检查预发布应用程序以确保其满足所有要求后,此过程可以手动触发。然而,它也可以自动化,以便在预发布完成后立即发生。
在本演示中,我们将创建部署管道以使用 AWS App Runner 将应用程序部署到 AWS 生产服务器,并立即自动化该过程。
重要提示
建议手动触发部署过程,这样在将新版本部署到生产环境之前,可以手动检查所有在预发布环境中的要求。
要部署到 AWS,您需要一个 AWS 账户和一个具有适当权限的 AWS IAM 账户。在本节中,我们探讨了如何创建管道并将我们的项目部署到 AWS。在下一节中,我们将继续使用 Docker 和上一章中创建的 Dockerfile 部署我们的项目。
使用 Docker
在 第七章**,Docker 化 Vue 3 应用程序 中,我们讨论了 Docker 化 Vue.js 3 项目时涉及的细节。此外,我们还学习了 Docker 化和部署企业级 Vue.js 3 网络应用程序的最佳实践和行业标准。
我们将使用为该项目创建的 Dockerfile,这样我们就可以将其作为容器化应用程序在 AWS 基础设施上运行。
更新我们之前创建的 Dockerfile,使用以下代码片段:
# Use the official Node.js 14 Alpine image from https://hub.docker.com/_/node.
# Using an image with specific version tags allows deterministic builds.
FROM node:fermium-alpine3.14 AS builder
# Create and change to the app directory.
WORKDIR /app
# Copy important root files to the builder image.
COPY package*.json ./
# Install production dependencies.
RUN npm install
# Copy the Vue 3 source to the container image.
COPY . .
# build app for production with minification
RUN npm run build
# Production stage
FROM nginx:stable-alpine as production-stage
# Copy the Vue 3 source to the container image.
COPY --from=builder /app/dist /usr/share/nginx/html
VOLUME /app/node_modules
EXPOSE 80
# Run the Vue service on container startup.
CMD ["nginx", "-g", "daemon off;"]
这是我们上一章中用来将我们的项目 Docker 化的相同 Dockerfile。您可以参考该章节了解如何将 Vue.js 3 应用程序 Docker 化的更多信息。
基础镜像将是 nginx:stable-alpine
,应用程序将监听端口 80
。有关逐步 Docker 化指南,请参阅 Vue.js 的官方文档,链接为 v2.vuejs.org/v2/cookbook/dockerize-vuejs-app.html?redirect=true
。
由于我们已经定义了 docker-compose.yml
文件,您可以使用以下 Docker Compose 命令测试应用程序容器(*第七章**,Docker 化 Vue 3 应用程序):
docker build -t vue-paper-dashboard .
docker run -dt --name vue-paper-dashboard -p 8080:80 vue-paper-dashboard:latest
成功运行应用程序容器后,您应该能够通过与 npm run dev
命令相同的地址访问仪表板。接下来,让我们配置 AWS 资源。
配置 AWS 资源
我们将使用 GitHub Actions 将我们的 Vue.js 3 应用程序持续部署到 AWS,因此我们需要在 AWS 上创建一个 IAM 账户和一个用户管理的角色,这些将在下一步中使用。
创建 IAM 账户
此 IAM 账户将由 GitHub Actions 代理使用。访问控制台 us-east-1.console.aws.amazon.com/iamv2/home#/home
并在 AWS 上创建一个 IAM 账户和一个用户管理的角色。
图 12.4 – 在 IAM 中为 GitHub Actions 创建新用户
点击 下一步:权限 选项,然后点击 创建用户 按钮。最后,点击 下载.csv 下载新用户的凭证并将其保存在某处——我们很快就会用到它。
为 IAM 用户创建角色
在本演示中,我们将创建一个名为 app-runner-service-role
的新角色,并附加 AWSAppRunnerServicePolicyForECRAccess
策略。此角色将由 AWS App Runner 服务使用,以便它们能够访问弹性容器注册(ECR)以管理我们的 Docker 镜像。
要创建服务角色,请按照以下步骤操作:
-
点击 角色 菜单。
-
点击 创建角色 按钮,选择 自定义可信策略 选项,并输入以下 JSON:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "build.apprunner.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
此代码片段是一个 JSON 文件,用于创建用于部署到亚马逊 弹性容器服务(ECS)的自定义可信策略。
在成功创建 app-runner-service-role
后,如图所示,请确保复制并记录亚马逊资源名称(ARN),因为它将在以后使用。
图 12.5 – 创建 app-runner-service-role
在本节中,我们逐步完成了创建 app-runner-service-role
和亚马逊 IAM 为 ECS 部署的权限的过程。在下一节中,我们将创建 IAM 用户的策略。
为 IAM 用户创建策略
导航到 github-vue-pinterest-demo
IAM 权限,并附加以下内联策略,这将授予 GitHub Actions(通过 IAM 角色)与 ECR 和 App Runner 一起工作的权限:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "apprunner:*",
"Resource": "*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": [
"iam:PassRole",
"iam:CreateServiceLinkedRole"
],
"Resource": "*"
},
{
"Sid": "VisualEditor2",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "{ENTER_YOUR_SERVICE_ROLE_ARN_HERE}"
},
{
"Sid": "VisualEditor3",
"Effect": "Allow",
"Action": [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:BatchCheckLayerAvailability",
"ecr:PutImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload",
"ecr:GetAuthorizationToken"
],
"Resource": "*"
}
]
}
通过更新 IAM 策略使其更具体(即,ARN 特定而不是通配符),可以通过创建策略并将其附加到 IAM 用户来解决与前面 JSON 相关的安全问题。
创建 ECR 私有仓库
我们几乎完成了;最后一步是在 ECR 上创建一个私有仓库来管理我们的 Docker 镜像。在提供的框中添加您选择的仓库名称,然后点击 创建 按钮,保留其余选项为默认值。
图 12.6 – 创建名为 vue-pinterest-demo 的私有仓库
在创建您的 ECR 实例后,前往您的 GitHub 仓库并添加部署应用程序所需的所有密钥和环境变量。
在本节中,我们创建了 ECR 实例并将我们的密钥添加到我们的 GitHub 仓库,以及所有所需的环境变量。在下一节中,我们将探讨如何使用 GitHub Actions 自动化部署过程。
使用 GitHub Actions
在本节中,我们将使用 GitHub Actions 自动化将应用程序部署到亚马逊 ECR 的过程。我们首先将添加亚马逊密钥到我们的 GitHub 仓库。按照以下步骤添加您的密钥:
-
在您的 GitHub 仓库中,转到 设置 | 密钥 | 操作 并添加所有必要的密钥变量。
-
打开您在创建 IAM 用户时下载的
new_user_credentials.csv
文件。 -
复制AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY的值,并将它们粘贴到你的 GitHub Secrets 中作为环境变量。
-
此外,你可以使用
us-east-1 for AWS_REGION
和你的app-runner-service-role
的 ARN 作为ROLE_ARN
。
在成功添加你的凭证后,在下一节中,我们将创建一个流水线,用于将企业项目部署到 AWS App Runner,并使用 ECR 来管理我们的 Docker 镜像。
生产环境流水线
打开production.yml
文件,并添加以下脚本以创建生产环境的部署流水线:
name: PRODUCTION - Deploy container to AWS App Runner
on:
push:
branches:
- master
workflow_dispatch: # Allow manual invocation of the
# workflow
env:
ENVIRONMENT_NAME: production
ECR_REPOSITORY_NAME: vue-pinterest-demo
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
persist-credentials: false
- name: Configure AWS credentials
id: aws-credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key:
${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Login to Amazon ECR
id: ecr-login
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push image to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.ecr-login.outputs.registry }}
ECR_REPOSITORY: ${{ env.ECR_REPOSITORY_NAME }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t
$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push
$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
echo "::set-output name=
image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
- name: Deploy to App Runner
id: deploy-app
uses: awslabs/amazon-app-runner-deploy@main
with:
service: erp-app-${{ env.ENVIRONMENT_NAME }}
image: ${{ steps.build-image.outputs.image }}
access-role-arn: ${{ secrets.ROLE_ARN }}
region: ${{ secrets.AWS_REGION }}
cpu : 1
memory : 2
port: 80
wait-for-service-stability: false
- name: App Runner output
run: echo "App runner output
${{ steps.deploy-app.outputs.service-id }}"
如果一切顺利,导航到 App Runner 服务控制台;将会有一个名为你指定的服务。你可以点击默认域名来预览你的应用或设置自定义域名。
图 12.7 – Pinterest 演示应用的预览
摘要
在本章中,我们学习了如何将 Vue.js 3 项目部署到 AWS 云,以及部署到 AWS 的一些最佳实践。我们通过探索部署流水线、展示不同的部署阶段以及如何配置每个阶段以执行特定任务,探讨了持续集成和持续交付。我们还检查了每个阶段的表现以及如何部署到预发布环境。
此外,我们还探讨了不同的部署选项以及如何使用 Amazon ECR 和 Docker 进行部署。我们通过实际操作学习了如何创建账户并设置 Amazon ECR 与 Docker,最后,我们实现了使用 CI/CD、Docker、Amazon ECR 和 GitHub Actions 的自动化部署。
在下一章中,我们将探讨 Nuxt.js 的 definitive guide。你将学习 Nuxt.js 的细节以及如何使用 Vue.js 3 构建和交付企业级 SSR 项目。我们还将探讨 Gridsome 的 definitive guide,你将学习 Gridsome 的细节以及如何使用 Vue.js 3 构建和交付企业级 CSR 项目。
第十三章:高级 Vue.js 框架
在上一章中,我们探讨了如何将 Vue.js 3 项目部署到 AWS 云。我们学习了部署到 AWS 的最佳实践。此外,我们还学习了企业公司如何部署他们的企业级 Vue 应用程序。
本章将探讨 Nuxt.js 的 definitive guide。我们将学习 Nuxt.js 的细节以及如何使用 Vue.js 3 构建和交付企业级 SSR 项目。此外,我们还将探索 Gridsome 的 definitive guide,你将学习 Gridsome 的细节以及如何使用 Vue.js 3 构建和交付企业级 CSR 项目。
在本章中,我们将涵盖以下关键主题:
-
Vue 框架简介
-
顶级 Vue 框架
-
理解 Nuxt.js 及其工作原理
-
Nuxt.js 的优势
-
创建 Nuxt.js 应用
-
什么是 Gridsome?
-
Gridsome 用途是什么?
-
Gridsome 是如何工作的?
-
Gridsome 的优势
-
创建 Gridsome 应用
技术要求
要开始本章的学习,你应该阅读第十二章,部署企业级 Vue.js 3,在那里我们学习了如何将 Vue.js 3 项目部署到 AWS 云以及部署到 AWS 的一些最佳实践。此外,我们还探讨了不同的部署选项,并掌握了将我们的 Vue.js 3 项目部署到 AWS 的最佳实践。在本章中,我们将大量依赖该章节的知识来学习更高级的 Vue 框架。
Vue 框架简介
单个框架不能解决前端工程的所有问题,因为它将变得臃肿且非常难以加载。Vue.js 也不例外;有一些问题并不是完全现成的 Vue.js。此外,它将需要更复杂的配置,并且可能导致将一些功能直接实现到 Vue.js 中会浪费开发时间。
开发者的迫切需求使 Vue.js 成为所有框架的框架。在近年来,Vue.js 框架发布后,我们注意到有相当数量的其他框架是基于 Vue.js 创建的。
这些框架提供不同的选项以满足开发需求,如服务器端渲染(SSR)、静态站点生成器(SSGs)、渐进式网页应用(PWAs)等。
根据其目的和能力,框架可以分为四个不同的组。以下包括以下内容:
-
Vue.js UI 框架:这些提供工具来创建现代、响应式的网站
-
移动框架:这些有助于构建混合移动网页应用
-
静态站点框架:这些生成静态网站
-
SSR 框架:这些用于创建 SSR 应用
在本节中,我们将探讨最顶级的 Vue.js 框架,并在本章的后面部分,我们将更详细地探讨前两个框架。
顶级 Vue 框架
当开发者创建通用 Vue 应用时,有各种 Vue.js 框架可供利用。让我们在以下小节中探索其中的一些。
Vue UI 框架
创建一个吸引人且用户友好的 UI 是前端开发的关键部分。如果界面的设计不吸引人或者不易使用,将很难保持一个稳定的用户基础。
在为大型企业产品设计 UI 时,利用提供预制 Vue 组件和元素的 Vue UI 框架是有益的。
Vue 的顶级 UI 组件框架如下:
-
Bootstrap Vue
-
Vuetify
-
Quasar Framework
-
Vue Material
您可以使用此npm 趋势链接比较每个框架的流行度:npmtrends.com/bootstrap-vue-vs-quasar-vs-vue-material-vs-vuetify
。
移动框架
根据 Monocubed 的说法(www.monocubed.com/blog/why-vuejs-gaining-popularity/
),多年来,Vue.js 在混合和健壮的 Web 应用开发中,包括混合移动开发方面获得了人气。
然而,使用这里列出的某些移动框架以及其他的移动开发库和框架,也可以使用 Vue.js 实现混合和原生移动开发:
-
Vue Native
-
Vux
-
Mint UI
您可以使用此npm 趋势链接比较每个框架的流行度:npmtrends.com/mint-ui-vs-vue-native-core-vs-vux
。
静态网站框架
一个 SSG 使用模板和原始数据来创建静态 HTML 页面。这种方法的优点是网站每次加载的方式相同,内容不会动态变化,这意味着网页不需要分别编码。
以下是一个用于生成静态网站的 Vue 框架列表:
-
Vue Press
-
Gridsome
-
Nuxt.js
此外,值得注意的是,Nuxt.js 可以用来生成静态网站。然而,这并不是 Nuxt.js 的主要焦点。
您可以使用此npm 趋势链接比较每个框架的流行度:npmtrends.com/gridsome-vs-nuxt-vs-vuepress
。
SSR 框架
根据官方文档(vuejs.org/guide/scaling-up/ssr.html#why-ssr
),SSR 应用具有更好的内容转换速度,更好的 SEO,以及相同的统一语言和声明式组件导向的金属模型,用于开发整个应用程序。
一个在服务器端渲染的 Vye.js 应用程序允许你的应用程序代码在服务器端和客户端上运行,这与仅运行在客户端的 SSGs 形成对比。
以下是一个用于实现 SSRs 的 Vue 框架列表:
-
Nuxt.js
-
Quasar
-
Vite SSR
你可以通过这个 npm 趋势 链接比较每个框架的流行度:npmtrends.com/nuxt-vs-quasar-vs-vite-ssr
。
在本节中,我们探讨了最顶级的 Vue.js 框架及其不同类别。在下一节中,我们将深入探讨 Nuxt.js 并了解其工作原理。
理解 Nuxt.js 及其工作原理
Nuxt.js 是一个基于 Vue.js 的开源框架,为开发者提供了创建前端项目的工具。它旨在使网页开发更加简单和强大,同时也提供了服务器端渲染功能,帮助开发者管理异步数据、中间件和路由的复杂配置:
图 13.1 – 官方 Nuxt.js 标志
Vue.js 应用可以通过一个众所周知的架构来组织,该架构可以用来创建基本或复杂的应用。此外,这种结构有助于提高 Vue.js 应用的开发。
在本节中,我们将了解 Nuxt.js 的不同用例以及为什么你应该考虑切换到 Nuxt.js。
Nuxt.js 的用途
使用 Nuxt.js,你可以在构建应用类型上无拘无束,Nuxt.js 已被用于开发高性能和 SEO 优化的网站。在接下来的子节中,我们将探讨你可以使用 Nuxt.js 构建的最流行的网站类型。
静态生成页面
静态生成页面是不需要任何外部数据源的网站,因为内容已经包含在 HTML 中。Nuxt.js 可以用来创建静态生成页面,如作品集、演示网站或教程页面。
单页应用(SPAs)
单页应用(SPA)是一种前端开发类型,它从外部源检索数据并在用户的设备上显示。许多流行的 JavaScript 框架,如 React.js、Vue.js 和 Angular,都是 SPA 框架,这并不奇怪。
HTML 5 历史应用程序编程接口(API)和位置哈希被用来创建 SPA 路由系统。这种能力允许开发者在不重新加载整个页面的情况下更改网站的 URL。
通用应用
这部分 Nuxt.js 是我最喜欢的,因为我用 Nuxt.js 开发的几乎所有应用都是通用应用。
一个通用应用是一种利用 SSR 在客户端浏览器完全显示页面之前在服务器上获取客户端数据的技巧。
SSR(服务器端渲染)是 Nuxt.js 内置的功能,无需进行繁琐的配置即可激活和启用 Vue.js 中的 SSR。
Nuxt.js 可以解决 Vue.js 中现有的 SSR 问题,这对 SEO 有益,甚至可以扩展以创建一个通用应用程序,允许使用单个代码库为单体应用程序的前端和后端服务。
这些只是你可以使用 Nuxt.js 构建的一些应用程序类别。在下一节中,我们将探讨 Nuxt.js 实际上是如何工作的。
Nuxt.js 是如何工作的?
根据你的设置,Nuxt.js 可以以两种不同的方式运行。如果你启用了 SSR 或使用通用模式,它将以与服务器端框架相同的方式运行。这意味着每次用户访问你的网站时,请求都会在服务器上处理,并且需要一个服务器来渲染和交付页面。
然而,如果启用了客户端渲染或未激活通用模式,内容和页面将使用 JavaScript 在浏览器中渲染。这种方法具有最快的加载时间,在速度和页面性能方面表现良好。
Nuxt.js 的生命周期提供了框架不同部分的概述,它们的执行顺序以及它们是如何协同工作的。此外,它还描述了构建阶段之后发生的事情,即你的应用程序被打包、分割和压缩。
在 Nuxt.js 中,根据是否启用了 SSR(服务器端渲染),主要使用三种动作和方法:
-
nuxtServerInit
动作是在服务器端执行的首个钩子,如果启用了 Vuex 存储。它用于填充存储,并且只有在存储已启用时才会被调用。此外,此钩子还可以用于在服务器上的 Vuex 存储中分派其他动作。 -
validate()
是一个用于验证页面组件动态参数的函数。它在渲染页面组件之前被调用。 -
AsyncData
和Fetch
是用于获取数据并在服务器端(AsyncData
)显示,或者获取数据并在渲染页面之前填充 Vuex 存储的函数(Fetch
)。
这里是一个快速总结,当你访问 Nuxt.js 网站时你的请求是如何处理的。当 Nuxt.js 收到初始页面访问时,它会调用nuxtServerInit
动作来更新存储或分派必要的动作,如果你的存储已启用;否则,Nuxt.js 将忽略nuxtServerInit
并进入下一阶段。
接下来,Nuxt.js 将查找你的nuxt.config.js
文件以查找任何全局中间件并相应地执行它。执行后,它将移动到布局页面并检查任何中间件以执行,最后,它将执行页面的中间件包括页面子组件。
在按顺序执行中间件之后,它将检查路由并使用validate()
函数对参数、查询等进行验证。
如果之前已启用,asyncData
方法随后被用来在服务器端获取和显示数据。之后,使用fetch
方法在客户端填充 Vuex。
到目前为止,页面应该已经包含了显示适当网页所需的所有数据。以下流程图展示了渲染单个页面所需的所有步骤:
图 13.2 – Nuxt.js 生命周期钩子的概述(来源:https://nuxtjs.org/docs/concepts/nuxt-lifecycle/)
官方 Nuxt.js 生命周期(nuxtjs.org/docs/concepts/nuxt-lifecycle
)页面提供了对 Nuxt.js 如何渲染和处理您的页面(无论是服务器端还是客户端)的更详细概述。
经过对 Nuxt.js 内部运作的了解,现在应该很容易理解。在下一节中,让我们调查一下使用 Nuxt.js 为您即将到来的项目带来的优势。
Nuxt.js 的优势
Nuxt.js 的优势不容小觑;您可以通过 SSR 的引入和企业级项目的项目结构来轻松发现其中的一些。
然而,在以下小节中,我们将了解 Nuxt.js 框架的一些优势以及为什么它正变得越来越受欢迎,用于构建启用 SSR 的 Vue 项目。
轻松创建通用应用
使用 Nuxt.js,您可以非常容易地创建 SSR 应用,无需经历配置 Vue 以支持 SSR 的痛苦过程。SSR 功能已经内置在 Nuxt.js 中,并且非常易于使用。
Nuxt.js 提供了两个重要的属性,称为 isServer
和 isClient
,以确定框架在运行时的状态。当检查您的组件是否应在服务器端或客户端渲染时,这可能很有用。
使用静态渲染为您的 Vue 应用带来通用优势
静态生成的网站正日益受到不同框架的青睐,这些框架专注于它们。然而,您可以使用 Nuxt.js 轻松生成静态网站,而无需安装任何额外的框架或工具。
您可以通过使用 nuxt
generate
命令快速创建网站的静态版本,包括 HTML 和路由。
Nuxt.js 允许创建一个强大的通用应用,无需服务器即可利用 SSR 功能,类似于构建静态生成的网站。
自动代码拆分
关注速度和性能的前端开发已成为企业软件的基本部分,Nuxt.js 由于其代码拆分功能而以其卓越的性能脱颖而出。
此功能允许为每个路由分配其自己的 JavaScript 文件,该文件仅包含运行该路由所需的代码。这种构建应用程序的方法有助于减少渲染单个页面时需要加载的代码量,从而减少加载时间。
Webpack 内置的配置在为您的网站创建静态网页时启用代码拆分。
ES6/7 编译
Nuxt.js 默认启用 ES6 和 7,因为 Webpack 和 Babel 已经预构建到其中,用于将最新版本的 JavaScript 转换为旧浏览器可以执行的版本。
Babel 已经配置为将所有的 .vue
文件和脚本标签内的 ES6 代码转换为与所有浏览器兼容的 JavaScript。此功能消除了从开始手动设置和配置浏览器兼容性的需求。
在下一节中,我们将探讨如何创建我们的第一个 Nuxt.js 应用程序,以及使用 Nuxt.js 开发企业级应用程序的实用方法。
创建 Nuxt.js 应用
本节将向您介绍使用 Nuxt.js 开发应用程序的实用方法。在我们深入之前,让我们探讨一些使用 Nuxt.js 开发企业级应用程序时的一些关键概念。
创建 Nuxt 应用
您可以通过不同的方式轻松创建 Nuxt.js 应用程序,但推荐的方式是使用以下任一命令:
Yarn create nuxt-app <project-name>
Or
npm init nuxt-app <project-name>
Or
npx create-nuxt-app <project-name>
接下来,进入创建的项目文件夹,使用以下命令提供您新创建的 Nuxt.js 项目:
cd <project-name>
npm run dev
Or
yarn dev
重要的是您需要将 <project-name>
替换为实际的项目名称。
现在我们已经生成了我们的新 Nuxt.js 项目,让我们探索项目附带的不同文件夹和文件。
理解 Nuxt.js 文件夹结构
当您使用前面提到的任何命令创建新项目时,由于包含的文件夹和文件数量众多,可能会感到有些令人畏惧。在本节中,我们将查看一些属于 Nuxt.js 项目的关键文件夹和文件。此外,这些文件和文件夹是必不可少的,并且必须保持不变,无需任何额外配置。以下图显示了 Nuxt.js 的文件夹结构:
图 13.3 – Nuxt.js 文件夹结构的截图
让我们在以下小节中回顾这个文件夹结构。
.nuxt
当您启动开发服务器时,.nuxt
文件夹将自动创建,并且不可见。这个文件夹也被称为构建目录,包括用于在开发期间提供项目的生成文件和工件。
assets
assets
目录包含所有原始材料,如图片、CSS、SASS 文档、字体等。Webpack 将在页面创建时编译此文件夹中包含的任何文件。
components
这个文件夹类似于 Vue 中的 components
文件夹。它是您所有 Vue 组件的仓库。组件是构成您页面各种组件的文件,可以重用并导入到任何页面、布局或组件中。
layouts
layouts
文件夹是组织应用程序不同页面布局的好地方。它可以用来区分仪表板页面结构和未登录用户的页面结构。这有助于保持应用程序不同部分的有序。
你可以创建不同的结构来对应你应用程序的不同结构,例如不同的侧边栏、菜单、页眉、页脚等。你可以通过 Nuxt.js 布局实现所有这些分离。
middleware
中间件可以定义为在页面或一组页面(布局)渲染之前或之后触发的自定义函数。这些中间件函数可以存储在 Nuxt.js 的 middleware
文件夹中。
中间件在创建仅限会员或启用身份验证的应用程序时非常重要且方便。你可以用它来限制用户访问某些受保护的页面。
plugins
plugins
目录是存储所有在初始化根 Vue 应用程序之前想要运行的 JavaScript 代码的地方。这是添加 Vue 插件和注入函数或常量的地方。
你将大量使用这个文件夹来包含 Nuxt.js 作为模块未包含的不同 Vue 插件。
它的工作原理是在 plugins
文件夹中创建一个 JavaScript 文件,使用 Vue.use()
函数将插件添加到 Vue 实例中,最后将文件添加到 nuxt.config.js
文件中的 plugins
数组中。
static
static
目录是一个特殊的目录,包含了你应用程序中所有不太可能被更改或将被 Nuxt.js 或 Webpack 进一步处理显示的静态文件。
位于 static
文件夹中的任何文件都将由 Nuxt.js 提供,并且可以通过项目的根 URL 访问。这包括 favicon
、robot.txt
等项目。
store
store
文件夹包含了你所有的 Vuex store 文档,并且它被自动划分为模块。Vuex store 包含在包中,但必须在 store
文件夹中创建一个 index.js
文件才能启用并使用。
Nuxt.js 是为了帮助企业级应用程序的开发而设计的,它预装了 Vuex 用于状态管理。这使得创建和管理此类规模的应用程序变得更加容易。
pages
pages
文件夹非常重要,因为它是 Nuxt.js 路由系统的基石。因此,在更新 Nuxt.js 配置之前,它不能被重命名。Nuxt.js 会自动读取 pages
目录下所有的 .vue
文件,并为每个页面创建相应的路由。
pages
目录包含了你应用程序的所有视图和路由,每个页面组件都是一个常规的 Vue 组件,Nuxt.js 会通过添加特殊属性和函数自动将其转换为路由,以使应用程序的开发变得顺畅和直接。
在下一节中,我们将探讨 Nuxt.js 如何自动将pages
文件夹中的.vue
文件转换为路由。
Nuxt.js 页面和路由系统
Nuxt.js 通过允许用户在pages
文件夹中创建目录和文件来简化路由过程,这将根据目录结构自动生成一个路由器文件。
例如,如果你在目录中有posts.vue
文件,它将自动转换为路由,然后你可以在浏览器中访问该路由以查看Posts
页面的内容。
这种路由系统允许你通过创建文件和文件夹来简单地建立三个不同的路由。让我们更详细地看看这些路由类型。
我们将探讨 Nuxt.js 支持的不同的路由类型,并查看每种路由类型在 Nuxt.js 中的使用方式。
基本路由
路由是一个将请求路由或定向到处理它们的代码的过程。这些请求可以以 URL 的形式出现,并被重定向到适当的处理器。这可能是一个简单的过程,因为它不需要额外的配置即可运行。例如,/about
、/contact
、/posts
等。要设置基本路由系统,pages
目录应按以下方式组织:
pages/
—| about.vue
—| contact.vue
—| posts.vue
Nuxt 将自动生成一个类似于以下的路由器文件:
router: {
routes: [
{
name: 'posts',
path: '/posts',
component: 'pages/posts.vue'
},
{
name: 'about',
path: '/about',
component: 'pages/about'
},
{
name: 'contact',
path: '/contact',
component: 'pages/contact'
},
]
}
之前的代码片段是由 Nuxt.js 自动生成的,不可编辑,因为所有内容都已根据pages
目录中的文件夹结构正确路由。
嵌套路由
嵌套路由是嵌套在父路由中的路由。这种类型的路由用于创建更详细的多级路由。
使用 Nuxt.js,你可以通过创建父文件夹并将所有路由文件放置在该文件夹中来轻松创建嵌套路由。请看以下文件夹结构:
pages/
--| dashboard/
-----| portfolios.vue
-----| settings.vue
--| dashboard.vue
--| posts.vue
--| contact.vue
--| index.vue
在前面的代码中,我们在之前显示的目录结构中创建了一个与仪表板同名的新的文件和文件夹。之后,我们将portfolios.vue
和settings.vue
文件作为子项放置在仪表板文件夹中。
这种简单的文件夹组织结构将创建一个具有以下路由的路由器:
router: {
routes: [
{
name: 'index',
path: '/',
component: 'pages/index.vue'
},
{
name: 'posts',
path: '/posts',
component: 'pages/posts'
},
{
name: 'contact',
path: '/contact',
component: 'pages/contact'
},
{
name: 'dashboard',
path: '/dashboard',
component: 'pages/dashboard.vue',
children: [
{
name: 'dashboard-portfolios',
path: '/dashboard/portfolios',
component: 'pages/dashboard/portfolios.vue'
},
{
name: 'dashboard-settings',
path: '/dashboard/settings',
component: 'pages/dashboard/settings.vue'
}
]
}
]
}
在 Vue.js 中,嵌套路由是手动创建的,并在index.js
路由文件内部注册,当为企业应用程序创建许多路由时,这很容易变得复杂,但使用 Nuxt.js,它变得非常简单且易于创建文件和嵌套文件夹。
动态路由
动态路由可以通过未定义的路由名称生成,这可能是由于 API 调用,或者因为你不想不断创建相同的页面。这些路由是从应用程序中的数据中获取的变量(如名称或 ID)生成的。
为了使路由动态化,你必须在 .vue
文件或目录名的末尾添加一个下划线。你可以命名文件或目录,但必须包含一个下划线,以便使其动态化。
例如,如果你在 pages
目录中定义了一个 _slug.vue
文件,你可以使用 params.slug
对象访问其值。
在构建博客应用时使用动态路由是有优势的;例如,当用户要选择的阅读的帖子 ID 或 slug 未知时。然而,使用动态路由,可以获取帖子的 ID/slug 并显示相应的帖子。
通过一个示例,我们将演示如何创建一个动态路由:
pages/
--| posts/
-----| _slug.vue
-----| index.vue
--| services.vue
--| contact.vue
--| index.vue
在这里,我们在 slug
中添加一个下划线,以创建页面的动态路由,以及一个带有字符串参数的父路由及其相应的子路由。这种页面结构将在文件中生成以下路由:
{
name: 'index',
path: '/,
component: 'pages/index.vue'
},
{
name: 'contact',
path: '/contact',
component: 'pages/contact.vue'
},
{
name: 'services',
path: '/services',
component: 'pages/services.vue'
},
{
name: 'posts',
path: '/posts',
component: 'pages/posts/index.vue',
children: [
{
name: 'posts-slug,
path: '/posts/:slug,
component: 'pages/posts/_slug.vue'
}
]
}
]
}
现在我们已经探讨了 Nuxt.js 框架内置的不同路由系统,你对 Nuxt.js 的工作原理有了扎实的了解,并可以使用它开始构建企业级通用应用程序。
在下一节中,我们将探索 Gridsome,了解 Gridsome 的细节以及如何使用 Vue.js 3 构建和交付企业级 CSR 项目。
Gridsome 是什么?
Gridsome 是一个强大的静态网站生成器。它由 Vue.js 驱动,用于构建默认快速生成的网站和应用。它也是一个用于构建网站和应用的 Jamstack 框架,能够提供更好的性能、更高的安全性和更低的扩展成本。
Gridsome 致力于通过预渲染文件并直接从 CDN 提供服务来实现 Jamstack 方法,以构建快速和安全的网站和应用——从而提高应用程序的速度,并消除管理或运行 Web 服务器的需求。
Jamstack 是一种将 Web 体验层从数据和业务逻辑中分离出来的架构方法,提高了灵活性、可伸缩性、性能和可维护性。
Gridsome 用作什么?
目前,Gridsome 不支持 SSR,但专注于创建更快的网站和应用。在接下来的小节中,我们将探讨你可以使用 Gridsome 构建的最流行的网站类型。
静态生成的页面
这些类型的网站不需要任何外部数据源——内容已经嵌入到 HTML 中。你可以使用 Gridsome 创建静态生成的页面,例如作品集、演示网站或教程页面,这些页面可以包含不同的数据源和更高的性能。
单页应用(SPAs)
利用外部 API 的动态数据并在客户端显示的开发方法被称为创建单页应用(SPA)。JavaScript 框架的大多数,如 React.js、Vue.js 和 Angular,都是 SPA 框架,这并不令人意外。
HTML 5 历史 API 和位置哈希被用来创建 SPA 路由系统。这种能力允许开发者在不重新加载整个页面的情况下更改网站的 URL。
在下一节中,我们将探讨 Gridsome 的工作原理以及如何使用它来创建一个静态渲染的网站。
Gridsome 是如何工作的?
Gridsome 是一个 Jamstack 框架;因此,它使用基于客户端 JavaScript、可重用 API 和预构建标记的现代 Web 开发架构。
它通过生成静态、SEO 优化的 HTML 标记来实现,这些标记在浏览器加载后会被转换为动态 DOM。这个简单功能使得 Gridsome 成为构建静态和动态网站的首选 Jamstack 框架。
内部,Gridsome 为你创建的每个页面构建一个 .html
文件和一个 .json
文件,并在首次页面加载后加载 .json
文件以预取和加载下一页的数据。此外,它还为每个页面构建一个 .js
包以利用代码拆分。
此外,源插件可以从本地文件或外部 API 获取数据,并将其存储在本地数据库中。统一的 GraphQL 数据层允许你从数据库中访问所需的数据,并在你的 Vue 组件中使用它。
以下图表解释了 Gridsome 的内部工作原理以及数据是如何传递和处理的,直到它到达你的 Vue 组件:
图 13.4 – Gridsome 的工作概述(来源:https://gridsome.org/docs/how-it-works/)
运行 Gridsome 有两种方式:
-
gridsome develop
:此命令启动本地开发服务器并监视更改 -
gridsome build
:此命令生成生产就绪的静态文件
你可以从官方文档 gridsome.org/docs/how-it-works/
中了解更多关于每个命令的工作原理以及它们如何生成这些静态页面。
接下来,让我们看看使用 Gridsome 的一些好处。
Gridsome 的好处
使用 Gridsome 的好处非常多,这取决于你打算构建的网站和应用类型。对于静态生成的网站,Gridsome 默认注重速度,并且拥有良好的项目结构,适用于构建企业级静态网站和应用。
以下小节描述了使用 Gridsome 的一些好处。
无服务器和静态生成
Gridsome 采用 Jamstack 方法构建网站,这提供了更好的性能和更高的安全性,并减少了你的开发栈中的成本和复杂性。Gridsome 使用 Jamstack 哲学生成静态页面和网站,最终产品是一个包含静态 HTML 文件的文件夹,这些文件可以部署到任何地方。
易于安装和使用
Gridsome 非常容易安装,并且使用起来简单。它附带了一个 CLI(命令行工具),它可以帮助你轻松创建 Gridsome 项目。
你可以通过运行以下命令来安装 Gridsome:
npm install –global @gridsome/cli
一旦 CLI 被安装,你就可以使用它来创建你想要的任意数量的 Gridsome 项目。
有序的项目结构
企业项目的一个挑战是项目结构,如前几章所述。Gridsome 通过帮助你使用预测性项目结构来解决这个问题。
我们将在理解 Gridsome 文件夹结构部分中介绍构成 Gridsome 项目的关键文件和文件夹。
自动路由
自动路由是前端开发行业中的一个非常重要的特性,从 Nuxt 开始,当你向pages
文件夹添加文件和文件夹时,路由会自动生成。
Gridsome 也通过自动路由功能使得创建路由变得非常简单。每当在src/pages
文件夹中有新文件或新文件夹时,路由会自动生成。这与之前讨论的 Nuxt 的工作方式类似。
代码拆分/预取
通过在 Gridsome 中集成代码拆分和预取,Gridsome 网站的导航变得超级快,因为你在点击之前,任何链接都已经预先获取。
此外,代码拆分功能有助于提高 Gridsome 网站的性能和加载速度,因为它允许用户只加载请求页面上需要的 JavaScript,其他内容按需加载。
支持 Markdown 文件
在 Gridsome 中使用 Markdown 是最容易实现内容自动管理的方式。你可以以博客文章、文章或其他任何形式创建内容,这些内容以自己的.md
(Markdown 扩展名)文件描述。这些 Markdown 文件将被分组并由 Gridsome 消费以生成单独的 HTML 文件。
现在我们已经看到了使用 Gridsome 的好处,接下来我们将看到如何在下一节中创建一个 Gridsome 应用。
创建 Gridsome 应用
本节将介绍使用 Gridsome 开发应用程序的实用方法。在我们深入之前,让我们探索一些使用 Gridsome 开发企业级应用程序的关键概念。
创建 Gridsome 应用
你可以通过多种方式轻松创建 Gridsome 应用,但推荐的方式是使用以下任意一条命令:
Yarn global add @gridsome/cli
Or
npm install - -global @gridsome/cli
接下来,进入创建的 Gridsome 项目,使用以下命令来运行你新创建的 Gridsome 项目:
gridsome create <project-name>
cd <project-name>
gridsome develop
重要的是,你需要将<project-name>
替换为实际的项目名称。
现在我们已经为我们生成了新的 Gridsome 项目,让我们探索项目附带的不同文件夹和文件。
理解 Gridsome 文件夹结构
当您使用任何前面的命令创建新项目时,它将附带许多令人眼花缭乱的文件夹和文件。但在这个部分,我们将探索新创建的 Gridsome 项目中的重要文件夹和文件。
此外,一些这些文件和文件夹至关重要,需要某些文件夹名称和文件名保持不变,无需额外配置。以下是 Gridsome 文件夹结构的样子:
图 13.5 – Gridsome 文件夹结构
让我们在以下小节中回顾一些重要的文件夹。
pages
pages
文件夹是 Gridsome 中最重要的文件夹之一,因为它负责自动路由,并且与 Nuxt 的工作方式完全相同,除了每个页面都将生成静态的,并且有自己的index.html
文件和标记。
在 Gridsome 中创建页面有两种选项:
- 基于文件的页面:
当您使用单个文件组件(单个.vue
文件)创建页面时,您应该使用文件系统。src/pages
目录中找到的任何单个文件组件都将自动转换为它自己的路由或 URL。文件位置用于生成 URL,您可以在以下示例中看到它:
pages/
—| about.vue
—| contact.vue
—| posts.vue
上述页面结构将被转换为以下结构:
`pages/about.vue` becomes `/about`
`pages/contact.vue` becomes `/contact`
`pages/posts.vue` becomes `/posts`
接下来,让我们探索 Gridsome 中创建页面的第二种选项,即程序化页面。
- 程序化页面:
位于gridsome.server.js
文件中的createPages
钩子可以用于生成程序化页面。如果您需要手动从外部 API 创建页面而不使用 Gridsome 内置的 GraphQL 数据层,这将非常有用。
您可以通过实现createPages
钩子来程序化创建一个页面,如下面的代码块所示:
module.exports = function (api) {
api.createPages(({ createPage }) => {
createPage({
path: '/my-new-page',
component: './src/templates/MyNewPage.vue'
})
})
}
您也可以使用之前展示的相同createPages
钩子来创建动态页面。
模板
Gridsome 使用模板来显示节点或集合的单个页面。当您创建一个模板文件时,Gridsome 会尝试定位一个与集合同名的文件,如果模板配置中没有指定。通常,模板会被映射到集合以显示信息。
下面是一个使用模板从 GraphQL 查询中显示帖子标题的示例:
<!-- src/templates/Post.vue -->
<template>
<Layout>
<h1 v-html="$page.post.title" />
</Layout>
</template>
<page-query>
query ($id: ID!) {
post(id: $id) {
title
}
}
</page-query>
模板在 Gridsome 中非常重要,因为它们是数据页面在其自己的 URL 中展示的方式。您可以从gridsome.org/docs/templates/
的文档中了解更多高级模板用法。
布局
布局是 Vue 组件,用于在页面和模板中包裹内容。您可以使用布局为您的网站创建不同的结构。它的工作方式与 Nuxt.js 中的布局完全相同。
通常,布局在页面中的使用方式如下:
<template>
<Layout>
<h1>About us</h1>
</Layout>
</template>
<script>
import Layout from '~/layouts/Default.vue'
export default {
components: {
Layout
}
}
</script>
在 Gridsome 中,布局文件是全局的,在使用之前不需要导入。Gridsome 项目附带更多重要的文件和文件夹,随着你的项目增长,你将很快发现你添加了更多的文件和文件夹。
然而,之前提到的文件夹是重要的文件夹,当你在项目中添加更多文件和文件夹时,它们的名称不应更改。
摘要
在本章中,我们探讨了关于 Nuxt.js 的每一个重要细节。你学习了 Nuxt.js 的细节以及如何使用 Vue.js 3 构建和交付企业级 SSR 项目。此外,我们还探讨了 Gridsome,这是一个用于构建静态生成网站的超级快速 Jamstack 框架。
我们还介绍了使用 Nuxt 和 Gridsome 的好处。然后,我们看到了如何使用这两个框架创建一个应用。最后,我们探讨了 Nuxt 和 Gridsome 的文件夹结构。