Vue 与 GraphQL 应用构建指南(二)

原文:zh.annas-archive.org/md5/60CC414A1AE322EC97E6A0F8A5BBE3AD

译者:飞龙

协议:CC BY-NC-SA 4.0

第三章:设置我们的聊天应用程序 - AWS Amplify 环境和 GraphQL

自从 Facebook 在 2012 年推出 GraphQL 以来,它就像飓风一样席卷了网络。大公司开始采用它,而中小型公司也看到了这种基于查询的 API 的潜力。

一开始看起来很奇怪,但随着您开始阅读和体验更多,您就不想再使用 REST API 了。简单性和数据获取能力使前端开发人员的生活变得更轻松,因为他们可以只获取他们想要的内容,而不必受限于只提供单个信息片段的端点。

这是一个漫长的配方的开始,所有的配方都将形成一个完整的聊天应用程序,但您可以在不需要编写整个章节的情况下,在配方中学习有关 GraphQL 和 AWS Amplify 的知识。

在本章中,我们将学习更多关于 AWS Amplify 环境和 GraphQL 的知识,以及如何将其添加到我们的应用程序并使其可用作通信驱动程序。

在本章中,我们将涵盖以下配方:

  • 创建您的 AWS Amplify 环境

  • 创建您的第一个 GraphQL API

  • 将 GraphQL 客户端添加到您的应用程序

  • 为您的应用程序创建 AWS Amplify 驱动程序

技术要求

在本章中,我们将使用 Node.js、AWS Amplify 和 Quasar Framework。

注意,Windows 用户!您需要安装一个名为windows-build-tools的 NPM 包,以便能够安装所需的软件包。要执行此操作,请以管理员身份打开 PowerShell 并执行以下命令:

> npm install -g windows-build-tools

要安装 Quasar Framework,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:

> npm install -g @quasar/cli

要安装 AWS Amplify,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:

> npm install -g @aws-amplify/cli

创建您的 AWS Amplify 环境

借助 AWS Amplify 的帮助,我们可以在几分钟内创建一个后端环境,其中包括 NoSQL 数据库、GraphQL 解析器和一个在线存储桶,供我们在开发后部署我们的应用程序。

为了创建 Vue 应用程序,我们将使用 Quasar Framework。这是一个基于 Vue 的框架,提供了开发应用程序所需的所有工具、结构和组件。

在这个配方中,我们将学习如何创建我们的 AWS 账户,在本地配置 AWS Amplify 环境,并使用 Quasar Framework 创建我们的初始项目。

准备就绪

这个教程的先决条件是 Node.js 12+。

所需的 Node.js 全局对象如下:

  • @aws-amplify/cli

  • @quasar/cli

如何做…

我们将把这个教程的任务分成四个部分:创建 AWS 账户,配置 AWS Amplify,创建您的 Quasar 项目,以及初始化 AWS Amplify 项目。

创建 AWS 账户

在这里,我们将学习如何在 AWS 门户上创建一个账户,以便我们可以访问 AWS 控制台:

  1. 转到aws.amazon.com

  2. 在网站上,点击“创建 AWS 账户”按钮。

  3. 选择创建一个“专业”账户或一个“个人”账户(因为我们将要探索平台并为自己开发示例应用程序,最好选择“个人”账户)。

  4. 现在亚马逊将要求您提供付款信息,以防您的使用超出了免费套餐限制。

  5. 现在是确认您的身份的时候 - 您需要提供一个有效的电话号码,亚马逊将用它来发送您需要输入的 PIN 码。

  6. 在收到 PIN 码后,您将看到一个成功的屏幕和一个“继续”按钮。

  7. 现在您需要为您的账户选择一个计划;您可以选择此教程的“基本计划”选项。

  8. 现在您已经完成,可以登录到您的 Amazon AWS 账户控制台。

配置 AWS Amplify

让我们配置本地 AWS Amplify 环境,以准备开始开发我们的聊天应用程序:

  1. 要设置 AWS Amplify,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:
> amplify configure
  1. 浏览器将打开,您需要登录到您的 AWS 控制台账户。

  2. 登录后,返回终端并按Enter。CLI 将要求您选择您希望应用程序执行的服务器区域。建议在us-east-1上运行。

  3. 选择区域后,CLI 将要求您为身份和访问管理IAM)定义用户名。您可以按Enter使用默认值,也可以输入您想要的值(但必须是唯一的)。

  4. 现在浏览器将打开以定义您指定的用户的用户详细信息。点击“下一步:权限”按钮转到下一个屏幕。

  5. 点击“下一步:标签”按钮转到 AWS 标签屏幕。在这个屏幕上,点击“下一步:审核”按钮来审查您定义的设置。

  6. 现在你可以点击“创建用户”按钮来创建用户并转到访问密钥屏幕。

  7. 最后,在此屏幕上,等待访问密钥 ID 和秘密访问密钥可用。在浏览器中复制访问密钥 ID,粘贴到终端中,然后按“Enter”键。

  8. 粘贴访问密钥 ID 后,您必须返回浏览器,点击秘密访问密钥上的“显示”链接,复制该值,粘贴到终端中,然后按“Enter”键。

  9. 最后,您需要定义 AWS 配置文件名称(您可以通过按“Enter”键使用默认值)。

您现在已在计算机上设置了 AWS Amplify 环境。

创建您的 Quasar 项目

现在我们将创建 Quasar Framework 项目,这将是我们的聊天应用程序:

  1. 要创建您的 Quasar Framework 应用程序,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:
> quasar create chat-app
  1. Quasar CLI 将要求输入项目名称;它需要是有效的 npm 软件包名称:
> ? Project name (internal usage for dev) chat-app
  1. CLI 将要求输入产品名称(通常用于渐进式 Web 应用程序PWA),混合移动应用程序和 Electron 应用程序):
? Project product name (must start with letter if building mobile 
  apps) Chat App
  1. 之后,CLI 将要求输入项目描述,这将用于混合应用程序和 PWA:
? Project description A Chat Application
  1. 现在 CLI 将要求输入项目的作者。通常,这是您的 npm 或 Git 配置的作者:
? Author Heitor Ramon Ribeiro <heitor.ramon@example.com>
  1. 现在您可以选择 CSS 预处理器。我们将选择Stylus(您可以选择最适合您的预处理器):
? Pick your favorite CSS preprocessor: (can be changed later) 
  Sass with indented syntax (recommended) 
  Sass with SCSS syntax (recommended) 
❯ Stylus 
  None (the others will still be available)
  1. Quasar 有两种将组件、指令和插件导入构建系统的方法。您可以通过在quasar.conf.js中声明来手动执行,也可以通过自动导入您在代码中使用的组件、指令和插件来自动执行。我们将使用自动导入方法:
? Pick a Quasar components & directives import strategy: (can be changed later) (Use arrow key s)* Auto-import in-use Quasar components & directives - slightly
    higher compile time; next to minimum bundle size; most 
     convenient 
  * Manually specify what to import - fastest compile time; minimum 
     bundle size; most tedious 
  * Import everything from Quasar - not treeshaking Quasar; biggest 
     bundle size; convenient
  1. 现在我们必须选择要添加到项目中的默认功能;我们将选择ESLintVuexAxiosVue-i18n
? Check the features needed for your project: (Press <space> to select, <a> to toggle all, <i> to invert selection) 
❯ ESLint 
 Vuex 
  TypeScript 
 Axios 
 Vue-i18n 
  IE11 support
  1. 现在您可以选择要在项目中使用的ESLint预设;在这种情况下,我们将选择AirBnB
? Pick an ESLint preset: (Use arrow keys) 
  Standard (https://github.com/standard/standard)Airbnb (https://github.com/airbnb/javascript) 
  Prettier (https://github.com/prettier/prettier)
  1. 您需要定义一个 Cordova/Capacitor ID(即使您不构建混合应用程序,也可以使用默认值):
? Cordova/Capacitor id (disregard if not building mobile apps) 
  org.cordova.quasar.app
  1. 最后,您可以选择要运行的软件包管理器,并安装您需要运行代码的软件包:
? Should we run `npm install` for you after the project has been 
  created? (recommended) (Use arrow keys) 
  Yes, use Yarn (recommended) 
❯ Yes, use NPM 
  No, I will handle that myself

初始化 AWS Amplify 项目

要初始化您的 AWS Amplify 项目,请执行以下步骤:

  1. 打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),进入项目文件夹,并执行以下命令:
> amplify init
  1. Amplify CLI 将要求输入项目名称:
? Enter a name for the project: chatapp
  1. 然后,您需要为您的机器上正在运行的当前项目定义一个环境:
? Enter a name for the environment: dev
  1. 现在您可以选择您将在项目中使用的默认编辑器:
? Choose your default editor: (Use arrow keys) 
❯ Visual Studio Code
  Atom Editor 
  Sublime Text
  InteliJ IDEA
  Vim (via Terminal, Mac OS only)
  Emac (via Terminal, Mac OS only)
  None
  1. 您需要决定由 AWS Amplify 托管的项目类型。在我们的情况下,这将是一个 JavaScript 应用程序:
? Choose the type of app that you're building? (recommended) (Use 
   arrow keys) 
  android 
  ios 
❯ javascript
  1. 对于框架,因为我们将使用 Quasar Framework 作为基础,我们需要从所呈现的框架列表中选择“无”:
? What javascript framework are you using? (recommended) (Use arrow 
  keys) 
  angular 
  ember
  ionic
  react
  react-native
  vue 
❯ none
  1. 您将需要定义应用程序的源路径;您可以将源目录路径保留为默认值src。然后按Enter继续:
? Source Directory Path: (src)
  1. 对于分发目录,由于 Quasar 使用不同类型的路径组织,我们需要将其定义为dist/spa
? Distribution Directory Path: dist/spa
  1. AWS Amplify 将在部署之前使用的构建命令,我们将将其定义为quasar build
? Build Command: quasar build
  1. 对于启动命令,我们需要使用 Quasar 内置的quasar dev命令:
? Start Command: quasar dev

对于 Windows 用户,由于 Amplify 和 WSL 不兼容,您可能需要将启动命令定义如下:

? Start Command: quasar.cmd dev
  1. 现在 CLI 会询问我们是否要为此配置使用本地 AWS 配置文件:
? Do you want to use an AWS profile: y
  1. 我们将选择之前创建的默认配置文件:
? Please choose the profile you want to use: (Use arrow keys)default
  1. CLI 完成初始化过程后,我们需要向项目添加托管。为此,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),进入项目文件夹,并执行以下命令:
> amplify add hosting
  1. CLI 会询问您的应用程序的托管过程。选择“使用 Amplify Console 进行托管”,然后按Enter继续:
? Select the plugin module to execute 
❯ Hosting with Amplify Console (Managed hosting with custom domains,
  Continuous deployment) 
  Amazon CloudFront and S3 
  1. 然后 CLI 会询问您部署过程将如何进行;选择“手动部署”,然后按Enter继续:
? Choose a type (Use arrow keys)
  Continuous deployment (Git-based deployments) 
❯ Manual deployment 
  Learn more 
  1. 当您完成所有操作后,要完成此过程,您需要发布它。打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),进入项目文件夹,并执行以下命令:
> amplify publish
  1. 您将被问及是否要继续发布,您可以接受。完成所有操作后,浏览器将打开默认的 Quasar Framework 首页:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

它是如何工作的…

AWS Amplify 是 Web 开发人员的一体化解决方案,提供了一整套工具,从托管应用程序到后端开发。

我们能够快速轻松地构建应用程序并将其上线,完全没有遇到基础设施方面的问题。

在这个步骤中,我们设法创建了我们的 AWS 账户,并为本地开发和网页部署准备好了我们的第一个 AWS Amplify 环境。此外,我们还能够创建了将用作聊天应用程序的 Quasar Framework 项目,并将其部署到 AWS 基础设施中,以准备应用程序的未来发布。

另请参阅

创建您的第一个 GraphQL API

AWS Amplify 提供了在简单步骤和许多附加选项(包括身份验证、部署和环境)的情况下,开箱即用地拥有 GraphQL API 的可能性。这使我们能够仅使用 GraphQL SDL 模式快速开发 API,并且 AWS Amplify 将为连接构建 API、DynamoDB 实例和代理服务器。

在这个步骤中,我们将学习如何使用 AWS Amplify 创建 GraphQL API,并为身份验证添加 AWS Cognito 功能。

准备工作

此步骤的先决条件如下:

  • 上一个步骤的项目

  • Node.js 12+

所需的 Node.js 全局对象是@aws-amplify/cli

要安装 AWS Amplify,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),并执行以下命令:

> npm install -g @aws-amplify/cli

在这个步骤中,我们将使用创建您的 AWS Amplify 环境步骤中的项目。请先完成该步骤中的说明。

如何做…

要启动我们的 GraphQL API,我们将继续使用在创建您的 AWS Amplify 环境步骤中创建的项目。

这个步骤将分为两部分:创建 AWS Cognito 和创建 GraphQL API。

创建 AWS Cognito 身份验证

为了给我们的 API 和应用程序增加一层安全性,我们将使用 AWS Cognito 服务。这将提供对用户和身份验证的控制作为服务:

  1. 要初始化您的 AWS Cognito 配置,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),进入项目文件夹,并执行以下命令:
> amplify auth add
  1. 现在 CLI 会要求您选择用于创建 Cognito 服务的配置类型。这些是预先制定的规则和配置的选择。我们将选择默认配置
