vue firebase_如何使用Vue.js,Vuex,Vuetify和Firebase构建SPA:使用Firebase添加身份验证...

vue firebase

第4部分:了解如何使用Firebase添加身份验证和购物车 (Part 4: learn how to use Firebase to add authentication and a cart)

Learn how to create a meal delivery website using Vue.js, Vuex, Vue Router, and Firebase.

了解如何使用Vue.js,Vuex,Vue Router和Firebase创建送餐网站。

This is part four of my four-part series on building a Vue application. Here is a list of all the parts:

这是我构建Vue应用程序的四部分系列的第四部分。 这是所有部分的列表:

Part 1: Installing Vue and Building an SPA using Vuetify and Vue Router

第1部分:使用Vuetify和Vue路由器安装Vue并构建SPA

Part 2: Using Vue Router

第2部分:使用Vue路由器

Part 3: Using Vuex and accessing API

第3部分:使用Vuex和访问API

Part 4: Using Firebase for Authentication & Cart

第4部分:使用Firebase进行身份验证和购物车

回顾 (Recap)

In the first part of this series, we created our Vue application using the Vue CLI. Also, we added Vuetify to the app. We used Vuetify to style our home page.

在本系列的第一部分中,我们使用Vue CLI创建了Vue应用程序。 另外,我们将Vuetify添加到了该应用程序中。 我们使用Vuetify来设置主页样式。

In the second part, we used Vue Router to add navigation between the different pages of our app. We added components for all the pages in our application.

在第二部分中,我们使用Vue Router在应用程序的不同页面之间添加导航。 我们为应用程序中的所有页面添加了组件。

In the third part, we were introduced to Vuex. We signed up for an API to provide recipes and used axios to retrieve them. This data was stored in the Vuex store which made it accessible to every component in the application.

在第三部分中,我们介绍了Vuex。 我们注册了提供食谱的API,并使用axios来检索它们。 此数据存储在Vuex存储中,这使得应用程序中的每个组件都可以访问。

什么是Firebase? (What is Firebase?)

Firebase is a real-time cloud infrastructure for client-side apps. Firebase can turn any Frontend application into a full-stack product capable of scaling infinitely in the cloud. It abstracts away most of your complex server-side features like user authentication, data persistence, file storage, and microservices, so you can focus on building an awesome experience for the end user.

Firebase是用于客户端应用程序的实时云基础架构。 Firebase可以将任何前端应用程序变成能够在云中无限扩展的全栈产品。 它抽象出了大多数复杂的服务器端功能,例如用户身份验证,数据持久性,文件存储和微服务,因此您可以专注于为最终用户构建出色的体验。

The first step is to go to firebase and create a new account. Log in to the account that you created. You will see this dashboard:

第一步是转到firebase并创建一个新帐户 。 登录到您创建的帐户。 您将看到此仪表板:

Click the Add Project button. Enter a name for your project. I entered “meal-prep” for the name of my project. Check all checkboxes. Then click the create project button.

单击Add Project按钮。 输入项目的名称。 我为项目名称输入了“进餐准备”。 选中所有复选框。 然后单击create project按钮。

Once your project is created, Firebase will take you to your project’s home page.

创建项目后,Firebase会将您带到项目的主页。