Do you want to use default authentication and security configuration: (Use arrow keys) 
❯ Default configuration  Default configuration with Social Provider (Federation)
  Manual configuration
  I want to learn more.
  1. 之后,您需要选择用户将如何登录;因为我们正在构建一个聊天应用程序,我们将选择电子邮件
Warning: you will not be able to edit these selections.
How do you want users to be able to sign in: (Use arrow keys) 
  Username
❯ Email  Phone Number
  Email and Phone Number
  I want to learn more.
  1. 对于 AWS Cognito,不需要选择更高级的设置。我们可以通过选择不,我完成了。来跳过这一步。
Do you want to configure advanced settings: (Use arrow keys) 
❯ No, I am done.  Yes, I want to make some additional changes.
  1. 最后,我们需要将这个配置推送到云端。为此,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),进入项目文件夹,并执行以下命令:
> amplify auth push
  1. 您将被问及是否要继续 - 输入y,CLI 将发布配置到 AWS Cognito 云:
? Are you sure you want to continue: y 

创建 GraphQL API

在这部分,我们将把说明分为两部分,首先创建 GraphQL SDL 模式,然后创建 GraphQL API。

创建 GraphQL SDL 模式

要使用 AWS Amplify 创建 GraphQL API,首先需要创建一个 GraphQL SDL 模式。AWS Amplify 将使用该模式生成 API 的数据库和解析器:

  1. src文件夹中创建一个名为chatApi.graphql的新文件,并打开它。

  2. 创建我们基本的S3Object模式类型,这是一个简单的模型,用于管理放置在 AWS S3 存储桶中的文件的存储:

type S3Object {
  bucket: String!
  region: String!
  key: String! } 
  1. 然后我们将创建我们的用户类型。这就像一个带有更多规则附加的数据库模型。这个类型将有一个@auth规则,只允许所有者,在这种情况下是用户,执行创建更新删除操作。之后,我们将声明用户字段:
type User
@model(subscriptions: null) @auth(rules: [
  { allow: owner, ownerField: "id", queries: null },
  { allow: owner, ownerField: "owner", queries: null },
]) {
  id: ID!
  email: String!
  username: String!
  avatar: S3Object
  name: String
  conversations: [ConversationLink] @connection(name: "UserLinks")
  messages: [Message] @connection(name: "UserMessages", keyField: "authorId")
  createdAt: String
  updatedAt: String }
  1. 我们的用户将与另一个用户进行对话。我们将创建一个对话类型,为了保护这个对话,我们将添加一个@auth规则,以确保只有这个对话的成员可以看到用户之间交换的消息。在messages字段中,我们将创建一个与消息类型@connection,并在关联字段中创建一个与对话链接类型@connection
type Conversation
@model(
  mutations: { create: "createConversation" }
  queries: { get: "getConversation" }
  subscriptions: null ) @auth(rules: [{ allow: owner, ownerField: "members" }]) {
  id: ID!
  messages: [Message] @connection(name: "ConversationMessages",
   sortField: "createdAt")
  associated: [ConversationLink] @connection(name: 
   "AssociatedLinks")
  name: String!
  members: [String!]!
  createdAt: String
  updatedAt: String }
  1. 对于消息类型,我们需要添加一个@auth装饰器规则,只允许所有者对其进行操作。我们需要创建一个@connection装饰器,将author字段连接到用户类型,并创建一个@connection装饰器,将conversation字段连接到对话类型
type Message
@model(subscriptions: null, queries: null) @auth(rules: [{ allow: owner, ownerField: "authorId", operations: [create, update, delete]}]) {
  id: ID!
  author: User @connection(name: "UserMessages", keyField: 
   "authorId")
  authorId: String
  content: String!
  conversation: Conversation! @connection(name: "ConversationMessages")
  messageConversationId: ID!
  createdAt: String
  updatedAt: String }
  1. 现在我们正在使用type ConversationLink将对话链接在一起。这个type需要user字段具有@connection装饰器到User@connection对话到type Conversation
type ConversationLink
@model(
  mutations: { create: "createConversationLink", update: 
"updateConversationLink" }
  queries: null
  subscriptions: null ) {
  id: ID!
  user: User! @connection(name: "UserLinks")
  conversationLinkUserId: ID
  conversation: Conversation! @connection(name: "AssociatedLinks")
  conversationLinkConversationId: ID!
  createdAt: String
  updatedAt: String }
  1. 最后,我们需要创建一个type Subscription来在 GraphQL API 内部具有事件处理程序。Subscription类型会监听并处理特定变化的特定变化,createConversationLinkcreateMessage,两者都会在数据库内触发事件:
type Subscription {
  onCreateConversationLink(conversationLinkUserId: ID!): 
   ConversationLink
  @aws_subscribe(mutations: ["createConversationLink"])
  onCreateMessage(messageConversationId: ID!): Message
  @aws_subscribe(mutations: ["createMessage"])
  onCreateUser: User
  @aws_subscribe(mutations: ["createUser"])
  onDeleteUser: User
  @aws_subscribe(mutations: ["deleteUser"])
  onUpdateUser: User
  @aws_subscribe(mutations: ["updateUser"]) }
使用 AWS Amplify 创建 GraphQL API

在这里,我们将使用 AWS Amplify API 使用先前创建的 GraphQL 模式来创建我们的 GraphQL API:

  1. 要初始化您的 AWS Amplify API 配置,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),进入项目文件夹,并执行以下命令:
> amplify add api
  1. 在这里,CLI 将询问您要创建什么类型的 API。我们将选择GraphQL
? Please select from one of the below mentioned services: (Use arrow 
  keys) 
❯ GraphQL  REST
  1. 现在 CLI 将要求输入 API 名称(您可以选择):
? Provide API name: chatapp
  1. 在这里,我们将选择 API 将使用的身份验证方法。由于我们将使用 AWS Cognito,我们需要选择Amazon Cognito User Pool选项:
? Choose the default authorization type for the API: (Use arrow
  keys) 
  API key
❯ Amazon Cognito User Pool  IAM
  OpenID Connect
  1. 然后 CLI 将询问您是否要在 API 上配置更多设置;我们将选择No, I am done.选项:
? Do you want to configure advanced settings for the GraphQL API:
  (Use arrow keys) 
❯ No, I am done.  Yes, I want to make some additional changes.
  1. 现在我们将被问及是否有注释的 GraphQL 模式;由于我们之前已经编写了一个,我们需要输入y
? Do you have an annotated GraphQL schema?: y
  1. 在这里,我们需要输入刚刚创建的文件的路径./src/chatApi.graphql
? Provide your schema file path: ./src/chatApi.graphql
  1. 完成后,我们需要将配置推送到 AWS Amplify。要执行此操作,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),进入项目文件夹,并执行以下命令:
> amplify push
  1. 当询问是否要继续时,输入y
? Are you sure you want to continue?: y
  1. CLI 将询问您是否要为新创建的 GraphQL API 生成代码;再次输入y
? Do you want to generate code for your newly created GraphQL API: y
  1. 在这里,您可以选择 CLI 要使用的语言来创建项目中使用的通信文件。我们将选择javascript,但您可以选择最符合您需求的语言:
? Choose the code generation language target: (Use arrow keys) 
❯ javascript
  typescript
  flow
  1. CLI 将询问要放置将生成的文件的位置,我们将使用默认值:
? Enter the file name pattern of graphql queries, mutation and
  subscriptions: (src/graphql/***/**.js) 
  1. 现在 CLI 将询问有关 GraphQL 操作的生成。由于我们正在创建我们的第一个 GraphQL API,我们将选择y,因此 CLI 将为我们创建所有文件:
? Do you want to generate/update all possible GraphQL operations - 
  queries, mutations and subscriptions: y 
  1. 最后,我们可以定义文件中模式的最大深度,我们将使用默认值2
? Enter maximum statement depth [increase from default if your 
  schema is deeply nested]: (2) 
  1. 当你完成所有的事情后,我们需要将配置发布到 AWS Amplify。要做到这一点,你需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),进入项目文件夹,并执行以下命令:
> amplify publish 

工作原理…

在创建一个带有 AWS Amplify 的 GraphQL API 的过程中,我们需要一个预先构建的模式,用于生成数据库和端点。这个模式是基于 GraphQL SDL 语言的。Amplify 已经在 SDL 中添加了更多的装饰符,这样我们就可以在 API 的开发中拥有更广泛的可能性。

与此同时,我们需要创建一个 AWS Cognito 用户池,用于保存将在应用程序上注册的用户。这是为了在应用程序外部管理和维护身份验证层,并作为一个服务使用,可以提供更多功能,包括双因素身份验证、必填字段和恢复模式。

最后,在一切都完成之后,我们的 API 已经在 AWS Amplify 上发布,并准备好进行开发,具有可以用作开发环境的 URL。

另请参阅

将 GraphQL 客户端添加到你的应用程序

Apollo Client 目前是 JavaScript 生态系统中最好的 GraphQL 客户端实现。它有一个庞大的社区支持,并得到了大公司的支持。

我们的 AWS Amplify GraphQL API 的实现在后端使用了 Apollo Server,因此 Apollo Client 的使用将是一个完美的匹配。AWS AppSync 也使用他们自己的 Apollo 实现作为客户端,所以我们仍然会使用 Apollo 作为客户端,但不是直接使用。

在这个配方中,我们将学习如何将 GraphQL 客户端添加到我们的应用程序中,以及如何连接到 AWS Amplify GraphQL 服务器来执行查询。

准备工作

这个配方的先决条件如下:

  • 上一个配方的项目

  • Node.js 12+

所需的 Node.js 全局对象如下:

  • @aws-amplify/cli

  • @quasar/cli

在这个示例中,我们将使用创建您的第一个 GraphQL API示例中的项目。在遵循本示例之前,请按照上一个示例中的步骤进行操作。

如何做…

我们将使用 Amplify 客户端将 GraphQL 客户端添加到我们的应用程序中。按照以下步骤创建 GraphQL 驱动程序:

  1. 要安装使用 GraphQL 客户端所需的软件包,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:
> npm install --save graphql aws-amplify graphql-tag aws-appsync
  1. boot文件夹中创建一个名为amplify.js的新文件,并打开它。

  2. 在这个文件中,我们将导入aws-amplify包和 AWS Amplify CLI 在配置过程中为我们创建的aws-exports.js文件。我们将使用我们拥有的配置来配置 Amplify。为了使 Quasar 引导文件起作用,我们需要导出一个default空函数:

import Amplify from 'aws-amplify';   import AwsExports from '../aws-exports';   Amplify.configure(AwsExports);   export default () => {}; 
  1. root文件夹中的quasar.conf.js文件中,我们需要向webpack捆绑器添加新规则。要做到这一点,找到extendWebpack函数。在函数的第一行之后,创建两个新规则给捆绑器,第一个规则将添加graphql-loaderwebpack 加载程序,第二个规则将允许捆绑器理解.mjs文件:
// The rest of the quasar.conf.js... extendWebpack (cfg) {
  //New rules that need to be added
 cfg.module.rules.push({
  test: /\.(graphql|gql)$/,
  exclude: /node_modules/,
  loader: 'graphql-tag/loader',
  });    cfg.module.rules.push({
  test: /\.mjs$/,
  include: /node_modules/,
  type: 'javascript/auto',
  });
 // Maintain these rules  cfg.module.rules.push({
  enforce: 'pre',
  test: /\.(js|vue)$/,
  loader: 'eslint-loader',
  exclude: /node_modules/,
  options: {
  formatter: 
       require('eslint').CLIEngine.getFormatter('stylish'),
  },
  });    cfg.resolve.alias = {
  ...cfg.resolve.alias,
  driver: path.resolve(__dirname, './src/driver'),
  }; }, // The rest of the quasar.conf.js...
  1. 现在,在src/driver文件夹中创建一个名为graphql.js的新文件,并打开它。

  2. 在这个文件中,我们需要从aws-appsync包中导入AWSAppSyncClient,从aws-amplify包中导入Auth,并从src文件夹中的aws-exports.js文件中导入AwsExports。然后,我们需要使用aws-exports的配置实例化AWSAppSyncClient,并导出客户端的这个实例化:

import AWSAppSyncClient from 'aws-appsync'; import { Auth } from 'aws-amplify'; import AwsExports from '../aws-exports';   export default new AWSAppSyncClient({
  url: AwsExports.aws_appsync_graphqlEndpoint,
  region: AwsExports.aws_appsync_region,
  auth: {
  type: AwsExports.aws_appsync_authenticationType,
  jwtToken: async () => (await 
      Auth.currentSession()).idToken.jwtToken,
  }, }); 
  1. quasar.conf.js文件中的root文件夹中,我们需要将新创建的amplify.js文件添加到引导序列中,该文件位于boot文件夹中。要做到这一点,找到boot数组,并在末尾添加文件在boot文件夹中的路径作为字符串,不包括扩展名。在我们的情况下,这将是'amplify'
// The rest of the quasar.conf.js... 
boot: [   'axios',
  'amplify' ], // The rest of the quasar.conf.js...  

它是如何工作的…

我们在全局范围内将aws-amplify包添加到我们的应用程序中,并通过新的graphql.js文件中的导出条目使其可用于使用。这使得在应用程序中可以使用AWSAmplifyAppSync

使用 Quasar Framework 的引导过程,我们能够在 Vue 应用程序开始在屏幕上呈现之前实例化 Amplify。

另请参阅

为您的应用程序创建 AWS Amplify 驱动程序

为了与 AWS Amplify 服务进行通信,我们需要使用他们的 SDK。这个过程是重复的,可以合并到我们将要使用的每个 Amplify 服务的驱动程序中。

在这个示例中,我们将学习如何创建通信驱动程序,以及如何使用 AWS Amplify 进行操作。

准备工作

这个示例的先决条件如下:

  • 上一个示例的项目

  • Node.js 12+

所需的 Node.js 全局对象如下:

  • @aws-amplify/cli

  • @quasar/cli

在这个示例中,我们将使用将 GraphQL 客户端添加到您的应用程序示例中的项目。请先完成该示例中的说明。

如何做…

在这个示例中,我们将其分为三个部分:第一部分将用于 AWS 存储驱动程序,第二部分将用于 Amplify Auth 驱动程序,最后,我们将看到 Amplify AppSync 实例的创建。

创建 AWS Amplify 存储驱动程序

要创建 AWS Amplify 存储驱动程序,我们首先需要创建 AWS Amplify 存储基础设施,并在我们的环境中设置好,之后我们需要创建 AWS Amplify 存储 SDK 与我们的应用程序之间的通信驱动程序。

添加 AWS Amplify 存储

在这部分,我们将向我们的 Amplify 服务列表中添加 AWS S3 功能。这是必需的,这样我们就可以在 AWS S3 云基础设施上保存文件:

  1. 首先,我们需要向项目添加 AWS 存储。为此,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),进入项目文件夹并执行以下命令:
> amplify add storage
  1. 现在我们需要选择将上传什么内容。我们需要选择内容(图片、音频、视频等)
? Please select from one of the below mentioned services: (Use arrow 
  keys)Content (Images, audio, video, etc.)  NoSQL Database
  1. 我们需要为资源添加一个名称。我们将其称为bucket
? Please provide a friendly name for your resource that will be used 
  to label this category in the project: bucket
  1. 现在我们需要提供一个 AWS S3 存储桶名称。我们将其称为chatappbucket
? Please provide bucket name: chatappbucket 
  1. 然后我们需要选择谁可以操作存储桶文件。由于应用程序将仅基于授权,我们需要选择仅授权用户
? Who should have access: (Use arrow keys) 
❯ Auth users only  Auth and guest users
  1. 现在您需要选择用户在存储桶中的访问级别:
? What kind of access do you want for Authenticated users? 
  create/update
  read
❯ delete
  1. 当被问及创建自定义 Lambda 触发器时,选择n
? Do you want to add a Lambda Trigger for you S3 Bucket: n
  1. 最后,我们需要将更改推送到云端。为此,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),进入项目文件夹,并执行以下命令:
> amplify push
  1. 当您完成所有操作后,我们需要将配置发布到 AWS Amplify。为此,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows),进入项目文件夹,并执行以下命令:
> amplify publish
创建 Amplify Storage 驱动程序

在这部分,我们将创建与 Amplify Storage 通信的驱动程序。该驱动程序将处理我们应用程序中的文件上传:

  1. src/driver文件夹中创建一个名为bucket.js的新文件并打开它。

  2. aws-amplify包中导入Storage类,从quasar中导入uid函数,以及AwsExports

import { Storage } from 'aws-amplify'; import { uid } from 'quasar'; import AwsExports from '../aws-exports'; 
  1. 创建一个名为uploadFile的异步函数,它接收三个参数:filenametypename参数的默认值为uid()type参数的默认值为'image/png'。在这个函数中,我们将调用Storage.put函数,传递namefile作为参数,作为第三个参数,我们将传递一个 JavaScript 对象,其中contentType属性定义为接收到的type,并且accept属性定义为'**/**'。上传完成后,我们将返回一个具有bucketregionuploadedFile属性的 JavaScript 对象:
export async function uploadFile(file, name = uid(), type = 'image/png') {
  try {
  const uploadedFile = await Storage.put(name, file, {
  contentType: type,
  accept: '*/*',
  });    return {
  ...uploadedFile,
  bucket: AwsConfig.aws_user_files_s3_bucket,
  region: AwsConfig.aws_user_files_s3_bucket_region,
  };
  } catch (err) {
  return Promise.reject(err);
  } }
  1. 创建一个名为getFile的异步函数,它接收name参数,默认值为空字符串。在函数内部,我们将返回Storage.get,传递name参数和设置为public级别的选项:
export async function getFile(name = '') {
  try {
  return await Storage.get(name, { level: 'public' });
  } catch (err) {
  return Promise.reject(err);
  } } 
  1. 最后,导出一个默认的 JavaScript 对象,并将创建的函数uploadFilegetFile作为属性添加进去:
export default {
  uploadFile,
  getFile, };  

创建 Amplify Auth 驱动程序

现在我们将创建认证驱动程序。该驱动程序负责处理应用程序中的所有认证请求并获取用户信息:

  1. src/driver文件夹中创建一个名为auth.js的新文件并打开它。

  2. 在新创建的文件中,从aws-amplify包中导入Auth类:

import { Auth } from 'aws-amplify';
  1. 创建一个名为signIn的新异步函数。它将接收emailpassword作为参数,并且该函数将返回Auth.signIn函数,传递emailpassword作为参数:
export async function signIn(email = '', password = '') {
  try {
  return Auth.signIn({
  username: email,
  password,
  });
  } catch (err) {
  return Promise.reject(err);
  } }
  1. 创建一个名为signUp的新异步函数,该函数将接收emailpassword作为参数。该函数将返回Auth.signUp函数,传递一个带有这些属性的 JavaScript 对象作为参数:usernamepasswordattributesvalidationData

username属性将是作为参数接收的email值。

password属性将是作为参数接收的password值。

attributes属性将是一个带有email属性的 JavaScript 对象,该属性将作为参数接收:

export async function signUp(email = '', password = '') {
  try {
  return Auth.signUp({
  username: email,
  password: `${password}`,
  attributes: {
 email,
  },
  validationData: [],
  });
  } catch (err) {
  return Promise.reject(err);
  } }
  1. 创建一个名为validateUser的新异步函数,该函数将接收usernamecode作为参数。该函数等待Auth.confirmSignUp函数的响应,将usernamecode作为参数传递给该函数,并在完成时返回true
export async function validateUser(username = '', code = '') {
  try {
  await Auth.confirmSignUp(username, `${code}`);    return Promise.resolve(true);
  } catch (err) {
  return Promise.reject(err);
  } }
  1. 创建一个名为resendValidationCode的新异步函数,该函数将接收username作为参数。该函数返回Auth.resendSignUp函数,将username作为参数:
export async function resendValidationCode(username = '') {
  try {
  return Auth.resendSignUp(username);
  } catch (err) {
  return Promise.reject(err);
  } } 
  1. 创建一个名为signOut的新异步函数,该函数返回Auth.signOut函数:
export async function signOut() {
  try {
  return Auth.signOut();
  } catch (err) {
  return Promise.reject(err);
  } }
  1. 创建一个名为changePassword的新异步函数,该函数将接收oldPasswordnewPassword作为参数。该函数等待获取当前经过身份验证的用户,并返回Auth.changePassword函数,将获取的useroldPasswordnewPassword作为参数:
export async function changePassword(oldPassword = '', newPassword = '') {
  try {
  const user = await Auth.currentAuthenticatedUser();
  return Auth.changePassword(user, `${oldPassword}`, `${newPassword}`);
  } catch (err) {
  return Promise.reject(err);
  } }
  1. 创建一个名为getCurrentAuthUser的新异步函数;该函数将获取当前经过身份验证的用户,并返回一个带有idemailusername属性的 JavaScript 对象:
export async function getCurrentAuthUser() {
  try {
  const user = await Auth.currentAuthenticatedUser();    return Promise.resolve({
  id: user.username,
  email: user.signInUserSession.idToken.payload.email,
  username: user.username,
  });
  } catch (err) {
  return Promise.reject(err);
  } } 

创建 Amplify AppSync 实例

在经过身份验证的情况下与 AWS Amplify API 通信,我们需要创建一个新的 AWS Amplify AppSync API 实例,其中包含用户身份验证信息:

  1. src/driver文件夹中创建一个名为appsync.js的新文件并打开它。

  2. 在新创建的文件中,从aws-amplify包中导入AuthAPI,从@aws-amplify/api包中导入GRAPHQL_AUTH_MODE枚举,以及 AWS 配置:

import { Auth, API } from 'aws-amplify'; import { GRAPHQL_AUTH_MODE } from '@aws-amplify/api'; import AwsExports from '../aws-exports';
  1. 通过执行API.configure函数从aws-amplify包中配置 API,传递一个 JavaScript 对象作为参数,其中包含urlregionauth的属性。

url属性中,传递 GraphQL 端点 URL 的配置。

region属性中,传递当前正在使用的 AWS 区域的配置。

auth属性中,我们需要传递一个具有两个属性typejwtToken的 JavaScript 对象。

我们需要将type属性设置为GRAPHQL_AUTH_MODE.AMAZON_COGNITO_USER_POOLS

jwtToken中,我们将传递一个异步函数,该函数将返回当前登录用户的令牌:

API.configure({
  url: awsconfig.aws_appsync_graphqlEndpoint,
  region: awsconfig.aws_appsync_region,
  auth: {
  type: GRAPHQL_AUTH_MODE.AMAZON_COGNITO_USER_POOLS,
  jwtToken: async () => (await Auth.currentSession()).getIdToken().getJwtToken(),
  }, });
  1. 最后,我们将API导出为名为AuthAPI的常量:
export const AuthAPI = API;

工作原理…

在这个示例中,我们学习了如何将应用程序的责任分离为可以在多个领域重复使用而无需重写整个代码的驱动程序。通过这个过程,我们能够创建一个用于 Amplify 存储的驱动程序,可以异步发送文件,并且这些文件被保存在 AWS S3 服务器上的存储桶中。

在我们对 Auth 驱动程序的工作中,我们能够创建一个可以管理 Amplify 身份验证 SDK 并在需要时提供信息并封装特殊功能以使在我们的应用程序中执行任务更容易的驱动程序。

最后,在 Amplify AppSync API 中,我们成功实例化了 API 连接器,并使用了所有需要的身份验证标头,以便应用程序可以在没有任何问题的情况下执行,并且用户可以在请求时访问所有信息。

另请参阅

第四章:创建自定义应用程序组件和布局

要开始我们应用程序的开发,我们需要创建整个应用程序将使用的自定义组件和输入。这些组件将采用无状态的方法创建。

我们将开发UsernameInput组件,PasswordInput组件,EmailInput组件和AvatarInput组件。我们还将开发应用程序页面和聊天布局的基本布局,它将包装聊天页面。

在本章中,我们将涵盖以下示例:

  • 为应用程序创建自定义输入

  • 创建应用程序布局

技术要求

在本章中,我们将使用Node.jsQuasar Framework

注意,Windows 用户!您需要安装一个名为windows-build-toolsnpm包,以便能够安装所需的包。要做到这一点,以管理员身份打开 PowerShell 并执行以下命令:

> npm install -g windows-build-tools

要安装 Quasar Framework,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:

> npm install -g @quasar/cli 

为应用程序创建自定义输入

创建应用程序需要创建大量的表单。所有这些表单都需要输入,这些输入很可能在应用程序中重复出现。

在这个示例中,我们将创建自定义输入表单,我们将在几乎每个表单中使用它们。

创建自定义输入表单的过程有助于开发人员节省调试时间,代码的可重用性和未来的改进。

准备工作

这个示例的先决条件如下:

  • 最后的示例项目

  • Node.js 12+

所需的 Node.js 全局对象如下:

  • @aws-amplify/cli

  • @quasar/cli

要开始我们的自定义组件,我们将继续使用在第三章设置我们的聊天应用程序 - AWS Amplify 环境和 GraphQL中创建的项目。

如何做…

为了更好地重用代码,我们将创建单独的组件来处理应用程序上的自定义表单。在这种情况下,我们将创建六个组件:

  • UsernameInput

  • PasswordInput

  • NameInput

  • EmailInput

  • AvatarInput

  • AvatarDisplay

所以,让我们开始吧。

创建 UsernameInput 组件

UsernameInput将负责处理用户名的检查和验证,这样我们就不需要在每个需要使用它的页面上重新编写所有规则。

单文件组件<script>部分

在这里,我们将创建UsernameInput组件的<script>部分:

  1. src/components文件夹中创建一个名为UsernameInput.vue的新文件,并打开它。

  2. 创建一个带有nameprops属性的默认导出的 JavaScript 对象:

export default {
  name: '',
  props: {}, };
  1. 对于name属性,将其定义为"UsernameInput"
name: 'UsernameInput',
  1. 对于props属性,将其定义为一个 JavaScript 对象,并添加一个名为value的新属性,它也将是一个具有typedefaultrequired属性的 JavaScript 对象。type属性需要定义为Stringdefault''requiredfalse
props: {
  value: {
    type: String,
    default: '',
    required: false,
  },
},
单文件组件<template>部分

在这里,我们将创建UsernameInput组件的<template>部分:

  1. <template>部分,创建一个QInput组件。创建两个动态属性,valuerules。现在,value将绑定到value属性,rules属性将接收一个数组。数组的第一项是一个函数,用于验证输入,第二项是出现错误时的消息。

  2. outlinedlazy-rules属性设置为true,并将label属性定义为"Your Username"

  3. 最后,通过创建一个v-on指令,使用$listeners Vue API 作为值来为事件创建事件侦听器。

完成所有步骤后,您的最终代码应该像这样:

<template>
  <q-input
  :value="value"
  :rules="[ val => (val && val.length > 5 || 'Please type a valid 
      Username')]"
  outlined
  label="Your Username"
  lazy-rules
  v-on="$listeners"
  /> </template>