We need to integrate our project’s configuration into our meal-prep application. Click on the web button to add Firebase to your application. (NOTE: if you are not sure which button that is, it is the button with the <;/>. In the image above, the button is right above the words “get started.” Click the copy button to copy the snippet to your clipboard.

我们需要将项目的配置集成到我们的饭前准备应用程序中。 单击Web按钮将Firebase添加到您的应用程序。 (注意:如果不确定是哪个按钮,则带有< ; />的按钮。在上图中,该按钮位于“入门”字样的正上方。单击复制按钮,将代码段复制到您的剪贴板。

Next, we need to incorporate this snippet into our meal prep application. You can initialize your firebase application in the main.js file. You can do it in the App.vue file.

接下来,我们需要将此代码段合并到我们的餐食准备应用程序中。 您可以在main.js文件中初始化main.js应用程序。 您可以在App.vue文件中执行App.vue操作。

Instead, we are going to create a new directory called firebase in the src folder. Inside this new directory create a file called index.js. Paste the contents of your clipboard into this file. Remove the two lines with the script tags. In the first line of the file import firebase. On the last line initialize firebase. Your file should look like this:

相反,我们将在src文件夹中创建一个名为firebase的新目录。 在这个新目录中,创建一个名为index.js的文件。 将剪贴板的内容粘贴到此文件中。 删除带有script标记的两行。 在文件的第一行中导入firebase。 在最后一行初始化firebase。 您的文件应如下所示:

import firebase from 'firebase';const config = {    apiKey: "<youKeyHere>",    authDomain: "<youKeyHere>",    databaseURL: "<youKeyHere>",    projectId: "<youKeyHere>",    storageBucket: "<youKeyHere>",    messagingSenderId: "<youKeyHere>"};firebase.initializeApp(config);

We are importing firebase from an npm package that we have not installed yet. Let’s install it now. In your terminal, install firebase with this command:

我们正在从尚未安装的npm软件包中导入firebase。 现在安装它。 在您的终端中,使用以下命令安装firebase:

npm install firebase --save

Now that we have installed firebase and created a config file, we need to add this file to our application so Vue is aware of it. Open up the main.js file and import in the config file we created. Here is what my main.js file looks like:

现在,我们已经安装了firebase并创建了一个配置文件,我们需要将此文件添加到我们的应用程序中,以便Vue意识到这一点。 打开main.js文件,然后导入我们创建的配置文件。 这是我的main.js文件的样子:

import '@babel/polyfill';import Vue from 'vue';import './plugins/vuetify';import App from './App.vue';import router from './router';import store from './store';import '@/firebase/';Vue.config.productionTip = false;new Vue({    router,    store,    render: h => h(App)}).$mount('#app');

Go back to your firebase console in the browser. Click on Authentication. Click on the set up sign-in method button.

返回浏览器中的Firebase控制台。 单击Authentication 。 单击set up sign-in method按钮。

In the list of sign-in providers, click on Email/Password:

在登录提供商列表中,单击“电子邮件/密码”:

Enable the option to all users to sign up using their email address and password. Then click the save button.

启用所有用户使用其电子邮件地址和密码进行注册的选项。 然后单击save按钮。

创建注册表格 (Creating Sign Up Form)

In a previous post, we stubbed out the Join.vue and Signin.vue files. These two files will have almost the same code. We will create the Join form first. When we are finished we will copy/paste it into the Signin form.

在上一篇文章中,我们对Join.vue和Signin.vue文件进行了存根处理。 这两个文件将具有几乎相同的代码。 我们将首先创建Join表单。 完成后,我们会将其复制/粘贴到“登录”表单中。

Open the Join.vue component. You can remove everything that is in the template. Vuetify has a default layout structure for components. It flows like this:

打开Join.vue组件。 您可以删除模板中的所有内容。 Vuetify具有组件的默认布局结构。 它像这样流动:

  • v-container

    V型容器
  • v-layout

    垂直布局
  • v-flex

    弯曲

So let’s create that layout now in the component. The start of our file looks like this:

现在让我们在组件中创建该布局。 我们文件的开始看起来像这样:

<template>    <v-container fill-height>        <v-layout align-center justify-center>            &lt;v-flex xs12 sm8 md4>            </v-flex>        </v-layout>    </v-container></template>

For the v-container we are adding fill-height. We add this so that it centers the form vertically in the window. For the v-flex we add xs12 sm8 and md4 values. This is similar to Bootstrap’s column width definition. On extra-small devices, the form will take up all 12 columns meaning the whole screen. On small devices, the form will be 3/4 of the screen wide. On medium and large screens the form will be 1/3 of the screen.

对于v-container我们要添加fill-height 。 我们添加它,使它在窗口中垂直居中。 对于v-flex我们添加xs12 sm8md4值。 这类似于Bootstrap的列宽定义。 在超小型设备上,该表格将占据全部12列,即整个屏幕。 在小型设备上,表格的宽度为屏幕的3/4。 在中型和大型屏幕上,表单将是屏幕的1/3。

Inside the v-flex we are going to use a v-card. We add class=”elevation-12" to the v-card so that it appears to be floating above the page. For the top of the form, we will use a v-toolbar. We give it a color of primary. For the default installation of Vuetify the primary color is blue. We want the text in the toolbar to be white text instead of the default black. To turn the text white we add dark to the v-toolbar.

v-flex内部,我们将使用v-card 。 我们添加class=”elevation-12"v-card ,以便它似乎是漂浮在网页上面。对于形的顶部,我们将使用一个v-toolbar ,我们给它的颜色primary 。对于Vuetify的默认安装是蓝色,我们希望工具栏中的文本是白色,而不是默认的黑色;要使文本变成白色,我们要在v-toolbar添加dark

Next, we have a v-card-text. Inside of it, we have a v-form. For the form we are giving it a reference with the name of form. We are assigning it to the v-model with a value of valid.

接下来,我们有一个v-card-text 。 在它里面,我们有一个v-form 。 对于表单,我们给它提供一个名为form的引用。 我们将其分配给具有validv-model

The last thing we add is lazy-validation. Our form needs to capture the user’s email and password. We will use two v-text-field to capture these values. To make things look better I have prepended an icon for each field. Each field has a v-model and rules.

我们添加的最后一件事是lazy-validation 。 我们的表单需要捕获用户的电子邮件和密码。 我们将使用两个v-text-field来捕获这些值。 为了使情况看起来更好,我为每个字段添加了一个图标。 每个字段都有一个v-modelrules

Before the form is submitted the field will be validated against all rules that are defined. If they pass then you can submit the form. We will take advantage of this when the user clicks the Join button.

在提交表单之前,将根据定义的所有规则验证该字段。 如果他们通过了,那么您可以提交表格。 当用户单击“加入”按钮时,我们将利用这一点。

The last item to add to the form is a button. We add a v-card-actions and add a button. Here is what the template looks like for our component:

要添加到表单的最后一项是按钮。 我们添加了v-card-actions并添加了一个按钮。 这是我们组件的模板的外观:

<template>    <v-container fill-height>        <v-layout align-center justify-center>            &lt;v-flex xs12 sm8 md4>                <v-card class="elevation-12">                    <v-toolbar dark color="primary">                        <v-toolbar-title>Join Form</v-toolbar-title>                    </v-toolbar>                    <;v-card-text>                        <v-form ref="form" v-model="valid" lazy-validation>;                            &lt;v-text-field prepend-icon="person" name="email" label="Email" type="email"                                          v-model="email" :rules="emailRules" required>                            </v-text-field>                            <v-text-field prepend-icon="lock" name="password" label="Password" id="password"                                          type="password" required v-model="password" :rules="passwordRules">                            </v-text-field>                        </v-form>                    </v-card-text>                    <v-card-actions>                        <v-spacer></v-spacer>                        <v-btn color="primary" :disabled="!valid" @click="submit">Join</v-btn>                    </v-card-actions>                </v-card>            </v-flex>        </v-layout>    </v-container></template>

We defined several models in our template. We need to add them to the data section of our script. In the script add a data object. We will add valid, email, password, emailRules and passwordRules.

我们在模板中定义了几个模型。 我们需要将它们添加到脚本的data部分。 在脚本中添加一个数据对象。 我们将添加有效的,电子邮件,密码,emailRules和passwordRules。

Email and password will contain the values the user inputs into the two text fields. Valid will tell if our form has passed all the rules we have created. For email, we check to make sure that the field is not empty. We also check that the contents match a basic RegExp to validate the email address. For password, we check to make sure the field is not empty. We also check to make sure the password is at least six characters in length.

电子邮件和密码将包含用户在两个文本字段中输入的值。 Valid将告诉我们表单是否通过了我们创建的所有规则。 对于电子邮件,我们检查以确保该字段不为空。 我们还检查内容是否与基本RegExp匹配以验证电子邮件地址。 对于密码,我们检查以确保该字段不为空。 我们还会检查以确保密码长度至少为六个字符。

Here is what the data object looks like now:

这是数据对象现在的样子:

data() {    return {        valid: false,        email: '',        password: '',        emailRules: [            v => !!v || 'E-mail is required',            v => /.+@.+/.test(v) || 'E-mail must be valid'        ],        passwordRules: [            v => !!v || 'Password is required',            v =>                v.length >= 6 ||                'Password must be greater than 6 characters'        ]    };},

The last thing we need to add is methods. In methods, we have submit(). This method will validate our form first. If it passes validation then it will call an action in our Vuex store called userJoin. We pass it the email and password the user entered in the form.

我们需要添加的最后一件事是方法。 在方法中,我们有submit() 。 此方法将首先验证我们的表单。 如果通过验证,它将在我们的Vuex存储中调用一个名为userJoin 。 我们向它传递用户在表格中输入的电子邮件和密码。

Here is what the methods look like:

这些方法如下所示:

methods: {    submit() {        if (this.$refs.form.validate()) {            this.$store.dispatch('userJoin', {                email: this.email,                password: this.password            });        }    }}
在Vuex中创建userJoin操作 (Creating userJoin Action in Vuex)

Open up the store.js file. We will create a new action called userJoin. By default the first parameter passed to this action is context. I will use object destructuring to get just commit from context. Commit is how I will call my mutation.

打开store.js文件。 我们将创建一个名为userJoin的新操作。 默认情况下,传递给该操作的第一个参数是context 。 我将使用对象分解来从context commit 。 承诺就是我所说的突变。

I will be using firebase to create the new user in the firebase database. To be able to use firebase in the store, I will need to import it. At the top of the file import firebase with this command:

我将使用firebase在firebase数据库中创建新用户。 为了能够在商店中使用Firebase,我需要将其导入。 使用以下命令在文件顶部导入firebase:

import firebase from 'firebase';

Firebase authentication provides a method called createUserWithEmailAndPassword. We will pass the user’s email and password into this method. If it succeeds in registering the user it will return a user object. When it succeeds we will call two mutations: setUser and setIsAuthenticated. Here is what the action looks like:

Firebase身份验证提供了一种名为createUserWithEmailAndPassword的方法。 我们会将用户的电子邮件和密码传递给此方法。 如果成功注册用户,它将返回一个用户对象。 成功后,我们将调用两个突变: setUsersetIsAuthenticated 。 动作如下所示:

userJoin({ commit }, { email, password }) {    firebase        .auth()        .createUserWithEmailAndPassword(email, password)        .then(user => {            commit('setUser', user);            commit('setIsAuthenticated', true);        })        .catch(() => {            commit('setUser', null);            commit('setIsAuthenticated', false);        });}

This action calls two mutations. So let’s create then now. In mutations add a new mutation called setUser. Set the state value of user to the payload. Next, create a second mutation called setIsAuthenticated. Set the state value of isAuthenticated to the payload. Here is what the two mutations look like:

该动作称为两个突变。 因此,让我们现在创建。 在突变中添加一个名为setUser的新突变。 将用户的状态值设置为有效负载。 接下来,创建另一个名为setIsAuthenticated突变。 将isAuthenticated的状态值设置为有效负载。 这是两个突变的样子:

setUser(state, payload) {    state.user = payload;},setIsAuthenticated(state, payload) {    state.isAuthenticated = payload;}

In state, we need to add two new value: user and isAuthenticated. Here is what state looks like now:

在状态下,我们需要添加两个新值: userisAuthenticated 。 这是现在的状态:

state: {    recipes: [],    apiUrl: 'https://api.edamam.com/search',    user: null,    isAuthenticated: false},
测试添加新用户 (Test Adding a New User)

Start your server with the command npm run serve. Click on the Join button in the navigation. Enter your email and a password and click the join button. When you click the button nothing visible happens. To verify that the user was registered, go the firebase console in your browser. Click on Authentication. You should see a list of users that have been registered for your application. Here you can see that the user I just registered was created.

使用命令npm run serve启动服务器。 单击导航中的Join按钮。 输入您的电子邮件和密码,然后单击加入按钮。 当您单击按钮时,看不见任何东西。 要验证用户是否已注册,请在浏览器中进入firebase控制台。 单击Authentication 。 您应该看到已为您的应用程序注册的用户列表。 在这里您可以看到我刚刚注册的用户已创建。

We need to notify the user that they have been successfully created. We will do this but later. First, we are going to copy and paste the contents of the Join.vue component into the Signin.vue component. There are only two changes you will need to make in the template. Change the title to “Login Form.” For the button make the text say “Login.” In the submit method have it dispatch to userLogin. Just to make sure you have it correct, here is what my entire Signin.vue file looks like:

我们需要通知用户它们已经成功创建。 我们会这样做,但稍后。 首先,我们将复制Join.vue组件的内容并将其粘贴到Signin.vue组件中。 您只需在模板中进行两项更改。 将标题更改为“登录表单”。 对于按钮,使文本说“登录”。 在userLogin方法中将其调度到userLogin 。 为了确保您设置正确无误,这是我整个Signin.vue文件的外观:

<template>    <v-container fill-height>        <v-layout align-center justify-center>            &lt;v-flex xs12 sm8 md4>                <v-card class="elevation-12">                    <v-toolbar dark color="primary">                        <v-toolbar-title>Login Form</v-toolbar-title>                    </v-toolbar>                    <;v-card-text>                        <v-form ref="form" v-model="valid" lazy-validation>;                            &lt;v-text-field prepend-icon="person" name="email" label="Email" type="email"                                          v-model="email" :rules="emailRules" required>                            </v-text-field>                            <v-text-field prepend-icon="lock" name="password" label="Password" id="password"                                          type="password" required v-model="password" :rules="passwordRules">                            </v-text-field>                        </v-form>                    </v-card-text>                    <v-card-actions>                        <v-spacer></v-spacer>                        <v-btn color="primary" :disabled="!valid" @click="submit">Login</v-btn>                    </v-card-actions>                </v-card&gt;            </v-flex&gt;        </v-layout>    </v-container></template><script>export default {    name: 'Signin',    data() {        return {            valid: false,            email: '',            password: '',            emailRules: [                v => !!v || 'E-mail is required',                v => /.+@.+/.test(v) || 'E-mail must be valid'            ],            passwordRules: [                v => !!v || 'Password is required',                v =&gt;                    v.length >= 6 ||                    'Password must be greater than 6 characters'            ]        };    },    methods: {        submit() {            if (this.$refs.form.validate()) {                this.$store.dispatch('userLogin', {                    email: this.email,                    password: this.password                });            }        }    }};</script><style scoped></style>

That’s it. You have now created both the Join and Login forms.

而已。 现在,您已经创建了“加入”和“登录”表单。

We need to create the action for Login. Open up the store.js file. Create a new action called userLogin. We will use firebase to login the user. Firebase provides a method called signInWithEmailAndPassword. We will call this method and pass in the user’s email and password they entered into the form. If the user entered their email and password correctly then we will call the two mutations setUser and setIsAuthenticated. Here is what the userLogin action looks like:

我们需要为登录创建操作。 打开store.js文件。 创建一个名为userLogin的新操作。 我们将使用Firebase登录用户。 Firebase提供了一种名为signInWithEmailAndPassword的方法。 我们将调用此方法,并将用户输入的电子邮件和密码输入表单。 如果用户正确输入了他们的电子邮件和密码,那么我们将调用两个突变setUsersetIsAuthenticated 。 这是userLogin操作的样子:

userLogin({ commit }, { email, password }) {    firebase        .auth()        .signInWithEmailAndPassword(email, password)        .then(user => {            commit('setUser', user);            commit('setIsAuthenticated', true);        })        .catch(() => {            commit('setUser', null);            commit('setIsAuthenticated', false);        });},
重定向到个人资料 (Redirecting to Profile)

When a user successfully registers or login, we want to redirect them to their profile. When we created our app initially the Vue CLI 3 it created two routes for us. Those routes were / and /about. Eventually, the profile will contain a list of all recipes that the user has ordered from the menu page. Remember the button we put at the bottom of every recipe? That button will add the recipe to the user’s profile and store it in the database in firebase.

用户成功注册或登录后,我们希望将他们重定向到其个人资料。 最初创建应用程序时,Vue CLI 3为我们创建了两条路由。 这些路线是//about 。 最终,配置文件将包含用户从menu页面订购的所有食谱的列表。 还记得我们放在每个食谱底部的按钮吗? 该按钮会将配方添加到用户的个人资料中,并将其存储在firebase中的数据库中。

To redirect the user to the profile we will first import router at the top of the store.js file. The router is imported with the command:

为了将用户重定向到配置文件,我们将首先在store.js文件顶部导入路由器。 使用以下命令导入路由器:

import router from '@/router';

Next, in both actions we redirect the user to /about if they successfully register or login. You can do the redirection with this command:

接下来,在这两个操作中,如果用户成功注册或登录,我们会将用户重定向到/ about。 您可以使用以下命令进行重定向:

router.push('/about');

If the user fails to register an account or login successfully we will redirect the user to the home page. (NOTE: in a perfect scenario we will provide some notice to the user why the registration or login failed). You can redirect them to the home page with this command:

如果用户无法注册帐户或无法成功登录,我们会将用户重定向到主页。 (注意:在理想情况下,我们将向用户提供一些注册或登录失败的通知)。 您可以使用以下命令将它们重定向到主页:

router.push('/');

To test the redirection, start your server and click on the Login button. Enter the email and password you used when you created your user account. Click the Join button. If everything worked successfully you should be redirected to the About page.

要测试重定向,请启动服务器,然后单击“登录”按钮。 输入创建用户帐户时使用的电子邮件和密码。 单击加入按钮。 如果一切顺利,则应将您重定向到“关于”页面。

更新导航 (Updating the navigation)

The navigation has buttons for Sign In and Join. When a user successfully registers or login we would like to hide these two buttons. In their place, we want to show a Logout button.

导航中包含用于Sign InJoin按钮。 用户成功注册或登录后,我们想隐藏这两个按钮。 在他们的位置,我们要显示一个Logout按钮。

Open up the AppNavigation component. We are going to group the two current buttons in a div. We are going to remove the class to hide the buttons on small and extra-small devices. Instead, we will place this class on the div. We add a v-if to the div to only show if the user is currently not authenticated. Below the div we will add a new button for Logout. This new button will have a style of outline with a color of white. When you click on this button it will call the method logout. We add a v-else to this button to show when the user is authenticated.

打开AppNavigation组件。 我们将在div中将两个当前按钮分组。 我们将删除该类以隐藏小型和超小型设备上的按钮。 相反,我们将把此类放在div上。 我们在div中添加v-if以仅显示当前未通过身份验证的用户。 在div下面,我们将为注销添加一个新按钮。 此新按钮将具有白色的轮廓样式。 当您单击此按钮时,它将调用方法logout 。 我们在此按钮上添加了v-else,以显示用户通过身份验证的时间。

Next, add a method called logout. This method will call an action in our store called userSignOut.

接下来,添加一个名为logout的方法。 此方法将调用我们商店中名为userSignOut

We also need to add a new computed property called isAuthenticated. This property returns the value of isAuthenticated in the state of our store.

我们还需要添加一个称为isAuthenticated的新计算属性。 此属性返回商店状态中isAuthenticated的值。

Here is what your AppNavigation should look like:

这是您的AppNavigation外观:

<template>    <span>        <v-navigation-drawer app v-model="drawer" class="brown lighten-2" dark disable-resize-watcher>            <v-list>                <template v-for="(item, index) in items">                    <v-list-tile :key="index">                        <v-list-tile-content>                            {{item.title}}                        </v-list-tile-content>                    <;/v-list-tile>                    <v-divider :key="`divider-${index}`"></v-divider>                </template>            </v-list>        </v-navigation-drawer>        <v-toolbar app color="brown darken-4" dark>            <v-toolbar-side-icon class="hidden-md-and-up" @click="drawer = !drawer"></v-toolbar-side-icon>            <v-spacer class="hidden-md-and-up"></v-spacer>            <router-link to="/"&gt;                <v-toolbar-title to="/">{{appTitle}}</v-toolbar-title>;            </router-link>            <;v-btn flat class="hidden-sm-and-down" to="/menu">Menu</v-btn>            <v-spacer class="hidden-sm-and-down"></v-spacer>            <;div v-if="!isAuthenticated" class="hidden-sm-and-down">                <v-btn flat to="/sign-in">SIGN IN</v-btn>                <v-btn color="brown lighten-3" to="/join">JOIN</v-btn>            </div>            <v-btn v-else outline color="white" @click="logout">Logout</v-btn>        </v-toolbar>    </span></template><script>export default {    name: 'AppNavigation',    data() {        return {            appTitle: 'Meal Prep',            drawer: false,            items: [{ title: 'Menu' }, { title: 'Sign In' }, { title: 'Join' }]        };    },    computed: {        isAuthenticated() {            return this.$store.getters.isAuthenticated;        }    },    methods: {        logout() {            this.$store.dispatch('userSignOut');        }    }};</script><style scoped>a {    color: white;    text-decoration: none;}</style>

We need to add the getter and action we just defined. Open up the store.js file. Create a new action called userSignout. This action will use firebase.auth() to sign the user out. After signing the user out, it sets the state variables user to null and isAuthenticated to false. Here is the userSignout method in the store:

我们需要添加刚刚定义的吸气剂和动作。 打开store.js文件。 创建一个名为userSignout的新操作。 此操作将使用firebase.auth()将用户注销。 将用户注销后,它将状态变量user为null,并将isAuthenticated为false。 这是商店中的userSignout方法:

userSignOut({ commit }) {    firebase        .auth()        .signOut()        .then(() => {            commit('setUser', null);            commit('setIsAuthenticated', false);            router.push('/');        })        .catch(() => {            commit('setUser', null);            commit('setIsAuthenticated', false);            router.push('/');        });}

Next, we need to add a getters section to the store object. The isAuthenticated getters method will return true or false based on user authentication. Here is what the getters section of the store looks like:

接下来,我们需要向商店对象添加一个getters部分。 isAuthenticated getters方法将根据用户身份验证返回true或false。 这是商店的getters部分的样子:

getters: {    isAuthenticated(state) {        return state.user !== null && state.user !== undefined;    }}

将配方添加到数据库 (Adding Recipes to Database)

Once a user is logged in they can click on any recipe to add it to their account. Their recipes will be shown in their profile which is the /about route. We need a database to store these recipes. Go to your firebase console in the browser. Click on database in the left side navigation panel. On the next screen you will see buttons to create a realtime database or a cloud firestore database. Make sure you create a new realtime database. In the dialog make sure you select to start in test mode. Then click the enable button.

用户登录后,可以单击任何配方将其添加到他们的帐户中。 他们的食谱将显示在其个人资料(即/about路线)中。 我们需要一个数据库来存储这些食谱。 在浏览器中转到您的Firebase控制台。 在左侧导航面板中单击database 。 在下一个屏幕上,您将看到用于创建实时数据库或Cloud Firestore数据库的按钮。 确保创建一个新的实时数据库。 在对话框中,确保您选择以start in test mode 。 然后单击enable按钮。

Now we want to store the user’s recipes in the database. Open up the MealRecipes component. If a user tries to order a recipe and they are not logged in then we should redirect them to the login page. So let’s take care of that now. On the Order button add an @click that calls the method orderRecipe. Be sure to pass in item as an argument to the method. Your button should look like this:

现在,我们要将用户的配方存储在数据库中。 打开MealRecipes组件。 如果用户尝试订购食谱而他们尚未登录,则我们应将其重定向到登录页面。 因此,让我们现在来照顾它。 在“ Order按钮上,添加一个@click,调用方法orderRecipe。 确保将item作为方法的参数传递。 您的按钮应如下所示:

<v-card-actions>    &lt;v-btn color="green" dark @click="orderRecipe(item)">Order</v-btn></v-card-actions>

Before we create our method, we will create a computed value for isAuthenticated. This is the exact same code we used in AppNavigation earlier to show and hide the login and logout button correctly. Add a computed isAuthenticated. It should look like this:

在创建方法之前,我们将为isAuthenticated创建一个计算值。 这与我们之前在AppNavigation使用的代码AppNavigation ,可以正确显示和隐藏登录和注销按钮。 添加计算的isAuthenticated。 它看起来应该像这样:

export default {    name: 'MealRecipes',    computed: {        recipes() {            return this.$store.state.recipes;        },        isAuthenticated() {            return this.$store.getters.isAuthenticated;        }    }};

Now we are ready to create our orderRecipe method. Add this method and its parameter. In this method, we want to first check if the user is logged in or not. If they are not we want to redirect them to /sign-in . If they are signed in then we want to call an action in the Vuex store that will append the recipe to the user account in the database. Here is what our method looks like:

现在我们准备创建orderRecipe方法。 添加此方法及其参数。 在这种方法中,我们要首先检查用户是否已登录。 如果不是,我们希望将其重定向到/sign-in 。 如果他们已登录,则我们要在Vuex存储中调用一个操作,该操作会将配方追加到数据库中的用户帐户。 这是我们的方法:

methods: {    orderRecipe(item) {        if (this.isAuthenticated) {            this.$store.dispatch('addRecipe', item);        } else {            this.$router.push('/sign-in');        }    }}

Open up the store.js file. We need to create a new action to add recipes. In this action, we are going to use firebase to add the recipe to a database called users. When the user was registered in firebase they were assigned a unique userid. We will be using this uid to store the name of the recipe in the database.

打开store.js文件。 我们需要创建一个新操作来添加配方。 在此操作中,我们将使用firebase将配方添加到名为users的数据库中。 在Firebase中注册用户后,会为其分配一个唯一的用户ID。 我们将使用该uid将配方名称存储在数据库中。

In this action, we will be using state to get the value of the currently selected user. The user in state is an object. That object has a key called user. In that object, we will find the uid. We use that to push the title of the selected recipe into the database. Here is the action:

在此操作中,我们将使用state来获取当前所选用户的值。 处于stateuser是一个对象。 该对象具有一个称为用户的密钥。 在该对象中,我们将找到uid 。 我们用它来将所选配方的标题推送到数据库中。 这是动作:

addRecipe({ state }, payload) {    firebase        .database()        .ref('users')        .child(state.user.user.uid)        .push(payload.recipe.label);}

Now start your server and log in. Select a diet from the menu page. Then order a couple of recipes. The recipes you ordered should be shown in the database in firebase.

现在启动服务器并登录。从菜单页面选择一种饮食。 然后点几个食谱。 您订购的食谱应显示在firebase的数据库中。

Now that we have the recipes added to the database, we actually need to display them on the profile page for the user. Open up the About.vue file. Whenever this page is loaded it should fetch all the user’s recipes. To do this we add mounted() in our script. This will call a method called getRecipes.

现在我们已经将配方添加到数据库中,实际上,我们需要在用户的个人资料页面上显示它们。 打开About.vue文件。 每当加载此页面时,它都应获取所有用户的食谱。 为此,我们在脚本中添加mounted() 。 这将调用一个名为getRecipes的方法。

Let’s create that method now. In the method, we are going to call an action in our Vuex store that will get all the user’s recipes. We have not created this action in the store yet but in simple terms, this action will get the user’s recipes. Then it will store them in a variable in state called userRecipes.

现在创建该方法。 在该方法中,我们将在Vuex存储中调用一个操作,该操作将获取所有用户的食谱。 我们尚未在商店中创建此操作,但简单来说,此操作将获取用户的食谱。 然后它将它们存储在stateuserRecipes的变量中。

Before we leave About.vue, add a computed property for userRecipes. This will return the userRecipes from state in our store. This is what About.vue script should look like:

在离开About.vue之前,为userRecipes添加一个计算属性。 这将从我们商店的state返回userRecipes 。 这是About.vue脚本应如下所示:

export default {    name: 'About',    computed: {        userRecipes() {            return this.$store.state.userRecipes;        }    },    mounted() {        this.getRecipes();    },    methods: {        getRecipes() {            this.$store.dispatch('getUserRecipes');        }    }};

Next, open up your store.js file. We need to create the getUserRecipes action. When the user logins we store a variable in state called user. This variable will have the unique user ID assigned to that user when it was registered in firebase. We want to get all the recipes in the users database that have this user ID. Once we get all the recipes we want to set userRecipes to contain them. Here is the getUserRecipes action:

接下来,打开store.js文件。 我们需要创建getUserRecipes操作。 当用户登录时,我们会存储一个state为user的变量。 在Firebase中注册时,此变量将具有分配给该用户的唯一用户ID。 我们希望在用户数据库中获得具有该用户ID的所有配方。 一旦获得所有配方,我们便希望将userRecipes设置为包含它们。 这是getUserRecipes操作:

getUserRecipes({ state, commit }) {    return firebase        .database()        .ref('users/' + state.user.user.uid)        .once('value', snapshot => {            commit('setUserRecipes', snapshot.val());        });}

In our mutations, we need to add a setUserRecipes. It looks like this:

在我们的变异中,我们需要添加一个setUserRecipes 。 看起来像这样:

setUserRecipes(state, payload) {    state.userRecipes = payload;}

We also need to add a userRecipes in state. We set its initial value to an empty array. Here is my entire state object:

我们还需要在state添加一个userRecipes 。 我们将其初始值设置为一个空数组。 这是我的整个状态对象:

state: {    recipes: [],    apiUrl: 'https://api.edamam.com/search',    user: null,    isAuthenticated: false,    userRecipes: []},

Now that we are getting the recipes we need to display them on the page to the user. So go back to your About.vue file. In the template, we are going to loop through all the user’s recipes and display them. I am going to show you my code for the template first then explain what I have done:

现在我们已经获得了食谱,我们需要将其显示在页面上给用户。 因此,请返回您的About.vue文件。 在模板中,我们将遍历所有用户的食谱并显示它们。 我将首先向您展示我的模板代码,然后说明我做了什么:

<template>    <v-container >        <v-layout column>;            <h1 class="title my-3">My Recipes</h1>            <div v-for="(item, idx) in userRecipes" class="subheading mb-2" :key="idx">                {{item}}            </div>        </v-layout>    </v-container></template>

I have set the layout to be column. I did this because I want each recipe to be listed on the page. To make things look clearer I have added a title. I added my-3 to add margin-top and margin-bottom so that there is spacing between the title and the list of recipes. Next, I looped through each recipe and display it. This is what the user sees if they have recipes:

我将布局设置为column 。 我这样做是因为我希望每个食谱都在页面上列出。 为了使事情看起来更清楚,我添加了一个标题。 我添加了my-3,以在页边距的顶部和页边距的底部添加空白,以便在标题和配方列表之间留有间隔。 接下来,我遍历每个食谱并显示它。 这是用户看到的是否有食谱的信息:

This is great, but when if a user logs in and they do not have any recipes? They see the title “My Recipes” and a blank page. This is not a user-friendly design. So let’s change it to display something more friendly.

很好,但是如果用户登录并且他们没有任何食谱,该怎么办? 他们看到标题“我的食谱”和空白页。 这不是用户友好的设计。 因此,让我们对其进行更改以显示更友好的内容。

We will display a button that will take the user to the menu page. In our template, we will add this button. To make the button redirect to the menu page we can add to=”/menu” to the button. Here is my final template for the About.vue component.

我们将显示一个按钮,该按钮会将用户带到menu页面。 在我们的模板中,我们将添加此按钮。 为了使按钮重定向到菜单页面,我们可以在按钮上添加to=”/menu” 。 这是About.vue组件的最终模板。

<template>    <v-container >        <v-layout column>;            <h1 class="title my-3">My Recipes</h1>            <div v-for="(item, idx) in userRecipes" class="subheading mb-2" :key="idx">                {{item}}            </div>            &lt;v-flex mt-4>                <v-btn color="primary" to="/menu">Go To Menu</v-btn>            </v-flex>        </v-layout>    </v-container></template>
在导航中显示个人资料 (Showing Profile in Navigation)

The last thing we need to add is the ability to show a link to the profile in the navigation. Just like the logout button, this should only be shown if the user is authenticated.

我们需要添加的最后一件事是能够在导航中显示指向配置文件的链接。 就像注销按钮一样,仅在用户通过身份验证时才显示。

Open up the AppNavigation components. We are going to group the profile button and the logout button in a div. This is the same thing we did earlier for the Sign In and Join buttons. Add a div and move the logout button to be inside this div. Add another button for profile. This button will be flat just like the Sign In button.

打开AppNavigation组件。 我们将在div中将配置文件按钮和注销按钮分组。 这与我们之前对“ Sign In和“ Join按钮所做的操作相同。 添加一个div并将注销按钮移到该div内。 为profile添加另一个按钮。 该按钮将与“ Sign In按钮一样平坦。

Here is what my AppNavigation looks like now:

这是我现在的AppNavigation外观:

<template>    <span>        <v-navigation-drawer app v-model="drawer" class="brown lighten-2" dark disable-resize-watcher>            <v-list>                <template v-for="(item, index) in items">                    <v-list-tile :key="index">                        <v-list-tile-content>                            {{item.title}}                        </v-list-tile-content>                    <;/v-list-tile>                    <v-divider :key="`divider-${index}`"></v-divider>                </template>            </v-list>        </v-navigation-drawer>        <v-toolbar app color="brown darken-4" dark>            <v-toolbar-side-icon class="hidden-md-and-up" @click="drawer = !drawer"></v-toolbar-side-icon>            <v-spacer class="hidden-md-and-up"></v-spacer>            <router-link to="/"&gt;                <v-toolbar-title to="/">{{appTitle}}</v-toolbar-title>;            </router-link>            <;v-btn flat class="hidden-sm-and-down" to="/menu">Menu</v-btn>            <v-spacer class="hidden-sm-and-down"></v-spacer>            <;div v-if="!isAuthenticated" class="hidden-sm-and-down"&gt;                <v-btn flat to="/sign-in">SIGN IN</v-btn>                <v-btn color="brown lighten-3" to="/join">JOIN</v-btn>            </div>            <div v-else>                <v-btn flat to="/about">PROFILE</v-btn>                <v-btn outline color="white" @click="logout">Logout</v-btn>            </div>        </v-toolbar>    </span></template>

添加路线守卫 (Adding Route Guards)

Currently, the user can navigate to the profile page by typing it into the URL of the browser We don’t want to let users do this if they are not logged in. Vue Router provides the ability to add route guards before navigating to a URL. We want to test if a user is authenticated before allowing them to redirect to the /about page.

当前,用户可以通过在浏览器的URL中键入个人资料页面来导航到个人资料页面。如果用户未登录,我们不想让他们这样做。VueRouter提供了在导航到URL之前添加路由保护的功能。 。 我们要在允许用户重定向到/about页面之前测试用户是否已通过身份验证。

Open up the router.js file. Route guards work in conjunction with meta tags. Find the /about route. We will add a authRequired meta tag to it. The route should look like this:

打开router.js文件。 路由卫士与元标记结合使用。 找到/about路线。 我们将向其添加一个authRequired元标记。 路线应如下所示:

{    path: '/about',    name: 'about',    component: () =&gt; import('./views/About.vue'),    meta: {        authRequired: true    }},

Route guards are checked in a method called beforeEach that is part of Vue Router. This method is passed three parameters:

路由保护是通过Vue路由器中称为beforeEach的方法进行检查的。 此方法传递了三个参数:

  • the route you are going to

    你要去的路线
  • the route you came from

    您来自的路线
  • a next method that continues with the current route

    下一条继续当前路线的方法

Our beforeEach method will check every route that we are going to to see if it contains the meta tag of authRequired. If it does, it will check to see if the user is Authenticated. If the user is not authenticated it will redirect them to the /sign-in page. If the user is logged in then it will allow the route to proceed. If a user routes to any page that does not have the authRequired meta tag then the route will proceed.

我们的beforeEach方法将检查我们将要查看的每条路由,看看它是否包含authRequired的元标记。 如果是这样,它将检查用户是否已通过身份验证。 如果用户未通过身份验证,它将把他们重定向到/sign-in页面。 如果用户已登录,则它将允许路由继续进行。 如果用户路由到没有authRequired元标记的任何页面,则路由将继续。

Here is the method I have added to my router to do this checking:

这是我添加到路由器中以执行此检查的方法:

router.beforeEach((to, from, next) => {    if (to.matched.some(record => record.meta.authRequired)) {        if (!store.state.user) {            next({                path: '/sign-in'            });        } else {            next();        }    } else {        next();    }});

获取代码 (Get the Code)

Even though this is a 4-part series, you can get the finished code in my GitHub account. Please help me out and star the repo when you get the code.

即使这是一个四部分的系列,您也可以在我的GitHub帐户中获得完成的代码。 获取代码后,请帮助我,并给存储库加注星标

摘要 (Summary)

In this part of this series, you have learned:

在本系列的这一部分中,您了解了:

  • What is firebase

    什么是firebase
  • Using firebase to authenticate users who sign in with email and password

    使用Firebase对使用电子邮件和密码登录的用户进行身份验证
  • Using firebase to store the recipes a user has ordered

    使用Firebase存储用户订购的食谱
  • Using route guards to not users access pages if they are not authenticated

    如果用户未通过身份验证,则使用路由防护不让用户访问页面
  • Display user’s list of recipes from the database on firebase

    从Firebase上的数据库中显示用户的配方列表

If you enjoyed this article please clap for it. If you think somebody else would benefit from this article please share it with them.

如果您喜欢这篇文章,请为它鼓掌。 如果您认为其他人将从本文中受益,请与他们分享。

If you have any questions or find anything wrong with the code, please leave a comment. If there are other topics you would like for me to write about, please leave a comment.

如果您有任何疑问或代码有任何问题,请发表评论。 如果您希望我撰写其他主题,请发表评论。

Here are other articles I have written that you might want to read.

这是我写过的其他文章,您可能想阅读。

How to add Internationalization to a Vue Application¡Hola! Bonjour. Ciao. 你好. Here is how you add internationalization to Vue.medium.freecodecamp.orgInstantiation Patterns in JavaScriptInstantiation patterns are ways to create something in JavaScript. JavaScript provides four different methods to create…medium.comHere are 5 Layouts That You Can Make With FlexBoxThe CSS Flexible Box Layout — Flexbox — provides a simple solution to the design and layout problems designers and…hackernoon.com

如何将国际化添加到Vue应用程序中 ¡ 你好。 再见。 你好。 这是将国际化添加到Vue的方法。 medium.freecodecamp.org JavaScript中的 实例 化模式 实例化模式是在JavaScript中创建内容的方法。 JavaScript提供了四种创建方法…… medium.com 您可以使用FlexBox进行5种布局 CSS FlexBox布局(Flexbox)为设计人员和设计师提供了一种解决设计和布局问题的简单解决方案 。hackernoon.com

Follow Me On Twitter!

在推特上关注我!

翻译自: https://www.freecodecamp.org/news/how-to-build-a-spa-using-vue-js-vuex-vuetify-and-firebase-adding-authentication-with-firebase-d9932d1e4365/

vue firebase

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值