这是您的组件呈现出来的样子:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建一个 PasswordInput 组件

PasswordInput将是一个组件,具有特殊逻辑,通过单击按钮切换密码的可见性。我们将在这个组件中包装这个逻辑,这样每次使用这个组件时就不需要重新编写它。

单文件组件<script>部分

在这部分,我们将创建PasswordInput组件的<script>部分:

  1. components文件夹中创建一个名为PasswordInput.vue的新文件,并打开它。

  2. 创建一个默认导出的 JavaScript 对象,具有三个属性,namepropsdata

export default {
  name: '',
  props: {},
  data: () => (), };
  1. 对于name属性,将值定义为"PasswordInput"
name: 'PasswordInput',
  1. 对于props属性,添加两个属性,valuelabel,都是 JavaScript 对象。每个对象内部应该有三个属性:typedefaultrequired。将value.type设置为Stringvalue.default设置为''value.required设置为false。然后,将label.type设置为Stringlabel.default设置为'Your Password'label.required设置为false
props: {
  value: {
  type: String,
  default: '',
  required: false,
  },
  label: {
  type: String,
  default: 'Your password',
  required: false,
  }, }, 
  1. 最后,在data属性中,添加一个 JavaScript 对象作为返回值,其中isPwd值设置为true
data: () => ({
  isPwd: true, }),
单文件组件部分

现在我们将创建PasswordInput<template>部分。按照以下说明来实现正确的输入组件:

  1. <template>部分,创建一个QInput组件,并将valuelabelrules属性添加为变量。value将绑定到value属性,label将绑定到label属性,rules将接收一个函数数组,用于执行对表单输入的基本验证。

  2. 对于type属性,将其定义为一个变量,并将其设置为对isPwd的三元验证,在"password""text"之间切换。

  3. outlinedlazy-rules属性设置为true

  4. 创建一个hint变量属性,并将其定义为三元运算符,它将检查当前值的长度是否匹配最小值大小;否则,它将向用户显示一条消息。

  5. 然后,通过创建一个v-on指令并使用$listenersVue API 作为值来为事件创建事件侦听器。

  6. QInput模板内部,我们将添加一个子组件,该组件将占据一个命名插槽v-slot:append,该插槽将容纳一个QIcon组件。

  7. 对于QIcon组件,定义name属性以对isPwd变量进行响应,因此当isPwd设置为true时,它将是'visibility_off',或者当isPwd设置为false时,它将是'visibility'。将class属性定义为"cursor-pointer",以便鼠标具有实际鼠标的外观和"hover hand icon",并在@click事件侦听器上,我们将设置isPwd为当前isPwd的相反值。

完成所有步骤后,您的最终代码应该像这样:

<template>
  <q-input
  :value="value"
  :type="isPwd ? 'password' : 'text'"
  :rules="[ val => val.length >= 8 || 'Your password need to have 8
             or more characters', val => val !== null && val !== '' || 
              'Please type your password']"
  :hint=" value.length < 8 ? 'Your password has a minimum of 8 
             characters' : ''"
  :label="label"
  outlined
  lazy-rules
  v-on="$listeners"
  >
  <template v-slot:append>
    <q-icon
      :name="isPwd ? 'visibility_off' : 'visibility'"
      class="cursor-pointer"
      @click="isPwd = !isPwd"
    />
  </template>
  </q-input> </template>

这是您的组件呈现的方式:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建 NameInput 组件

在我们创建的所有组件中,NameInput组件是最简单的,几乎没有改变QInput组件的行为,只是添加了验证规则和一些个性化。

单文件组件

在这部分,我们将创建NameInput组件的<script>部分:

  1. 创建一个默认导出的 JavaScript 对象,有两个属性:nameprops
export default {
  name: '',
  props: {},  }; 
  1. name属性中,将值定义为'NameInput'
name: 'NameInput',
  1. props属性中,添加一个属性value,作为一个 JavaScript 对象,里面有三个属性:typedefaultrequired。将value.type设置为Stringvalue.default设置为**''**value.required设置为false
props: {
  value: {
  type: String,
  default: '',
  required: false,
  },  },
单文件组件部分

在这部分,我们将创建NameInput组件的<template>部分:

  1. <template>部分,创建一个QInput组件,并添加valuerules属性作为变量。value将绑定到value属性,rules将接收一个函数数组,用于检查表单输入的基本验证。

  2. outlinedlazy-rules属性设置为true,并将label属性定义为"Your Name"

  3. 最后,通过创建一个v-on指令并将"$listeners" Vue API 作为值来为事件创建事件监听器。

完成所有步骤后,你的最终代码应该像这样:

<template>
  <q-input
  :value="value"
  :rules="[ val => (val && val.length > 0
    || 'Please type a valid Name')]"
  outlined
  label="Your Name"
  lazy-rules
  v-on="$listeners"
  /> </template>

这是你的组件渲染结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建 EmailInput 组件

EmailInput组件中,我们需要特别注意规则验证的处理,因为我们需要检查正在输入的电子邮件是否是有效的电子邮件地址。

单文件组件

在这部分,我们将创建EmailInput组件的<script>部分:

  1. 创建一个默认导出的 JavaScript 对象,有三个属性:namepropsmethods
export default {
  name: '',
  props: {},
 methods: {}, }; 
  1. name属性中,将值定义为'EmailInput'
name: 'EmailInput',
  1. props属性中,添加一个属性value,作为一个 JavaScript 对象,里面有三个属性:typedefaultrequired。将value.type设置为Stringvalue.default设置为**'**value.required设置为false
props: {
  value: {
  type: String,
  default: '',
  required: false,
  },  },
  1. methods属性中,我们需要添加一个名为validateEmail的新方法,该方法接收一个名为email的参数。此方法将通过正则表达式测试接收到的参数,以检查它是否是有效的表达式,并返回结果:
methods: {
  validateEmail(email) {
  const regex = /^(([^\s"(),.:;<>@[\\\]]+(\.[^\s"(),.:;
     <>@[\\\]]+)*)|(".+"))@((\[(?:\d{1,3}\.){3}\d{1,3}])|(([\dA-Za-
 z\-]+\.)+[A-Za-z]{2,}))$/;
  return regex.test(email);
  }, }, 
单文件组件部分

在这里,我们将创建EmailInput组件的<template>部分:

  1. <template>部分,创建一个QInput组件,并将valuerules属性作为变量添加。value将绑定到value属性,rules将接收一个函数数组,用于执行基本验证表单输入的检查。

  2. outlinedlazy-rules属性添加为true,将label属性定义为"Your E-Mail",将type属性定义为"email".

  3. 最后,通过创建一个v-on指令并将"$listeners"作为值,为事件创建事件侦听器。

完成所有步骤后,您的最终代码应该像这样:

<template>
  <q-input
  :value="value"
  :rules="[ val => (val && val.length > 0 && validateEmail(val)
    || 'Please type a valid E-mail')]"
  outlined
  type="email"
  label="Your E-mail"
  lazy-rules
  v-on="$listeners"
  /> </template>

这是您的组件呈现:

创建 AvatarInput 组件

对于AvatarInput组件,我们需要添加使用AWS-Amplify StorageAPI 驱动程序的逻辑。通过这样做,我们可以直接通过组件上传文件,并使逻辑和组件在整个应用程序中更具可重用性。

单文件组件

在这部分,我们将创建AvatarInput组件的<script>部分:

  1. quasar包中导入uid和从'src/driver/bucket'中导入uploadFile
import { uid } from 'quasar';
import { uploadFile } from 'src/driver/bucket';
  1. 创建一个默认导出的 JavaScript 对象,具有四个属性,namepropsdatamethods
export default {
  name: '',
  props: {},
  data: () => ({})
 methods: {}, };
  1. name属性中,将值定义为"AvatarInput"
name: 'AvatarInput',
  1. props属性中,添加一个属性value,作为 JavaScript 对象,内部有三个属性 - typedefaultrequired。将value.type设置为Object,将value.default设置为返回 JavaScript 对象的工厂函数,将value.required设置为false
props: {
  value: {
  type: Object,
  required: false,
  default: () => ({}),
  }, }, 
  1. data属性中,我们需要添加六个新属性:filetypenames3filephotoUrlcanUpload
  • file属性将是一个数组。

  • typenamephotoUrl将是字符串。

  • canUpload属性将是一个布尔值,定义为false

  • s3file将是一个具有三个属性的 JavaScript 对象,keybucketregion,它们都是字符串:

data: () => ({
  file: [],
  type: '',
  name: '',
  s3file: {
  key: '',
  bucket: '',
  region: '',
  },
  photoUrl: '',
  canUpload: false, }),
  1. methods属性上,我们需要添加一个名为uploadFile的新方法。这个方法将检查是否可以开始上传过程,然后调用uploadFile函数,传递this.filethis.namethis.type作为参数。在我们收到上传函数的响应后,我们将使用结果来定义this.s3File$emit以及事件'input'。最后,我们将this.canUpload定义为false
async uploadFile() {
  try {
  if (this.canUpload) {
  const file = await uploadFile(this.file, this.name, 
         this.type);
  this.s3file = file;
  this.$emit('input', file);
  this.canUpload = false;
 } } catch (err) {
  console.error(err);
 } }, 
  1. 最后,创建一个名为getFile的方法,它接收$event作为参数。在函数中,我们将把this.type定义为$event.type,将this.name定义为uid生成函数和文件名的连接。然后,我们将为FileReader实例创建一个监听器,它将把that.photoURL设置为读取的结果,并将that.canUpload设置为true
getFile($event) {
  this.type = $event.type;
  this.name = `${uid()}-${$event.name}`;
  const that = this;
  const reader = new FileReader();
  reader.onload = ({ target }) => {
  that.photoUrl = target.result;
  that.canUpload = true;
  };
  reader.readAsDataURL(this.file); },
单文件组件部分

现在是创建AvatarInput组件的<template>部分的时候了:

  1. 创建一个QFile组件,将v-model指令绑定到file数据属性。将outlinedbottom-slots属性定义为true,并将label属性设置为"Your Avatar"。对于class属性,将其设置为"q-pr-md",最后将@input事件监听器设置为目标getFile方法:
<q-file
  v-model="file"
  outlined
 bottom-slots label="Your Avatar"
  class="q-pr-md"
  @input="getFile" >
</q-file>
  1. QFile组件内部,我们将添加一个直接子组件,它将放置在一个命名为v-slot:before的插槽中,并且只有在数据属性中存在任何photoUrl时才会显示。在这个插槽中,我们将添加一个QAvatar组件,其子组件是一个HTML img标签,其中src属性绑定到photoUrl数据属性:
<template
  v-if="photoUrl"
  v-slot:before >
 <q-avatar>
 <img :src="photoUrl">
 </q-avatar> </template>
  1. 在我们创建的插槽之后,我们需要创建另一个插槽,现在放置在名为v-slot:after的插槽下面,里面有一个QBtn组件。QBtn将具有以下属性:rounddenseflaticon定义为"cloud_upload",并且@click事件监听器绑定到uploadFile方法:
<template v-slot:after>
 <q-btn
  round
 dense flat icon="cloud_upload"
  @click="uploadFile"
  /> </template>

这是您的组件渲染结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建 avatar mixin

在这里,我们将创建一个简单的 mixin,它将用于在新的对话组件和联系人页面中显示用户头像,或者如果没有定义头像,则显示用户名的首字母:

  1. src文件夹下创建一个名为mixins的新文件夹,然后创建一个名为getAvatar.js的文件,并打开它。

  2. driver/bucket文件中导入getFile函数。

  3. 导出一个带有methods属性的default JavaScript 对象。在methods属性内部,创建一个名为getAvatar的新函数。此函数将接收两个参数,objectname。对于此函数,我们将检查对象是否为null,以及是否有一个名字来显示初始字母。如果 JavaScript 对象中有属性,我们将返回getFile函数的结果,将key属性作为参数传递:

import { uploadFile } from 'src/driver/bucket';   export default {
  methods: {
  async getAvatar(object, name) {
  const baseUrl = 'http://placehold.jp/350/9c27b0/FFFFFF/600x600.png?text=';    if (object === null && !name) return `${baseUrl}%20`;    if (!object && name) return `${baseUrl}${name.split('').shift()}`;    return getFile(object.key);
 }, }, }; 

创建 AvatarDisplay 组件

AvatarDisplay将负责处理用户名的检查和验证,因此我们不需要在每个需要使用它的页面上重新编写所有规则。

单文件组件

在这里,我们将创建AvatarDisplay组件的<script>部分:

  1. components文件夹中创建一个名为AvatarDisplay.vue的新文件,并打开它。

  2. 创建一个带有以下属性的export default JavaScript 对象:namepropsmixinsbeforeMountdatawatchcomputedmethods

import { QImg } from 'quasar'; import getAvatar from 'src/mixins/getAvatar';   export default {
  name: '',
  props: {},
  mixins: [],
  async beforeMount() {},
  data: () => ({}),
  watch: {},
  computed: {},
  methods: {}, };
  1. 对于name属性,将其定义为"AvatarDisplay"
name: 'UsernameInput',
  1. 对于props属性,将其定义为 JavaScript 对象,并添加三个新属性,分别称为avatarObjectnametagavatarObject属性将是一个具有typedefaultrequired属性的 JavaScript 对象。nametag属性需要定义为Stringdefault''requiredfalse。对于tag属性,我们将将默认属性设置为'q-img'
props: {
  avatarObject: {
  type: Object,
  required: false,
  default: () => ({}),
  },
  name: {
  type: String,
  required: false,
  default: '',
  },
  tag: {
  type: String,
  required: false,
  default: 'q-img',
  }, },
  1. 对于mixins属性,我们将在数组中添加导入的getAvatar mixin:
mixins: [getAvatar],
  1. 现在,在data中返回 JavaScript 对象,我们将创建一个名为src的属性,其默认值为''
data: () => ({
  src: '', }),
  1. 然后对于computed属性,创建一个名为 components 的新属性,返回一个三元运算符,检查tag属性是否等于'q-img',并返回 Quasar 中导入的QImg组件;如果不是,则返回'img'标签:
computed: {
  componentIs() {
  return this.tag === 'q-img' ? QImg : 'img';
  }, },
  1. methods属性中,创建一个名为updateSrc的新方法。在这个方法中,我们将src定义为getAvatar方法的结果。我们将函数的参数传递给avatarObjectname属性:
methods: {
  async updateSrc() {
  this.src = await this.getAvatar(this.avatarObject, this.name);
  }, },
  1. beforeMount生命周期钩子中,我们将调用updateSrc方法:
async beforeMount() {
  await this.updateSrc(); },
  1. 最后,对于watch属性,创建两个属性,avatarObjectname。对于avatarObject属性,将其定义为一个具有两个属性handlerdeep的 Javascript 对象。在deep属性中,将其定义为true,在handler属性上,将其定义为调用updateSrc方法的函数。然后在name属性上,创建一个handler属性,定义为调用updateSrc方法的函数:
watch: {
  avatarObject: {
  async handler() {
    await this.updateSrc();
  },
  deep: true,
  },
  name: {
  async handler() {
    await this.updateSrc();
  },
  }, },
单文件组件部分

在这里,我们将创建AvatarDisplay组件的<template>部分:

  1. <template>部分,创建一个component元素。创建两个动态属性,srcis。现在,src将绑定到数据src,而is属性将绑定到componentIs计算属性。最后,创建一个spinner-color属性,并将其定义为'primary'

完成所有步骤后,您的最终代码应该像这样:

<template>
  <component
  :src="src"
  :is="componentIs"
  spinner-color="primary"
  /> </template>

它是如何工作的…

在这个示例中,我们学习了如何通过包装 Quasar Framework 的组件并在其上添加自定义逻辑来为我们的应用程序创建自定义组件。

这种技术允许开发独特的组件,可以在应用程序中重复使用,而无需重写逻辑使其正常工作。

对于UsernameinputNameinput,我们在QInput组件周围创建了一个包装器,添加了验证规则和文本,以便更轻松地开发和重用组件,而无需添加更多逻辑。

PasswordInput组件中,我们添加了控制密码可见性的逻辑,该逻辑会更改输入的类型,并自定义了QInput组件,以便有一个特殊按钮来触发可见性控制。

对于EmailInput,我们需要基于正则表达式创建自定义验证规则,检查输入的电子邮件是否是有效的电子邮件,并防止用户意外输入无效的电子邮件。

最后,在AvatarInput中,使用QFile组件,我们创建了一个自定义输入,当浏览器读取文件并将文件上传到 AWS Amplify Storage 时,自动上传文件,并在文件上传后将文件 URL 返回给应用程序。

另请参阅

创建应用程序布局

在我们的应用程序中,我们将使用一个基于布局组件的父路由的vue-router结构,以及我们正在尝试访问的页面的最终路由。

这种模式改进了我们应用程序的开发,因为我们可以在vue-router上创建父子责任划分。

在本教程中,我们将学习如何创建自定义布局,将我们的页面包装在vue-router的父子结构中。

准备工作

本教程的先决条件如下:

  • 最后的教程项目

  • Node.js 12+

所需的 Node.js 全局对象如下:

  • @aws-amplify/cli

  • @quasar/cli

要开始我们的应用程序自定义布局,我们将继续使用在为应用程序创建自定义输入中创建的项目。

如何做…

准备好我们的组件后,我们可以开始创建用于用户登录或注册到聊天应用程序或编辑其信息的布局,以及用于聊天消息页面的聊天布局。

创建基本布局

在我们的应用程序中,我们将使用一种基本布局的技术。它将成为应用程序所有内容的包装器。此布局将应用在布局执行中没有自定义更改的地方。

单文件组件

在这部分,我们将创建基本布局的

  1. layouts文件夹中创建一个名为Base.vue的新文件。

  2. 使用 JavaScript 对象创建一个export default实例,其中name属性定义为'BaseLayout'

<script> export default {
  name: 'BaseLayout', }; </script> 
单文件组件部分

在这里,我们将创建基本布局的部分:

  1. 创建一个QLayout组件,其中view属性定义为"hHh Lpr lff"
<q-layout view="hHh Lpr lff"> </q-layout>
  1. QLayout组件内部,我们需要添加一个带有elevated属性的QHeader组件:
<q-header elevated> </q-header>
  1. QHeader组件中,我们将添加一个QToolbar组件,其中包含一个QToolbarTitle组件作为子元素,以文本作为插槽占位符:
<q-toolbar>
 <q-toolbar-title>
  Chat App
  </q-toolbar-title> </q-toolbar>
  1. QHeader组件之后,创建一个带有RouterView组件的QPageContainer组件作为直接子元素:
<q-page-container>
 <router-view /> </q-page-container>

创建聊天布局

对于我们应用程序的经过身份验证的页面,我们将使用不同的页面布局,其中将有按钮供用户注销、管理其用户并浏览应用程序。

单文件组件

让我们创建聊天布局的

  1. layouts文件夹中创建一个名为Chat.vue的新文件。

  2. src/driver/auth.js中导入signOut函数:

import {signOut,} from 'src/driver/auth';
  1. 创建一个export default实例,包括一个 JavaScript 对象,其中包括两个属性:一个名为name的属性,定义为'ChatLayout',另一个名为methods的属性:
export default {
  name: 'ChatLayout',
  methods: {   }, };
  1. methods属性中,添加一个名为logOff的新异步函数;在这个函数中,我们将执行signOut函数,并在其后重新加载浏览器:
async logOff() {
  await signOut();
  window.location.reload(); }
单文件组件部分

在这里,我们将创建聊天布局的<template>部分:

  1. 创建一个带有view属性定义为"hHh Lpr lff"QLayout组件:
<q-layout view="hHh Lpr lff"> </q-layout>
  1. QLayout组件内部,我们需要添加一个带有elevated属性的QHeader组件:
<q-header elevated> </q-header> 
  1. 对于QHeader组件,我们将添加一个QToolbar组件,其中包含一个QToolbarTitle组件作为子元素,文本作为插槽占位符:
<q-toolbar>
 <q-toolbar-title>
  Chat App
  </q-toolbar-title> </q-toolbar>
  1. 对于QToolbar组件,在QToolbarTitle组件之前,我们将添加一个带有denseflatround属性定义为trueQBtn组件。在icon属性中,我们将添加一个三元表达式,验证$route.meta.goBack是否存在,以显示back图标或person图标。最后,对于to属性,我们将做同样的操作,但值将是$route.meta.goBack或一个具有name属性为Edit的 JavaScript 对象。
<q-btn
  dense
  flat
  round
  replace
  :icon="$route.meta.goBack ? 'keyboard_arrow_left' : 'person'"
  :to="$route.meta.goBack ? $route.meta.goBack : {name: 'Edit'}" />
  1. QToolbarTitle组件之后,我们将添加一个带有denseflatround属性的QBtn组件,这些属性被定义为true。对于icon属性,我们将定义为exit_to_app,对于@click指令,我们将传递logOff方法:
<q-btn
  dense
 flat round icon="exit_to_app"
  @click="logOff" /> 
  1. QHeader组件之后,创建一个带有RouterView组件作为直接子元素的QPageContainer组件:
<q-page-container>
 <router-view /> </q-page-container>

工作原理…

在这个示例中,我们学习了如何创建我们将在应用程序中使用的布局。这些布局是我们应用程序页面的包装器,使得在需要时可以轻松添加常见项目,如菜单、头部项目和页脚项目,而无需编辑每个页面文件。

对于创建的两种布局,我们使用了常见的QLayoutQHeaderQToolbarTitle组件。这些组件创建了页面的结构,包括布局容器、头部容器和自定义头部工具栏。

最后,对于聊天布局,我们在页眉菜单中添加了两个按钮:一个按钮可以是返回按钮或菜单,具体取决于路由中是否存在该参数;另一个是注销按钮,用户可以用它来从应用程序中注销。

另请参阅

第五章:创建用户 Vuex 模块、页面和路由

现在,是时候给应用程序一个可识别的面孔了。在本章中,我们将开始开发用户与应用程序之间的交互。

我们将利用我们从前面章节中收集的知识,通过使用自定义业务规则、Vuex 数据存储、特殊应用程序布局和用户可以交互的页面,将这个应用程序变得生动起来。

在本章中,我们将学习如何创建用户 Vuex 模块,以便我们可以存储和管理与用户、用户注册、登录、验证和编辑页面相关的一切。

在本章中,我们将涵盖以下配方:

  • 在您的应用程序中创建用户 Vuex 模块

  • 为您的应用程序创建用户页面和路由

让我们开始吧!

技术要求

在本章中,我们将使用Node.jsAWS AmplifyQuasar Framework

注意,Windows 用户! 您需要安装一个名为windows-build-toolsnpm包,以便能够安装所需的软件包。要做到这一点,以管理员身份打开 PowerShell 并执行> npm install -g windows-build-tools命令。

要安装Quasar Framework,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:

> npm install -g @quasar/cli

要安装AWS Amplify,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:

> npm install -g @aws-amplify/cli

在您的应用程序中创建用户 Vuex 模块

现在,是时候开始在我们的应用状态管理器或 Vuex 中存储数据了。在应用上下文中,存储的所有数据都保存在命名空间中。

在这个配方中,我们将学习如何创建用户 Vuex 模块。利用我们从上一章中获得的知识,然后创建动作来创建新用户、更新他们的数据、验证用户、在 Amplify 上登录用户,并列出应用程序中的所有用户。

准备工作

本配方的先决条件是 Node.js 12+。

本章所需的 Node.js 全局对象如下:

  • @aws-amplify/cli

  • @quasar/cli

要开始我们的用户 Vuex 存储模块,我们将继续使用我们在第四章中创建的项目,即创建自定义应用程序组件和布局

这个食谱将使用 GraphQL 查询和突变完成,以及它们的驱动程序,这些驱动程序是在第三章的创建您的第一个 GraphQL API为您的应用程序创建 AWS Amplify 驱动程序食谱中编写的。

如何做…

我们将把用户的 Vuex 模块的创建分为五个部分:创建statemutationsgettersactions,然后将模块添加到 Vuex 中。

创建用户的 Vuex 状态

要在 Vuex 模块上存储数据,我们需要一个将为我们存储数据的状态。按照以下步骤创建用户状态:

  1. store文件夹中,创建一个名为user的新文件夹。在内部,创建一个名为state.js的新文件并打开它。

  2. 创建一个名为createState的新函数,它返回一个 JavaScript 对象,提供idusernameemailnameavatarpasswordloadingvalidatederror属性。idusernameemailnamepassword属性将被定义为空字符串,而loadingvalidated属性将被定义为falseerror将被定义为undefinedavatar是一个具有三个属性的 JavaScript 对象-keybucketregion

export function createState() {
  return {
  id: '',
  username: '',
  email: '',
  name: '',
  avatar: {
  key: '',
  bucket: '',
  region: '',
  },
  password: '',
  loading: false,
  validated: false,
  error: undefined,
  }; }
  1. 最后,为了将状态导出为单例并将其作为 JavaScript 对象可用,我们需要export default执行createState函数:
export default createState();

创建用户的 Vuex mutations

要在状态上保存任何数据,Vuex 需要一个 mutation。按照以下步骤创建将管理此模块的 mutations 的用户 mutation:

  1. store/user文件夹内创建一个名为types.js的新文件并打开它。

  2. 在文件中,导出一个默认的 JavaScript 对象,提供CREATE_USERSET_USER_DATACLEAR_USERUSER_VALIDATEDLOADINGERROR属性。值与属性相同,但格式为字符串。

export default {
  CREATE_USER: 'CREATE_USER',   SET_USER_DATA: 'SET_USER_DATA',
  CLEAR_USER: 'CLEAR_USER',
  USER_VALIDATED: 'USER_VALIDATED',
  LOADING: 'LOADING',
  ERROR: 'ERROR', };
  1. store/user文件夹内创建一个名为mutations.js的新文件并打开它。

  2. 导入新创建的types.js文件和state.js中的createStateJavaScript 对象:

import MT from './types';  import { createState } from './state';
  1. 创建一个名为setLoading的新函数,状态作为第一个参数。在内部,我们将设置state.loadingtrue
function setLoading(state) {
 state.loading = true; }
  1. 创建一个名为setError的新函数,以state作为第一个参数,并以error作为第二个参数,其默认值为new Error()。在内部,我们将将state.error设置为error,将state.loading设置为false
function setError(state, error = new Error()) {
 state.error = error;
  state.loading = false; }
  1. 创建一个名为createUser的新函数,以state作为第一个参数,并以 JavaScript 对象作为第二个参数。这个 JavaScript 对象将提供idemailpasswordnameusername属性。所有属性都将是空字符串。在函数内部,我们将定义state属性为函数参数中收到的属性:
function createUser(state, {
 id = '',
  email = '',
  password = '',
  name = '',
  username = '', }) {
 state.username = username;
  state.email = email;
  state.name = name;
  state.id = id;
  state.password = window.btoa(password);
  state.loading = false; }
  1. 创建一个名为validateUser的新函数,以state作为第一个参数。在其中,我们将将state.validated属性设置为true,删除state.password属性,并将state.loading属性设置为false
function validateUser(state) {
 state.validated = true;
  delete state.password;
  state.loading = false; }
  1. 创建一个名为setUserData的新函数,以state作为第一个参数,并以 JavaScript 对象作为第二个参数。这个对象将提供idemailpasswordnameusername属性。它们都将是空字符串。avatar是一个具有三个属性的 JavaScript 对象:keybucketregion。在函数内部,我们将定义state属性为函数参数中收到的属性:
function setUserData(state, {
 id = '',
  email = '',
  name = '',
  username = '',
  avatar = {
  key: '',
  bucket: '',
  region: '',
  }, }) {
 state.id = id;
  state.email = email;
  state.name = name;
  state.username = username;
  state.avatar = avatar || {
  key: '',
  bucket: '',
  region: '',
  };    delete state.password;    state.validated = true;
  state.loading = false; }
  1. 创建一个名为clearUser的新函数,以state作为第一个参数。然后,在其中的函数中,我们将从createState函数获取一个新的干净的state,并迭代当前的state,将state属性的值重新定义为默认值:
function clearUser(state) {
  const newState = createState();    Object.keys(state).forEach((key) => {
 state[key] = newState[key];
  }); }
  1. 最后,导出一个默认的 JavaScript 对象,其中键是导入的变异类型,值是对应于每种类型的函数:
  • MT.LOADING设置为setLoading

  • MT.ERROR设置为setError

  • MT.CREATE_USER设置为createUser

  • MT.USER_VALIDATED设置为validateUser

  • MT.SET_USER_DATA设置为setUserData

  • MT.CLEAR_USER设置为clearUser

export default {
 [MT.LOADING]: setLoading,
  [MT.ERROR]: setError,
  [MT.CREATE_USER]: createUser,
  [MT.USER_VALIDATED]: validateUser,
  [MT.SET_USER_DATA]: setUserData,
  [MT.CLEAR_USER]: clearUser, };  

创建用户 Vuex getter

要访问存储在状态中的数据,我们需要创建一些“getter”。按照以下步骤为用户模块创建“getter”:

getter函数中,该函数将始终接收到 Vuexstore的当前state作为第一个参数。

  1. store/user文件夹内创建一个名为getters.js的新文件。

  2. 创建一个名为getUserId的新函数,返回state.id

const getUserId = (state) => state.id;
  1. 创建一个名为getUserEmail的新函数,返回state.email
const getUserEmail = (state) => state.email;
  1. 创建一个名为getUserUsername的新函数,返回state.username
const getUserUsername = (state) => state.username;
  1. 创建一个名为getUserAvatar的新函数,返回state.avatar
const getUserAvatar = (state) => state.avatar;
  1. 创建一个名为getUser的新函数,返回一个提供idnameusernameavataremail属性的 JavaScript 对象。这些属性的值将对应于state
const getUser = (state) => ({
  id: state.id,
  name: state.name,
  username: state.username,
  avatar: state.avatar,
  email: state.email, });
  1. 创建一个名为isLoading的新函数,返回state.loading
const isLoading = (state) => state.loading;
  1. 创建一个名为hasError的新函数,返回state.error
const hasError = (state) => state.error;
  1. 最后,导出一个带有创建的函数(getUserIdgetUserEmailgetUserUsernamegetUserAvatargetUserisLoadinghasError)作为属性的defaultJavaScript 对象:
export default {
  getUserId,
  getUserEmail,
  getUserUsername,
  getUserAvatar,
  getUser,
  isLoading,
  hasError, };

创建用户 Vuex 操作

按照以下步骤创建用户 Vuex 操作:

  1. store/user文件夹内创建一个名为actions.js的文件并打开它。

  2. 首先,我们需要导入这里将要使用的函数、枚举和类。

  • aws-amplifynpm 包中导入graphqlOperation

  • 从 GraphQL 查询中导入getUserlistUsers

  • 从 GraphQL 变异中导入createUserupdateUser

  • driver/auth.js中导入signUpvalidateUsersignIngetCurrentAuthUserchangePassword函数。

  • driver/appsync导入AuthAPI

  • ./types.js导入 Vuex 变异类型:

import { graphqlOperation } from 'aws-amplify';
import { getUser, listUsers } from 'src/graphql/queries';
import { createUser, updateUser } from 'src/graphql/mutations';
import { AuthAPI } from 'src/driver/appsync';
import {
  signUp,
  validateUser,
  signIn,
  getCurrentAuthUser,
  changePassword,
} from 'src/driver/auth';
import MT from './types';

  1. 创建一个名为initialLogin的新异步函数。此函数将接收一个 JavaScript 对象作为第一个参数。这将提供一个commit属性。在这个函数中,我们将获取当前认证的用户,从 GraphQL API 获取他们的数据,并将用户数据提交到 Vuex 存储中:
async function initialLogin({ commit }) {
  try {
  commit(MT.LOADING);    const AuthUser = await getCurrentAuthUser();    const { data } = await AuthAPI.graphql(graphqlOperation(getUser, {
    id: AuthUser.username,
  }));    commit(MT.SET_USER_DATA, data.getUser);    return Promise.resolve(AuthUser);
  } catch (err) {
  commit(MT.ERROR, err);
  return Promise.reject(err);
  } }
  1. 创建一个名为signUpNewUser的新异步函数。此函数将接收一个带有commit属性的 JavaScript 对象作为第一个参数。第二个参数也是一个 JavaScript 对象,但具有emailnamepassword属性。在这个函数中,我们将执行auth.js驱动器中的signUp函数来注册并在 AWS Cognito 用户池中创建用户,然后将用户数据提交到 Vuex 存储中:
async function signUpNewUser({ commit }, {
  email = '',
  name = '',
  username = '',
  password = '', }) {
  try {
  commit(MT.LOADING);    const userData = await signUp(email, password);    commit(MT.CREATE_USER, {
    id: userData.userSub,
    email,
    password,
    name,
    username,
  });    return Promise.resolve(userData);
  } catch (err) {
  commit(MT.ERROR, err);
  return Promise.reject(err);
  } }
  1. 创建一个名为createNewUser的新异步函数。这个函数将接收一个 JavaScript 对象作为第一个参数,其中包含commitstate属性。对于第二个参数,函数将接收一个code字符串。在这个函数中,我们将从state中获取用户数据,并执行auth.js驱动器中的validateUser函数,以检查用户是否是 AWS Cognito 用户池中的有效用户。然后,我们将执行auth.js中的signIn函数,将emailpassword作为参数传递,需要将password转换为加密的 base64 字符串,然后发送到函数。之后,我们将获取经过身份验证的用户数据,并将其发送到 GraphQL API 以创建一个新用户:
async function createNewUser({ commit, state }, code) {
  try {
  commit(MT.LOADING);
  const {
    email,
    name,
    username,
    password,
  } = state;
  const userData = await validateUser(email, code);    await signIn(`${email}`, `${window.atob(password)}`);    const { id } = await getCurrentAuthUser();    await AuthAPI.graphql(graphqlOperation(
    createUser,
    {
      input: {
        id,
        username,
        email,
        name,
      },
    },
  ));    commit(MT.USER_VALIDATED);    return Promise.resolve(userData);
  } catch (err) {
  commit(MT.ERROR, err);
  return Promise.reject(err);
  } }
  1. 创建一个名为signInUser的新异步函数。这个函数将接收一个 JavaScript 对象作为第一个参数,其中包含commitdispatch属性。第二个参数也是一个 JavaScript 对象,包含emailpassword属性。在这个函数内部,我们将执行auth.js驱动器中的signIn函数,将emailpassword作为参数传递,然后触发initialLogin Vuex 动作:
async function signInUser({ commit, dispatch }, { email = '', password = '' }) {
  try {
  commit(MT.LOADING);    await signIn(`${email}`, `${password}`);    await dispatch('initialLogin');    return Promise.resolve(true);
  } catch (err) {
  commit(MT.ERROR);
  return Promise.reject(err);
  } }
  1. 创建一个名为editUser的新异步函数。这个函数将接收一个 JavaScript 对象作为第一个参数,其中包含commitstate属性。第二个参数也是一个 JavaScript 对象,包含usernamenameavatarpasswordnewPassword属性。在这个函数内部,我们将合并state的值和作为参数接收到的新值。然后将它们发送到 GraphQL API 以更新用户信息。然后,我们将检查是否passwordnewPasssword属性都填写了。如果是,我们将执行auth.js驱动器中的changePassword函数,以在 AWS Cognito 用户池中更改用户的密码:
async function editUser({ commit, state }, {
  username = '',
  name = '',
  avatar = {
  key: '',
  bucket: '',
  region: '',
  },
  password = '',
  newPassword = '', }) {
  try {
  commit(MT.LOADING);    const updateObject = {
    ...{
      name: state.name,
      username: state.username,
      avatar: state.avatar,
    },
    ...{
      name,
      username,
      avatar,
    },
  };    const { data } = await AuthAPI.graphql(graphqlOperation(updateUser,
    { input: { id: state.id, ...updateObject } }));    if (password && newPassword) {
    await changePassword(password, newPassword);
  }    commit(MT.SET_USER_DATA, data.updateUser);    return Promise.resolve(data.updateUser);
  } catch (err) {
  return Promise.reject(err);
  } }
  1. 创建一个名为listAllUsers的新异步函数。这个函数将获取数据库中的所有用户并返回一个列表:
async function listAllUsers() {
  try {
  const {
    data: {
      listUsers: {
        items: usersList,
      },
    },
  } = await AuthAPI.graphql(graphqlOperation(
    listUsers,
  ));    return Promise.resolve(usersList);
  } catch (e) {
  return Promise.reject(e);
  } }
  1. 最后,我们将导出所有默认创建的函数:
export default {
  initialLogin,
  signUpNewUser,
  createNewUser,
  signInUser,
  editUser,
  listAllUsers, };

将用户模块添加到 Vuex

按照以下步骤将创建的用户模块导入到 Vuex 状态中:

  1. store/user文件夹内创建一个名为index.js的新文件。

  2. 导入我们刚刚创建的state.jsactions.jsmutation.jsgetters.js文件:

import state from './state'; import actions from './actions'; import mutations from './mutations'; import getters from './getters';
  1. 创建一个带有 JavaScript 对象的export default,提供stateactionsmutationsgettersnamespaced(设置为true)属性:
export default {
  namespaced: true,
  state,
  actions,
  mutations,
  getters, };
  1. 打开store文件夹中的index.js文件。

  2. store/user文件夹中导入新创建的index.js

import Vue from 'vue'; import Vuex from 'vuex'; import user from './user';
  1. 在新的 Vuex 类实例化中,我们需要添加一个名为modules的新属性,并将其定义为 JavaScript 对象。然后,我们需要添加一个新的user属性-这将自动用作值,因为它与上一步中导入的 User 模块具有相同的名称:
export default function (/* { ssrContext } */) {
  const Store = new Vuex.Store({
  modules: {
  user,
  },
  strict: process.env.DEV,
  });    return Store; }

工作原理…

当声明你的 Vuex 存储时,你需要创建三个主要属性:statemutationsactions。这些属性作为一个单一的结构,通过注入的$store原型或导出的store变量绑定到 Vue 应用程序。

state是一个集中的对象,保存着你的信息,并使其可以被mutationsactionscomponents使用。改变state总是需要通过mutation执行同步函数。

mutation是一个同步函数,可以改变state并被追踪。这意味着在开发时,你可以在 Vuex 存储中时间旅行通过所有执行的mutations

action是一个异步函数,可以用来保存业务逻辑、API 调用、分发其他actions和执行mutations。这些函数是当你需要对 Vuex 存储进行更改时的常见入口点。

Vuex 存储的简单表示可以在以下图表中看到:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在这个示例中,我们创建了 User Vuex 模块。该模块包括了所有的业务逻辑,将帮助我们在应用程序中管理用户,从创建新用户到更新用户。

当我们查看 Vuex actions 时,我们使用 AppSync API 客户端来获取数据并将其发送到我们的 GraphQL API。我们使用了由 Amplify CLI 创建的查询和 mutations。为了能够与 GraphQL API 通信,以便我们可以更新用户,我们从为应用程序创建 AWS Amplify driver配方中获取了我们在 Auth Driver 中使用的数据,第三章,设置我们的聊天应用程序 - AWS Amplify 环境和 GraphQL

这些 API 请求由 Vuex mutations 操纵,并存储在 Vuex 状态中,我们可以通过 Vuex getter 访问。

另请参阅

为您的应用程序创建用户页面和路由

在使用 Vue 应用程序时,您需要一种管理用户位置的方法。您可以使用动态组件来处理这个问题,但最好的方法是通过路由管理。

在这个食谱中,我们将学习如何创建我们的应用程序页面,其中包含每个路由所需的业务规则。然后,我们将使用路由管理来处理一切。

准备工作

此食谱的先决条件如下:

  • 我们在上一个食谱中创建的项目

  • Node.js 12+

此食谱所需的 Node.js 全局对象如下:

  • @aws-amplify/cli

  • @quasar/cli

要开始我们的用户页面和路由,我们将继续使用在在应用程序上创建用户 Vuex 模块食谱中创建的项目。

如何做…

在这个食谱中,我们将为我们的应用程序创建所有我们需要的用户页面:登录页面、注册页面和用户编辑页面。

将对话框插件添加到 Quasar

使用 Quasar 对话框插件,我们需要将其添加到配置文件中。

打开项目根文件夹内的quasar.conf.js文件,并找到framework属性。然后,在plugins属性中,将'Dialog'字符串添加到数组中,以便 Quasar 在启动应用程序时加载Dialog插件:

framework: {
 ...
  plugins: [
  'Dialog',
 ],
 ...
},

创建用户登录页面

对于用户登录页面,我们将使用之前创建的两个组件:PasswordInputEmailInput

单文件组件<script>部分

现在是创建用户登录页面的<script>部分的时候了:

  1. src/pages文件夹中,打开Index.vue文件。

  2. vuex包中导入mapActionsmapGetters函数:

import { mapActions, mapGetters } from 'vuex';
  1. 创建一个具有五个属性的export default JavaScript 对象;即name(定义为'Index'),componentsdatacomputedmethods
export default {
  name: 'Index',
  components: {
  },
  data: () => ({   }),
  computed: {   },
  methods: {   }, }; 
  1. components属性中,添加两个名为PasswordInputEmailInput的新属性。将PasswordInput定义为一个匿名函数,其返回值为import('components/PasswordInput'),并将EmailInput定义为一个匿名函数,其返回值为import('components/EmailInput')
components: {
  PasswordInput: () => import('components/PasswordInput'),
  EmailInput: () => import('components/EmailInput'), },
  1. data属性中,我们将返回一个提供两个属性emailpassword的 JavaScript 对象,它们都将是空字符串:
data: () => ({
  email: '',
  password: '', }),
  1. computed属性中,我们将解构mapGetters函数,将我们想要的模块的命名空间作为第一个参数(在本例中为'user')。我们将把我们想要导入的getters数组(在本例中为isLoading)作为第二个参数传递进去:
computed: {
  ...mapGetters('user', [
  'isLoading',
  'getUserId',
  ]), },
  1. beforeMount生命周期钩子上,我们将添加一个if语句,检查getUserId是否为真,并将用户重定向到Contacts路由。
async beforeMount() {
  if (this.getUserId) {
  await this.$router.replace({ name: 'Contacts' });
  } }, 
  1. 最后,在methods属性中,我们将解构mapActions函数,将我们想要的模块的命名空间(在本例中为'user')作为第一个参数传递进去。对于第二个参数,我们将使用一个包含我们想要导入的actions的数组(在这种情况下,这是signInUser)。接下来,我们需要添加异步的onSubmit方法,该方法将调度signInUser并将用户发送到Contacts路由,以及createAccount方法,该方法将用户发送到SignUp路由:
methods: {
  ...mapActions('user', [
  'signInUser',
  ]),
  async onSubmit() {
  try {
    await this.signInUser({
      email: this.email,
      password: this.password,
    });
    await this.$router.push({ name: 'Contacts' });
  } catch (e) {
    this.$q.dialog({
      message: e.message,
    });
  }
  },
  createAccount() {
  this.$router.push({ name: 'SignUp' });
  }, },
单文件组件<template>部分

现在,我们需要添加<template>部分来完成我们的页面:

  1. 创建一个名为QPage的组件,其class属性定义为"bg-grey-1 flex flex-center"
<q-page padding class="bg-grey-1 flex flex-center">
</q-page>
  1. QPage组件内部,创建一个QCard组件,其style属性定义为"width: 350px"
<q-card style="width: 350px">  </q-card> 
  1. QCard组件内部,创建一个带有class属性定义为no-marginQCardSection和一个h6子组件:
<q-card-section>
  <h6 class="no-margin">Chat Application</h6>  </q-card-section>
  1. 现在,创建一个QCardSection,其中包含一个QForm子组件,其 class 属性定义为q-gutter-md。在QForm组件内部,创建一个EmailInput组件,其v-model指令绑定到data.email,以及一个PasswordInput组件,其v-model指令绑定到data.password属性:
<q-card-section>
 <q-form
  class="q-gutter-md"
  >
 <email-input
  v-model.trim="email"
  />
 <password-input
  v-model.trim="password"
  />
 </q-form> </q-card-section>
  1. 然后,创建一个 QCardActions 组件,其中定义了一个 align 属性为 right。在内部,添加一个 QBtnlabel 属性设置为 "Create new Account"color 设置为 primaryclass 设置为 q-ml-smflat 设置为 true,并且 @click 事件监听器绑定到 createAccount 方法。接下来,创建另一个 QBtn 组件,label 属性设置为 "Login"type 设置为 "submit"color 设置为 primary,并且 @click 事件监听器绑定到 onSubmit 方法:
<q-card-actions align="right">
 <q-btn
  label="Create new account"
  color="primary"
  flat
 class="q-ml-sm"
  @click="createAccount"
  />
 <q-btn
  label="Login"
  type="submit"
  color="primary"
  @click="onSubmit"
  /> </q-card-actions>
  1. 最后,创建一个 QInnerLoading 组件,将 :showing 属性绑定到 computed.isLoading。这将需要有一个 QSpinner 子组件,提供 size 属性。将其设置为 50pxcolor 设置为 primary
<q-inner-loading :showing="isLoading">
 <q-spinner size="50px" color="primary"/> </q-inner-loading>

要运行服务器并查看您的进度,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:

> quasar dev

请记住始终执行命令 npm run lint --fix,以自动修复任何代码 lint 错误。

这是页面预览的样子:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建用户注册页面

对于用户注册页面,我们将使用已经创建的四个组件:NameInputUsernameInputPasswordInputEmailInput

单文件组件

在这里,我们将创建用户注册页面的 <script> 部分:

  1. 在 **src/pages ** 文件夹中,创建一个名为 SignUp.vue 的新文件并打开它。

  2. vuex 包中导入 mapActionsmapGetters 函数:

import { mapActions, mapGetters } from 'vuex';
  1. 创建一个 export default JavaScript 对象,提供五个属性:name(定义为 'SignUp'),componentsdatacomputedmethods
export default {
  name: 'SignUp',
 components: {},  data: () => ({   }),
  computed: {   },
  methods: {   }, };
  1. components 属性中,添加四个新属性:NameInputUsernameInputPasswordInputEmailInput。像这样定义它们:
  • NameInput 作为一个匿名函数,返回值为 import('components/NameInput')

  • UsernameInput 作为一个匿名函数,返回值为 import('components/UsernameInput')

  • PasswordInput 作为一个匿名函数,返回值为 import('components/PasswordInput')

  • EmailInput 作为一个匿名函数,返回值为 import('components/EmailInput')

这可以在以下代码中看到:

components: {
  PasswordInput: () => import('components/PasswordInput'),
  EmailInput: () => import('components/EmailInput'),
  UsernameInput: () => import('components/UsernameInput'),
  NameInput: () => import('components/NameInput'), }, 
  1. data 属性中,我们将返回一个提供四个属性的 JavaScript 对象 - nameusernameemailpassword - 其中所有属性都将是空字符串:
data: () => ({
  name: '',
  username: '',
  email: '',
  password: '', }),
  1. computed属性中,我们将解构mapGetters函数,将我们想要的模块的命名空间(在本例中为'user')作为第一个参数传递。对于第二个参数,我们将使用一个要导入的getters数组,这种情况下是isLoading
computed: {
  ...mapGetters('user', [
  'isLoading',
  ]), },
  1. 最后,对于methods属性,首先,我们将解构mapActions函数,将我们想要的模块的命名空间(在本例中为'user')作为第一个参数传递。对于第二个参数,我们将传递一个要导入的actions数组,这种情况下是signUpNewUser。接下来,我们需要添加异步的onSubmit方法,它将分发signUpNewUser,然后将用户发送到Validate路由,以及onReset方法,它将清除数据:
methods: {
  ...mapActions('user', [
  'signUpNewUser',
  ]),
  async onSubmit() {
  try {
    await this.signUpNewUser({
      name: this.name,
      username: this.username,
      email: this.email,
      password: this.password,
    });
    await this.$router.replace({ name: 'Validate' });
  } catch (e) {
    this.$q.dialog({
      message: e.message,
    });
  }
  },
  onReset() {
  this.email = '';
  this.password = '';
  }, },
单文件组件<template>部分

要完成页面,我们需要添加<template>部分:

  1. 创建一个QPage组件,class属性定义为"bg-grey-1 flex flex-center"
<q-page padding class="bg-grey-1 flex flex-center">
</q-page>
  1. QPage组件内部,创建一个QCard组件,style属性定义为"width: 350px"
<q-card style="width: 350px">  </q-card>
  1. QCard组件内部,创建一个QCardSection,其中包含一个h6子组件,其中class属性定义为no-margin
<q-card-section>
 <h6 class="no-margin">Create a new Account</h6> </q-card-section> 
  1. 之后,创建一个QCardSection,其中包含一个QForm子组件,其中class属性定义为q-gutter-md。在QForm组件内部,创建一个NameInput组件,v-model指令绑定到data.name,一个UsernameInput组件,v-model指令绑定到data.username,一个EmailInput组件,v-model指令绑定到data.email,以及一个PasswordInput组件,v-model指令绑定到data.password属性:
<q-card-section>
 <q-form   class="q-gutter-md"
  >
 <name-input
  v-model.trim="name"
  />
 <username-input
  v-model.trim="username"
  />
 <email-input
  v-model.trim="email"
  />
 <password-input
  v-model.trim="password"
  />
 </q-form> </q-card-section>
  1. 现在,创建一个QCardActions组件,align属性设置为right。在内部,添加一个QBtnlabel属性设置为"Reset"color设置为primaryclass设置为q-ml-smflat设置为true@click事件监听器绑定到onReset方法。然后,创建另一个QBtn组件,label属性设置为"Create"type设置为"submit"color设置为primary@click事件监听器绑定到onSubmit方法:
<q-card-actions align="right">
 <q-btn
  label="Reset"
  type="reset"
  color="primary"
  flat
 class="q-ml-sm"
  @click="onReset"
  />
 <q-btn
  label="Create"
  type="submit"
  color="primary"
  @click="onSubmit"
  /> </q-card-actions>
  1. 最后,创建一个QInnerLoading组件,:showing属性绑定到computed.isLoading。这将需要一个QSpinner子组件。size属性需要设置为50pxcolor需要设置为primary
<q-inner-loading :showing="isLoading">
 <q-spinner size="50px" color="primary"/> </q-inner-loading> 

要运行服务器并查看您的进度,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:

> quasar dev

记得始终执行命令npm run lint --fix,自动修复任何代码 lint 错误。

这是页面的预览:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建用户验证页面。

用户创建了一个账户后,AWS Amplify 将发送一封带有验证 pin 码的电子邮件,我们需要将其发送回来进行验证。这个页面将是验证页面。

单文件组件<script>部分

按照以下步骤创建用户验证页面的<script>部分:

  1. src/pages文件夹内,创建一个名为Validate.vue的新文件并打开它。

  2. vuex包中导入mapActionsmapGetters函数,以及从src/driver/auth导入resendValidationCode

import { mapActions, mapGetters } from 'vuex';
import { resendValidationCode } from 'src/driver/auth';    
  1. 创建一个export default的 JavaScript 对象,提供四个属性:name(定义为'Validate')、datacomputedmethods
export default {
  name: 'Validate',   data: () => ({   }),
  computed: {   },
  methods: {   }, };
  1. data属性内,我们将返回一个具有空字符串code属性的 JavaScript 对象:
data: () => ({
  code: '', }),
  1. computed属性内,我们将解构mapGetters函数,传递我们想要的模块的命名空间作为第一个参数,例如'user'。对于第二个参数,我们将传递一个要导入的getters数组,例如isLoadinggetUserEmail
computed: {
  ...mapGetters('user', [
  'isLoading',
  'getUserEmail',
 ]), }, 
  1. 最后,在methods属性中,我们将解构mapActions函数,传递我们想要的模块的命名空间作为第一个参数,例如'user'。对于第二个参数,我们将传递一个要导入的actions数组,例如createNewUser。接下来,我们需要添加异步的onSubmit方法,它将分发createNewUser并将用户发送到Index路由;resendCode方法,它将重新发送用户另一个验证代码;以及onReset方法,它将重置数据:
methods: {
  ...mapActions('user', [
  'createNewUser',
  ]),
  async onSubmit() {
  try {
    await this.createNewUser(this.code);
    await this.$router.replace({ name: 'Index' });
  } catch (e) {
    console.error(e);
    this.$q.dialog({
      message: e.message,
    });
  }
  },
  async resendCode() {
  await resendValidationCode(this.getUserEmail);
  },
  onReset() {
  this.code = '';
  }, },
单文件组件<template>部分

按照以下步骤创建用户验证页面的<template>部分:

  1. 创建一个QPage组件,定义class属性为"bg-grey-1 flex flex-center"
<q-page padding class="bg-grey-1 flex flex-center">
</q-page>
  1. QPage组件内部,创建一个QCard组件,定义style属性为"width: 350px"
<q-card style="width: 350px">  </q-card>
  1. QCard 组件内,创建一个带有 h6 子组件和定义为 no-marginclass 属性的 QCardSection。然后,创建一个兄弟元素,其 class 属性定义为 text-subtitle2
<q-card-section>
 <h6 class="no-margin">Validate new account</h6>
 <div class="text-subtitle2">{{ getUserEmail }}</div> </q-card-section>
  1. 创建一个带有两个子组件的 QCardSection。这些将是 HTML 元素 P
<q-card-section>
 <p>A validation code were sent to you E-mail.</p>
 <p>Please enter it to validate your new account.</p> </q-card-section>
  1. 之后,创建一个带有 QForm 子组件和定义为 q-gutter-mdclass 属性的 QCardSection。在 QForm 组件内,添加 QInput 组件作为子元素。然后,在 QInput 组件内,将 v-model 指令绑定到 data.code。在 QInputrules 属性内,将 rules 值定义为一个验证数组,用于检查是否已输入任何代码。启用 lazy-rules,以便它只在一段时间后进行验证:
<q-card-section>
 <q-form
  class="q-gutter-md"
  >
 <q-input
  v-model.trim="code"
  :rules="[ val => val && val.length > 0
  || 'Please type the validation code']"
  outlined
 label="Validation Code"
  lazy-rules
  />
 </q-form> </q-card-section> 
  1. 现在,创建一个带有 align 属性设置为 rightQCardActions 组件。在内部,添加一个 label 属性设置为 "Reset"color 设置为 primaryclass 设置为 q-ml-smflat 设置为 true,并且 @click 事件监听器绑定到 onReset 方法的 QBtn。创建另一个 label 属性设置为 "Re-send code"color 设置为 secondaryclass 设置为 q-ml-smflat 设置为 true,并且 @click 事件监听器绑定到 resendCode 方法的 QBtn。最后,创建一个带有 label 属性设置为 "Validate"type 设置为 "submit"color 设置为 primary,并且 @click 事件监听器绑定到 onSubmit 方法的 QBtn 组件:
<q-card-actions align="right">
 <q-btn
  label="Reset"
  type="reset"
  color="primary"
  flat
 class="q-ml-sm"
  />
 <q-btn
  flat
 label="Re-send code"
  color="secondary"
  class="q-ml-sm"
  @click="resendCode"
  />
 <q-btn
  label="Validate"
  type="submit"
  color="primary"
  @click="onSubmit"
  /> </q-card-actions>
  1. 最后,创建一个带有 :showing 属性绑定到 computed.isLoadingQInnerLoading 组件。它应该有一个 size 设置为 50pxcolor 设置为 primaryQSpinner 子组件:
<q-inner-loading :showing="isLoading">
 <q-spinner size="50px" color="primary"/> </q-inner-loading>

要运行服务器并查看您的进度,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:

> quasar dev 

记得始终执行命令 npm run lint --fix,自动修复任何代码 lint 错误。

这是页面的预览:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建用户编辑页面

对于用户编辑页面,我们将使用我们已经创建的四个组件:NameInputUsernameInputAvatarInputPasswordInput

单文件组件

按照以下步骤开始开发用户编辑页面的 <script> 部分:

  1. src/pages 文件夹内,创建一个名为 Edit.vue 的新文件并打开它。

  2. vuex包中导入mapActionsmapGetters函数:

import { mapActions, mapGetters } from 'vuex';
  1. 创建一个export default的 JavaScript 对象,提供四个属性:name(定义为'SignUp')、datacomputedmethods
export default {
  name: 'EditUser',
  components: {},   data: () => ({   }),
  created() {},
  computed: {   },
  methods: {   }, };
  1. components属性中,添加四个名为NameInputUsernameInputPasswordInputAvatarInput的新属性。设置它们如下:

NameInput作为一个匿名函数,返回值为import('components/NameInput')

UsernameInput作为一个匿名函数,返回值为import('components/UsernameInput')

PasswordInput作为一个匿名函数,返回值为import('components/PasswordInput')

AvatarInput作为一个匿名函数,返回值为import('components/AvatarInput')

components: {
  AvatarInput: () => import('/components/AvatarInput'),
  PasswordInput: () => import('components/PasswordInput'),
  UsernameInput: () => import('components/UsernameInput'),
  NameInput: () => import('components/NameInput'), },
  1. data属性中,我们将返回一个提供五个属性的 JavaScript 对象:nameusernameavataremailpassword。所有这些都将是空字符串:
data: () => ({
  name: '',
  username: '',
  avatar: '',
  password: '',
  newPassword: '', }), 
  1. created生命周期钩子中,将data.name定义为getUser.namedata.username定义为getUser.username,以及data.avatar定义为getUser.avatar
created() {
  this.name = this.getUser.name;
  this.username = this.getUser.username;
  this.avatar = this.getUser.avatar; },
  1. computed属性中,我们将解构mapGetters函数,传递我们想要的模块的命名空间作为第一个参数,这里是'user'。对于第二个参数,我们将传递一个要导入的getters数组,这种情况下是isLoading
computed: {
  ...mapGetters('user', [
  'getUser',
  'isLoading',
 ]), },
  1. 最后,在methods属性中,我们将解构mapActions函数,传递我们想要的模块的命名空间作为第一个参数,这里是'user'。对于第二个参数,我们将传递一个要导入的actions数组,这种情况下是editUser。接下来,我们需要添加异步的onSubmit方法,它将调度$refs.avatar.uploadFile(),然后调度editUser发送用户到Chat路由,以及onReset方法,它将清除数据:
methods: {
  ...mapActions('user', [
  'editUser',
  ]),
  async onSubmit() {
  try {
  await this.$refs.avatar.uploadFile();    await this.editUser({
  name: this.name,
  avatar: this.$refs.avatar.s3file,
  username: this.username,
  password: this.password,
  newPassword: this.newPassword,
  });   await this.$router.replace({ name: 'Contacts' });
  } catch (e) {
  this.$q.dialog({
  message: e.message,
  });
  }
 },
  onReset() {
  this.name = this.getUser.name;
  this.username = this.getUser.username;   this.password = '';
  this.newPassword = '';
  }, },
单文件组件部分

按照以下步骤创建用户编辑页面的<template>部分:

  1. 创建一个带有class属性定义为"bg-grey-1 flex flex-center"QPage组件:
<q-page padding class="bg-grey-1 flex flex-center">
</q-page>
  1. QPage组件内部,创建一个带有style属性定义为"width: 350px"QCard组件:
<q-card style="width: 350px">  </q-card>
  1. QCard组件内部,创建一个带有h6子组件的QCardSection,并且class属性定义为no-margin
<q-card-section>
 <h6 class="no-margin">Edit user account</h6> </q-card-section> 
  1. 之后,创建一个带有class属性定义为q-gutter-mdQCardSection,其中包含一个QForm子组件。在QForm组件内部,创建一个AvatarInput组件,其中reference指令定义为avatarv-model指令绑定到data.avatar,一个NameInput组件,其中v-model指令绑定到data.name,一个UsernameInput组件,其中v-model指令绑定到data.username,一个EmailInput组件,其中v-model指令绑定到data.email,以及一个PasswordInput组件,其中v-model指令绑定到data.password属性:
<q-card-section>
 <q-form
  class="q-gutter-md"
  >
 <avatar-input
  v-model="avatar"
  ref="avatar"
  />
 <name-input
  v-model.trim="name"
  />
 <username-input
  v-model.trim="username"
  />
 <q-separator/>
 <password-input
  v-model.trim="password"
  label="Your old password"
  />
 <password-input
  v-model.trim="newPassword"
  label="Your new password"
  />
 </q-form> </q-card-section> 
  1. 现在,创建一个带有align属性设置为rightQCardActions组件。在内部,添加一个label属性设置为"Reset"color设置为primaryclass设置为q-ml-smflat设置为true,并且@click事件监听器绑定到onReset方法的QBtn。然后,创建另一个QBtn组件,其中label属性设置为"Create"type设置为"submit"color设置为primary,并且@click事件监听器绑定到onSubmit方法:
<q-card-actions align="right">
 <q-btn
  label="Reset"
  type="reset"
  color="primary"
  flat
 class="q-ml-sm"
  @click="onReset"
  />
 <q-btn
  label="Update"
  type="submit"
  color="primary"
  @click="onSubmit"
  /> </q-card-actions> 
  1. 最后,创建一个QInnerLoading组件,其中:showing属性绑定到computed.isLoading。它应该有一个QSpinner子组件,size设置为50pxcolor设置为primary
<q-inner-loading :showing="isLoading">
 <q-spinner size="50px" color="primary"/> </q-inner-loading>

要运行服务器并查看您的进度,您需要打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:

> quasar dev 

请记住始终执行命令npm run lint --fix,以自动修复任何代码 lint 错误。

这是页面的预览:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建应用程序路由

现在我们已经创建了用户页面、组件和布局,我们需要将它们绑定在一起,以便用户可以访问。为此,我们需要创建路由并使其可用,以便用户可以在页面之间导航。按照以下步骤进行操作:

  1. 打开router文件夹内的routes.js文件。

  2. routes常量设置为空数组:

const routes = [];
  1. 向该数组添加一个具有三个属性pathcomponentchildren的 JavaScript 对象。path属性是一个字符串,将是一个静态 URL,component属性是一个匿名函数,将返回一个带有将要渲染的组件的 WebPackimport函数,children属性是一个将在path内渲染的组件数组。每个子组件都是一个具有相同属性的 JavaScript 对象,另外还有一个叫做name的新属性。
{
  path: '/',
  component: () => import('layouts/Base.vue'),
  children: [
  {
    path: '',
    name: 'Index',
    meta: {
      authenticated: false,
    },
    component: () => import('pages/Index.vue'),
  },
  ], },
  1. 现在,对于/chatURL,我们需要在pages文件夹内创建两个新的占位符页面:Contacts.vueMessages.vue。在这些文件内,创建一个带有以下模板的空组件:
<template>
  <div />
</template>
<script>
export default {
  name: 'PlaceholderPage',
};
</script>
  1. message路由内,我们需要添加两个特殊参数::idpath。这些参数将用于在用户之间获取特定的消息。
{
  path: '/chat',
  component: () => import('layouts/Chat.vue'),
  children: [
  {
    path: 'contacts',
    name: 'Contacts',
    component: () => import('pages/Contacts.vue'),
  },
  {
    path: 'messages/:id/:name',
    name: 'Messages',
    meta: {
      authenticated: true,
      goBack: {
        name: 'Contacts',
      },
    },
    component: () => import('pages/Messages.vue'),
  },
  ], },
  1. 对于/userURL,我们将只创建一个子路由,即edit路由。在这个路由内,我们使用alias属性,因为vue-router需要有一个path为空的子路由进行首次子路由渲染。我们还将在我们的应用程序内有一个/user/edit路由可用。
{
  path: '/user',
  component: () => import('layouts/Chat.vue'),
  children: [
  {
    path: '',
    alias: 'edit',
    name: 'Edit',
    meta: {
      authenticated: true,
      goBack: {
        name: 'Contacts',
      },
    },
    component: () => import('pages/Edit.vue'),
  },
  ], },
  1. 最后,对于创建新用户,我们需要添加/registerURL,其中包括两个子路由:SignUpValidateSignUp路由将是注册 URL 上的主要路由,并且当用户进入此 URL 时将直接调用。Validate路由只有在用户被重定向到/register/validateURL 时才会被调用。
{
  path: '/register',
  component: () => import('layouts/Base.vue'),
  children: [
  {
    path: '',
    alias: 'sign-up',
    name: 'SignUp',
    meta: {
      authenticated: false,
    },
    component: () => import('pages/SignUp.vue'),
  },
  {
    path: 'validate',
    name: 'Validate',
    meta: {
      authenticated: false,
    },
    component: () => import('pages/Validate.vue'),
  },
  ], },

添加身份验证守卫

为了在用户进入应用程序时验证用户身份令牌,如果令牌有效,或者用户试图访问无权限的路由,我们需要为我们的应用程序创建一个身份验证守卫。

  1. src/boot文件夹内创建一个名为routeGuard.js的新文件。

  2. 创建一个默认的导出异步函数。在这个参数内,添加一个名为app的 JavaScript 对象属性。在函数内部,创建一个常量,使用app的对象重构获取store属性。然后,创建一个try/catch块。在try部分,检查'user/getUserId'是否存在,如果不存在则调度'user/initialLogin'。最后,在catch块内,将用户重定向到Index路由。

export default async ({ app }) => {
  const { store } = app;    try {
  if (!store.getters['user/getUserId']) {
    await store.dispatch('user/initialLogin');
  }
  } catch {
  await app.router.replace({ name: 'Index' });
  } };  
  1. 最后,打开项目根文件夹内的quasar.conf.js文件,并找到boot属性。将'routerGuard'项添加到数组中。
boot: [
  'amplify',
  'axios',
  'routeGuard', ],

它是如何工作的…

在本章中,我们开发了微组件,如NameInputEmailInput等,以简化开发宏组件或容器(如页面)的过程。

在这个配方中,我们使用了在上一个配方中开发的组件来创建完整的页面,例如用户登录、用户编辑和用户注册页面。

使用vue-router来管理使用自定义布局包装页面的父子过程,我们使用了本书先前配方中创建的布局来为我们的应用程序创建路由。我们使它们可用,以便我们可以像正常的 Web 应用程序一样访问应用程序,具有自定义 URL 和路由。

最后,我们在我们的主初始化 Vue 文件中添加了一些身份验证中间件,以便我们可以重定向已经经过身份验证的用户。这意味着当他们第二次进入应用程序时,他们不需要再次进行身份验证。

还有…

现在,您的应用程序已准备好进行用户注册和登录。可以浏览用户注册页面,并从亚马逊收到一封带有验证代码的电子邮件,以便您可以在服务器上验证用户。

要检查您的进程并在本地环境中运行它,请打开终端(macOS 或 Linux)或命令提示符/PowerShell(Windows)并执行以下命令:

> quasar dev

请记住始终执行命令npm run lint --fix,以自动修复任何代码 lint 错误。

另请参阅

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值