vuex构建vue项目_如何使用Vue.js,Vuex,Vuetify和Firebase构建SPA:使用Vuex并访问API

vuex构建vue项目

by Jennifer Bland

詹妮弗·布兰德(Jennifer Bland)

如何使用Vue.js,Vuex,Vuetify和Firebase构建SPA:使用Vuex并访问API (How to build an SPA using Vue.js, Vuex, Vuetify, and Firebase: use Vuex and access the API)

第3部分:了解如何使用Vuex并访问API以获得食谱 (Part 3: learn how to use Vuex and access the API to get your recipes)

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 three 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

第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在应用程序的不同页面之间添加导航。 我们为应用程序中的所有页面添加了组件。

访问API (Accessing the API)

We are building an SPA e-commerce website that sells meal delivery services. In order for this website to work, then we need recipes to create our meals. To generate our recipes we will use the API from Edamam. The Edamam recipe API contains 1.7+ million nutritionally analyzed recipes. The API allows you to filter recipes by diets. This is what we need since we will want to show recipes based on which diet the user has selected.

我们正在建立一个SPA电子商务网站,该网站出售送餐服务。 为了使该网站正常工作,我们需要食谱来制作餐点。 为了生成我们的食谱,我们将使用EdamamAPI 。 Edamam食谱API包含1.7+百万种经过营养分析的食谱。 该API允许您按饮食过滤食谱。 这就是我们需要的,因为我们将要根据用户选择的饮食来显示食谱。

使用Edamam创建一个帐户 (Create an account with Edamam)

The first step is to create your account with edamam. They provide a free account and that is what you want to sign up for. Click this link to go to the Edamam website. Click the sign up button for the Recipe Search API.

第一步是使用edamam创建您的帐户。 他们提供了一个免费帐户,这就是您想要注册的帐户。 单击此链接转到Edamam网站。 单击“食谱搜索API”的sign up按钮。

Next, you will be presented with three different levels that you can sign up for. We are going to use the free Developer tier. Click the start now button in the developer option.

接下来,将向您展示可以注册的三个不同级别。 我们将使用免费的Developer层。 单击开发人员选项中的start now按钮。

You will be presented with a sign-up form. Complete the form.

您将看到一个注册表格。 完成表格。

After completing the form you will be prompted to log in to your account. When you are logged in to your account, you will be asked to choose the API you need. Instead of clicking on any of the selection, instead, go to the menu and select Get an API key now!

填写完表格后,系统将提示您登录您的帐户。 登录帐户后,系统会要求您选择所需的API。 不用单击任何选择,而是转到菜单并选择立即Get an API key now!

You should see your Recipe Search API key. (NOTE: if you do not have a key then click the create a new application button to create one.) Click on the view button to see the details of your API key. You will have an Application ID and Application Keys. You will need these to access the API for your website.

您应该看到您的食谱搜索API密钥。 (注意:如果您没有密钥,请单击“ create a new application按钮以创建一个。)单击“查看”按钮以查看API密钥的详细信息。 您将拥有一个应用程序ID和应用程序密钥。 您将需要这些来访问您网站的API。

创建菜单页面 (Creating Menu Page)

The menu page is where we will show recipes for each of the three diets we are supporting. These recipes will be retrieved from the Edamam API service.

在菜单页面上,我们将显示我们支持的三种饮食中每一种的食谱。 这些配方将从Edamam API服务中检索。

The first thing we want to do is for the user to select a diet. We can do this by re-using the HomePlans component. We will modify the component to add a button to each diet for users to select. When visitors to the website click on a button they will see the recipes for that diet. But we do not want these buttons to be shown when the component is shown on the home page. So we are going to take care of this.

我们要做的第一件事是让用户选择饮食。 我们可以通过重新使用HomePlans组件来做到这一点。 我们将修改该组件,以便在每种饮食中添加一个按钮供用户选择。 当网站访问者单击按钮时,他们将看到该饮食的食谱。 但是我们不希望在主页上显示组件时显示这些按钮。 因此,我们将照顾这一点。

Open up the HomePlans component. Below the v-card-text section, we are going to add a v-card-actions section. This section will contain the button for users to select the plan. Here is what we are adding to each v-card in this component.

打开HomePlans组件。 在v-card-text部分下方,我们将添加一个v-card-actions部分。 本部分将包含供用户选择计划的按钮。 这是我们要添加到此组件中每个v-card的内容。

<v-card-actions v-if="['menu'].includes($route.name)">    &lt;v-btn outline block color="green" @click="showRecipes('vegan')">Select This Plan</v-btn></v-card-actions>

For each v-card-actions section, we will have a button. The button has the props of outline and block set. The button will call the method showRecipes when you click on the button. The method is passed a parameter with the text for the selected diet. Make sure you change this to reflect the selected diet. Here is what the template for the HomePlans component looks like now:

对于每个v-card-actions部分,我们都有一个按钮。 该按钮具有轮廓和块设置的道具。 当您单击按钮时,该按钮将调用showRecipes方法。 该方法被传递带有所选饮食文本的参数。 确保更改此设置以反映所选的饮食。 这是HomePlans组件的模板现在的样子:

<template>    <v-container grid-list-lg>        &lt;v-layout row>            &lt;v-flex xs12 class="text-xs-center display-1 font-weight-black my-5">Available Meal Plans</v-flex>        </v-layout>        <v-layout row wrap>            <v-flex xs12 sm12 md4>                <v-card>                    <v-responsive>                        <v-img src="http://source.unsplash.com/hjCA3ecCXAQ" height="500px">                            <v-container fill-height fluid>                                <v-layout fill-height>                                    <v-flex xs12 align-end flexbox>                                        <span class="headline white--text">KETO</span>                                    </v-flex>                                </v-layout>                            </v-container>                        </v-img>                    </v-responsive>
<v-card-text>                        <div>                            <h3 class="headline mb-0">Keto</h3>                            <div>The Keto diet is a high-fat, adequate-protein, low-carbohydrate diet. The diet forces the body to burn fats rather than carbohydrates by putting the body into ketosis.                            </div>                        </div>                    </v-card-text>
<v-card-actions>                        &lt;v-btn outline block color="green" @click="showRecipes('keto')">Select This Plan</v-btn>                    </v-card-actions>                </v-card>            </v-flex>
<v-flex xs12 sm12 md4>                <v-card>                    <v-responsive>                        <v-img src="http://source.unsplash.com/6S27S6pZ6o0" height="500px">                            <v-container fill-height fluid>                                <v-layout fill-height>                                    <v-flex xs12 align-end flexbox>                                        <span class="headline white--text">PALEO</span>                                    </v-flex>                                </v-layout>                            </v-container>                        </v-img>                    </v-responsive>
<v-card-text>                        <div>                            <h3 class="headline mb-0">Paleo</h3>                            <div>The Paleo diet requires the sole or predominant consumption of foods presumed to have been the only foods available to or consumed by humans during the Paleolithic era.                            </div>                        </div>                    </v-card-text>
<v-card-actions>                        &lt;v-btn outline block color="green" @click="showRecipes('paleo')">Select This Plan</v-btn>                    </v-card-actions>                </v-card>            </v-flex>
<v-flex xs12 sm12 md4>                <v-card>                    <v-responsive>                        <v-img src="http://source.unsplash.com/1SPu0KT-Ejg" height="500px">                            <v-container fill-height fluid>                                <v-layout fill-height>                                    <v-flex xs12 align-end flexbox>                                        <span class="headline white--text">VEGAN</span>                                    </v-flex>                                </v-layout>                            </v-container>                        </v-img>                    </v-responsive>
<v-card-text>                        <div>                            <h3 class="headline mb-0">Vegan</h3>                            <div>The vegan diet abstains from the use of animal products. The vegan diet does not consume meat, diary products, eggs or any all other animal-derived ingredients.                            </div>                        </div>                    </v-card-text>
<v-card-actions>                        &lt;v-btn outline block color="green" @click="showRecipes('vegan')">Select This Plan</v-btn>                    </v-card-actions>                </v-card>            </v-flex>
</v-layout>    </v-container></template>

Now that we have added the button, we want to hide it on the home page and show it on the menu page. To do this we are going to combine the v-if directive and the name we assigned to each route.

现在,我们已经添加了按钮,我们希望将其隐藏在主页上,并将其显示在菜单页面上。 为此,我们将结合v-if指令和分配给每个路由的名称。

In the router.js file, we added our routes. Routes is an array of objects. Each object has a path, name and component. We can use the array includes method to check if the current route is home. Here is what we will add to each v-card-actions sections:

router.js文件中,我们添加了路由。 路线是一组对象。 每个对象都有一个pathnamecomponent 。 我们可以使用array includes方法检查当前路线是否为家。 这是我们将添加到每个v-card-actions部分的内容:

<v-card-actions v-if="['menu'].includes($route.name)">    &lt;v-btn outline block color="green" @click="showRecipes('vegan')">Select This Plan</v-btn></v-card-actions>

Here is what the template for the HomePlans component looks like now:

这是HomePlans组件的模板现在的样子:

<template>    <v-container grid-list-lg>        &lt;v-layout row>            &lt;v-flex xs12 class="text-xs-center display-1 font-weight-black my-5">Available Meal Plans</v-flex>        </v-layout>        <v-layout row wrap>            <v-flex xs12 sm12 md4>                <v-card>                    <v-responsive>                        <v-img src="http://source.unsplash.com/hjCA3ecCXAQ" height="500px">                            <v-container fill-height fluid>                                <v-layout fill-height>                                    <v-flex xs12 align-end flexbox>                                        <span class="headline white--text">KETO</span>                                    </v-flex>                                </v-layout>                            </v-container>                        </v-img>                    </v-responsive>
<v-card-text>                        <div>                            <h3 class="headline mb-0">Keto</h3>                            <div>The Keto diet is a high-fat, adequate-protein, low-carbohydrate diet. The diet forces the body to burn fats rather than carbohydrates by putting the body into ketosis.                            </div>                        </div>                    </v-card-text>
<v-card-actions v-if="['menu'].includes($route.name)">                        &lt;v-btn outline block color="green" @click="showRecipes('keto')">Select This Plan</v-btn>                    </v-card-actions>                </v-card>            </v-flex>
<v-flex xs12 sm12 md4>                <v-card>                    <v-responsive>                        <v-img src="http://source.unsplash.com/6S27S6pZ6o0" height="500px">                            <v-container fill-height fluid>                                <v-layout fill-height>                                    <v-flex xs12 align-end flexbox>                                        <span class="headline white--text">PALEO</span>                                    </v-flex>                                </v-layout>                            </v-container>                        </v-img>                    </v-responsive>
<v-card-text>                        <div>                            <h3 class="headline mb-0">Paleo</h3>                            <div>The Paleo diet requires the sole or predominant consumption of foods presumed to have been the only foods available to or consumed by humans during the Paleolithic era.                            </div>                        </div>                    </v-card-text>
<v-card-actions v-if="['menu'].includes($route.name)">                        &lt;v-btn outline block color="green" @click="showRecipes('paleo')">Select This Plan</v-btn>                    </v-card-actions>                </v-card>            </v-flex>
<v-flex xs12 sm12 md4>                <v-card>                    <v-responsive>                        <v-img src="http://source.unsplash.com/1SPu0KT-Ejg" height="500px">                            <v-container fill-height fluid>                                <v-layout fill-height>                                    <v-flex xs12 align-end flexbox>                                        <span class="headline white--text">VEGAN</span>                                    </v-flex>                                </v-layout>                            </v-container>                        </v-img>                    </v-responsive>
<v-card-text>                        <div>                            <h3 class="headline mb-0">Vegan</h3>                            <div>The vegan diet abstains from the use of animal products. The vegan diet does not consume meat, diary products, eggs or any all other animal-derived ingredients.                            </div>                        </div>                    </v-card-text>
<v-card-actions v-if="['menu'].includes($route.name)">                        &lt;v-btn outline block color="green" @click="showRecipes('vegan')">Select This Plan</v-btn>                    </v-card-actions>                </v-card>            </v-flex>
</v-layout>    </v-container></template>
获取食谱 (Getting Recipes)

When a user clicks on the Add This Plan button it calls the method showRecipes. Let's create this method now. This method will retrieve recipes from the Edamam API. First, we need to install axios by entering this command at the terminal:

用户单击“ Add This Plan按钮时,将调用showRecipes方法。 让我们现在创建此方法。 此方法将从Edamam API检索配方。 首先,我们需要通过在终端上输入以下命令来安装axios:

npm install axios

To use axios we will need to import it. In the script section of the HomePlans component import it with this command:

要使用axios,我们需要将其导入。 在HomePlans组件的脚本部分中,使用以下命令将其导入:

import axios from 'axios';

Next, in the export default section of the HomePlans component, we will add our method.

接下来,在HomePlans组件的export default部分中,我们将添加我们的方法。

NOTE: I am going to show you how to use axios in a component to get data from an API. BUT then we are going to ditch this code and use Vuex. So from here to the title Using Vuex is code that we will not use it in the final version of our application — but I wanted to show it so you understand it.

注意:我将向您展示如何在组件中使用axios从API获取数据。 但是,我们将放弃此代码并使用Vuex。 因此,从这里到标题为Using Vuex是代码,我们将不会在应用程序的最终版本中使用它-但我想展示它以便您理解。

The method is called showRecipes and takes one parameter called plan. In this method, I will use axios to get 10 recipes from Edamam based on the diet plan selected. The axios call will be a GET to the URL https://api.edamam.com/search.

该方法称为showRecipes并采用一个称为plan参数。 在这种方法中,我将使用axios根据所选的饮食计划从Edamam获取10个食谱。 axios调用将是对URL https://api.edamam.com/search的GET

According to the Edamam API documentation, we are required to use a param called q that contains our query string. We will set this value to the plan parameter that is passed into our method. The documentation also requires us to supply params for app_id and app_key. You will need to set these values to your keys you were assigned when you signed up for the Edamam API.

根据Edamam API文档,我们需要使用包含查询字符串的名为q的参数。 我们将此值设置为传递到我们方法中的plan参数。 该文档还要求我们为app_id和app_key提供参数。 您需要将这些值设置为注册Edamam API时分配的密钥。

There are two more params we will use. They are to and from. These params specify the start and end of the number of recipes that are returned. For demo purposes, we will limit it to return just the first 10 recipes.

我们将使用另外两个参数。 他们是tofrom 。 这些参数指定返回的配方数的开始和结束。 出于演示目的,我们将其限制为仅返回前10个配方。

Our axios call will either succeed or fail. Axios provides a promise so we can use .then and .catch to handle both success and failure. If the call succeeds we want to set the recipes data value equal to the hits array that is returned from Edamam. All responses from axios are contained in the data object. We account for this by first assigning response to response.data. Next, we assign recipes to response.hits.

我们的axios调用将成功或失败。 Axios公司提供了一个承诺,所以我们可以使用.then.catch处理成功和失败。 如果调用成功,我们希望将配方数据值设置为等于从Edamam返回的hits数组。 来自axios的所有响应都包含在data对象中。 为此,我们首先将响应分配给response.data。 接下来,我们将配方分配给response.hits

What if the axios call fails? Well, we use the .catch of the promise to handle a failure. In this case, all we want to do is set recipes to an empty array.

如果axios调用失败怎么办? 好吧,我们使用.catch来处理失败。 在这种情况下,我们要做的就是将配方设置为一个空数组。

Here is what the method looks like:

该方法如下所示:

export default {    name: 'HomePlans',    data() {        return {            recipes: []        };    },    methods: {        showRecipes(plan) {            axios                .get('https://api.edamam.com/search', {                    params: {                        q: plan,                        app_id: '5b6623d5',                        app_key: '46674aa2193dbb7b88ffd897331e661a',                        from: 0,                        to: 9                    }                })                .then(response => {                    response = response.data;                    this.recipes = response.hits;                })                .catch(() => {                    this.recipes = [];                });        }    }};

使用Vuex (Using Vuex)

Now we have worked ourselves into a pickle with our code. We originally had a component that showed a picture, title and short description of a diet. We added a button to it to get recipes. Now if we keep going then we will need to add functionality to display the recipes that we retrieved from the Edamam API.

现在,我们将自己的代码编写成一个腌菜。 我们最初有一个显示饮食的图片,标题和简短说明的组件。 我们在其中添加了一个按钮以获取食谱。 现在,如果继续进行下去,我们将需要添加功能来显示从Edamam API检索到的配方。

I really don’t want all this functionality placed into this component. I want it to just display the image, title, short description and button. But by having the button in the component, I need a way to handle when the user clicks the button. I also need a way to display recipes. To do this I am going to move the functionality of handling the button click to Vuex.

我真的不希望将所有这些功能都放入该组件中。 我希望它仅显示图像,标题,简短描述和按钮。 但是通过在组件中包含按钮,我需要一种当用户单击按钮时进行处理的方法。 我还需要一种显示食谱的方法。 为此,我将把处理按钮单击的功能移到Vuex。

Vuex is a state management library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion. Vuex consists of:

Vuex是Vue.js应用程序的状态管理库。 它充当应用程序中所有组件的集中存储,其规则确保状态只能以可预测的方式进行更改。 Vuex包括:

  • The state, which is the source of truth that drives our app;

    状态是驱动我们应用程序的真理之源;
  • The mutations, which change the value of the state;

    突变,改变状态的值;
  • The actions, which are the possible ways the state could change in reaction to user inputs from the view.

    这些动作是状态根据视图中的用户输入做出响应而可能更改的方式。

When we created our application using the Vue CLI 3 we specified that we would be using Vuex. As a result, the CLI created the file store.js in the src folder for us.

使用Vue CLI 3创建应用程序时,我们指定将使用Vuex。 结果,CLI在src文件夹中为我们创建了文件store.js

State will contain the recipes. We will use an actions to make the API call to retrieve recipes. A mutations will be used to update the variable recipe in state with the recipes returned from the actions call.

State将包含食谱。 我们将使用一个actions来进行API调用以检索配方。 mutations将用于使用actions调用返回的recipe更新state为可变的recipe

Open up the store.js file. First, add a new recipes variable in state and assign it to an empty array. Also add a variable called apiUrl. This variable will contain the url for our API call. It should look like this:

打开store.js文件。 首先,在状态中添加一个新的食谱变量,并将其分配给一个空数组。 还添加一个名为apiUrl的变量。 此变量将包含我们的API调用的网址。 它看起来应该像这样:

export default new Vuex.Store({    state: {        recipes: [],        apiUrl: 'https://api.edamam.com/search'    },    mutations: {},    actions: {}});

Next, we are going to create an action called getRecipes. This action will use axios to get recipes from the API. To use axios we will need to import it. At the top of the file, there are two import commands. Place this after them:

接下来,我们将创建一个名为getRecipes的操作。 该操作将使用axios从API获取配方。 要使用axios,我们需要将其导入。 在文件的顶部,有两个导入命令。 将它们放在它们后面:

import axios from 'axios';

Earlier I showed you using promises with the axios call. Now I am going to show you how to do the same call using async / await. The method getRecipes will have to be prefixed with async. Inside the method, we have a try catch block. Inside the try block, we will set a variable response to the data returned from the axios call. We put await in front of the axios call. If the call succeeds we will call the mutation setRecipes. SetRecipes will change the state to set recipes to the array of recipes returned from the API call.

之前,我向您展示了在axios调用中使用promise。 现在,我将向您展示如何使用async / await进行相同的调用。 方法getRecipes必须以async为前缀。 在方法内部,我们有一个try catch块。 在try块中,我们将对axios调用返回的数据设置变量response 。 我们在axios调用之前等待。 如果调用成功,我们将调用变异setRecipes 。 SetRecipes将更改状态,以将配方设置为从API调用返回的配方数组。

If the API call fails it will end up in the catch block. In this scenario, we call the same mutation but pass it an empty array. Here is what the store.js should look like:

如果API调用失败,它将最终在catch块中。 在这种情况下,我们称相同的突变,但将其传递给一个空数组。 这是store.js的样子:

import Vue from 'vue';import Vuex from 'vuex';import axios from 'axios';Vue.use(Vuex);export default new Vuex.Store({    state: {        recipes: [],        apiUrl: 'https://api.edamam.com/search'    },    mutations: {        setRecipes(state, payload) {            state.recipes = payload;        }    },    actions: {        async getRecipes({ state, commit }, plan) {            try {                let response = await axios.get(`${state.apiUrl}`, {                    params: {                        q: plan,                        app_id: '<yourAppIdHere>',                        app_key: '<yourAppKeyHere>',                        from: 0,                        to: 9                    }                });                commit('setRecipes', response.data.hits);            } catch (error) {                commit('setRecipes', []);            }        }    }});
更新HomePlans组件 (Updating HomePlans Component)

Let’s go back to our HomePlans component and clean it up. We can remove the import axios line of code. We can remove the data() object. In the showRecipes method, you can delete all of the code. We now need just one line of code to call our action in our Vuex store. To call an action in Vuex you use a dispatch. This is the one line of code for our showRecipes method:

让我们回到HomePlans组件并进行清理。 我们可以删除导入axios代码行。 我们可以删除data()对象。 在showRecipes方法中,您可以删除所有代码。 现在,我们只需要一行代码即可在Vuex商店中调用我们的操作。 要在Vuex中调用动作,请使用dispatch 。 这是我们的showRecipes方法的一行代码:

this.$store.dispatch('getRecipes', plan);

Here is what our HomePlans component looks like:

这是我们的HomePlans组件的外观:

<template>    <v-container grid-list-lg>        &lt;v-layout row>            &lt;v-flex xs12 class="text-xs-center display-1 font-weight-black my-5">Available Meal Plans</v-flex>        </v-layout>        <v-layout row wrap>            <v-flex xs12 sm12 md4>                <v-card>                    <v-responsive>                        <v-img src="http://source.unsplash.com/hjCA3ecCXAQ" height="500px">                            <v-container fill-height fluid>                                <v-layout fill-height>                                    <v-flex xs12 align-end flexbox>                                        <span class="headline white--text">KETO</span>                                    </v-flex>                                </v-layout>                            </v-container>                        </v-img>                    </v-responsive>                    <v-card-text>                        <div>                            <h3 class="headline mb-0">Keto</h3>                            <div>The Keto diet is a high-fat, adequate-protein, low-carbohydrate diet. The diet forces the body to burn fats rather than carbohydrates by putting the body into ketosis.                            </div>                        </div>                    </v-card-text>                    <v-card-actions v-if="['menu'].includes($route.name)">                        <v-btn outline block color="green" @click="showRecipes('keto')">Select This Plan</v-btn>                    &lt;/v-card-actions>                </v-card&gt;            </v-flex>            <v-flex xs12 sm12 md4>                <v-card>                    <v-responsive>                        <v-img src="http://source.unsplash.com/6S27S6pZ6o0" height="500px">                            <v-container fill-height fluid>                                <v-layout fill-height>                                    <v-flex xs12 align-end flexbox>                                        <span class="headline white--text">PALEO</span>                                    </v-flex>                                </v-layout>                            </v-container>                        </v-img>                    </v-responsive>                    <v-card-text>                        <div>                            <h3 class="headline mb-0">Paleo</h3>                            <div>The Paleo diet requires the sole or predominant consumption of foods presumed to have been the only foods available to or consumed by humans during the Paleolithic era.                            </div>                        </div&gt;                    </v-card-text>                    <v-card-actions v-if="['menu'].includes($route.name)">                        <v-btn outline block color="green" @click="showRecipes('paleo')">Select This Plan</v-btn>                    </v-card-actions>                </v-card&gt;            </v-flex>            <v-flex xs12 sm12 md4&gt;                <v-card>                    <v-responsive>                        <v-img src="http://source.unsplash.com/1SPu0KT-Ejg" height="500px">                            <v-container fill-height fluid>                                <v-layout fill-height>                                    <v-flex xs12 align-end flexbox>                                        <span class="headline white--text">VEGAN</span>                                    </v-flex>                                </v-layout>                            </v-container>                        </v-img>                    </v-responsive>                    <v-card-text>                        <div>                            <h3 class="headline mb-0">Vegan</h3>                            <div>The vegan diet abstains from the use of animal products. The vegan diet does not consume meat, diary products, eggs or any all other animal-derived ingredients.                            </div>                        </div>                    </v-card-text>                    <v-card-actions v-if="['menu'].includes($route.name)">                        <v-btn outline block color="green" @click="showRecipes('vegan')">Select This Plan</v-btn>                    </v-card-actions>                </v-card>            </v-flex>        </v-layout>    </v-container></template><script>export default {    name: 'HomePlans',    methods: {        showRecipes(plan) {            this.$store.dispatch('getRecipes', plan);        }    }};</script><style scoped></style>
显示食谱 (Displaying Recipes)

We have used Vuex to get recipes from the API. We store the array of recipes in the Vuex store. Now we need a new component that will be used to display the recipes. Inside your components folder create a new file called MealRecipes.vue.

我们已经使用Vuex从API获取配方。 我们将一系列食谱存储在Vuex商店中。 现在,我们需要一个用于显示配方的新组件。 在components文件夹内,创建一个名为MealRecipes.vue的新文件。

In this new component, we will add a computed value for recipes. This computed variable will get its value from Vuex store. Its value will be set to the value of recipes in state. This is what it looks like:

在这个新组件中,我们将为配方添加一个计算值。 该计算变量将从Vuex存储中获取其值。 其值将被设置为staterecipes值。 看起来是这样的:

<script>export default {    name: 'MealRecipes',    computed: {        recipes() {            return this.$store.state.recipes;        }    }};</script>

We need to update the template in this component to display our recipes. Vuetify provides a grid-list which creates spacing between items displayed on the page. We will use this functionality by placing it on the v-container that is the root element in our template. Like this:

我们需要更新此组件中的模板以显示我们的食谱。 Vuetify提供了一个网格列表,可在页面上显示的项目之间创建间距。 我们将使用此功能,方法是将其放在模板的根元素v容器中。 像这样:

<v-container grid-list-lg>
</v-container>

Inside the v-container we will have a v-layout. Inside the v-layout we will have a v-flex. We set the layout on the v-layout to be row. We will also add wrap. On the v-flex we will loop through all the recipes in the array and display them. So we need a v-for . Vue now requires you to have an index for every v-for loop. We add an idx and set that to the key. Here is what our MealRecipes component looks like so far.

v-container内部,我们将有一个v-layout 。 在v-layout我们将有一个v-flex 。 我们将v布局上的布局设置为row 。 我们还将添加wrap 。 在v-flex我们将遍历阵列中的所有配方并显示它们。 因此,我们需要一个v-for 。 Vue现在要求您为每个v-for循环都有一个索引。 我们添加一个idx并将其设置为key 。 到目前为止,这是我们的MealRecipes组件的外观。

<v-container grid-list-lg>    <v-layout row wrap>        <v-flex xs12 sm6 md6 lg4 v-for="(item, idx) in recipes" :key="idx">        </v-flex>    </v-layout><v-container>

We will use the Vuetify v-card to display each recipe. This is very similar to the layout we used for the HomePlans component. We will display an image for the recipe, a title and a list of ingredients.

我们将使用Vuetify v-card显示每个食谱。 这与我们用于HomePlans组件的布局非常相似。 我们将显示配方的图像,标题和配料列表。

The API call returns an array of recipes. If you look at one entry in the array you will notice that it has a recipe object. Inside that object, we will find a URL for the recipe image, title, and list of ingredients. The API returns two different arrays for the ingredients. The one we will use is the one in the ingredientLines array.

API调用返回配方数组。 如果查看阵列中的一个条目,您会注意到它具有一个配方对象。 在该对象内,我们将找到配方图像,标题和配料列表的URL。 API返回两个不同的成分数组。 我们将使用的一个是compositionLines数组中的一个。

Here is what the MealRecipes component looks like:

这是MealRecipes组件的外观:

<template>    <v-container grid-list-lg>        <v-layout row wrap>            <v-flex xs12 sm6 md6 lg4 v-for="(item, idx) in recipes" :key="idx">                <v-card>                    <v-responsive>                        <v-img :src="item.recipe.image"></v-img>                    </v-responsive>                    <v-card-text>                        <div class="title"&gt;{{item.recipe.label}}</div>                        <div class="subheading">Ingredients</div>                        <ul&gt;                            <li v-for="(ingredient, i) in item.recipe.ingredientLines" :key="i">{{ingredient}}</li>                        </ul>                    </v-card-text>                </v-card>            </v-flex>        </v-layout>    </v-container></template><script>export default {    name: 'MealRecipes',    computed: {        recipes() {            return this.$store.state.recipes;        }    }};</script><style scoped></style>

Now that we have the component finished, we need to use it inside the Menu.vue component. Open the Menu.vue component. Import the MealRecipes component with this command:

现在我们已经完成了组件,我们需要在Menu.vue组件内使用它。 打开Menu.vue组件。 使用以下命令导入MealRecipes组件:

import MealRecipes from '@/components/MealRecipes';

Add it to the components like this:

像这样将其添加到组件中:

export default {    name: 'Menu',    components: {        HomePlans,        MealRecipes    }};

In the template add mealRecipes below homePlans. Here is what Menu.vue should look like:

在模板中,在homePlans下添加饭菜食谱。 这是Menu.vue外观:

<template>    <div>        <home-plans></home-plans>        <meal-recipes></meal-recipes&gt;    </div&gt;&lt;/template><script>import HomePlans from '@/components/HomePlans';import MealRecipes from '@/components/MealRecipes';export default {    name: 'Menu',    components: {        HomePlans,        MealRecipes    }};</script><style scoped></style>

Start the application with the command npm run serve in the terminal. Open your browser to http://localhost:8080 and you will see the application running. Click on menu in the navigation. Then click on any of the diet plans. You should see a list of recipes like this:

在终端中使用命令npm run serve启动应用程序。 打开浏览器到http:// localhost:8080 ,您将看到该应用程序正在运行。 单击导航中的菜单。 然后单击任何饮食计划。 您应该会看到类似这样的食谱列表:

I want to make two quick changes to how the recipes are styled. First I want to add some more spacing between the recipe title and ingredients. Second I want to add a button to the bottom of every recipe for a person to order.

我想对食谱的样式进行两项快速更改。 首先,我想在配方标题和配料之间增加一些间距。 其次,我想在每个食谱的底部添加一个按钮,供一个人订购。

So open up the MealRecipes component. For the title, I already have a class of title. I am going to add to that a value of my-3. This is equivalent to adding margin-top and margin-bottom to the title. This lets the title offset from the image and the ingredients.

因此,打开MealRecipes组件。 对于标题,我已经有一个title类。 我将添加my-3的值。 这等效于在标题中添加margin-top和margin-bottom。 这样可以使标题从图像和成分偏移。

Last change I want to make is to add a button. Inside the v-card and below the v-card-text we will add a v-card-actions. Inside that, we will add a button. We will use the default button with a green color. By default, Vuetify makes the text in buttons black in color. We can change that to white by adding the dark directive. Here is our button:

我要进行的最后更改是添加一个按钮。 在v-card内部和v-card-text下面,我们将添加v-card-actions 。 在其中,我们将添加一个按钮。 我们将使用绿色的默认按钮。 默认情况下,Vuetify使按钮中的文本变为黑色。 我们可以通过添加dark指令将其更改为白色。 这是我们的按钮:

<v-card-actions>    &lt;v-btn color="green" dark>Order</v-btn></v-card-actions>

Here is our MealRecipes component:

这是我们的MealRecipes组件:

<template>    <v-container grid-list-lg>        <v-layout row wrap>            <v-flex xs12 sm6 md6 lg4 v-for="(item, idx) in recipes" :key="idx">                <v-card>                    <v-responsive>                        <v-img :src="item.recipe.image"></v-img>                    </v-responsive>                    <v-card-text>                        <div class="title my-5"&gt;{{item.recipe.label}}</div>                        <div class="subheading">Ingredients</div>                        <ul&gt;                            <li v-for="(ingredient, i) in item.recipe.ingredientLines" :key="i">{{ingredient}}</li>                        </ul&gt;                    </v-card-text>                    <v-card-actions>                        <v-btn color="green" dark>Order</v-btn>                    </v-card-actions>                </v-card>            </v-flex>        </v-layout>    </v-container></template>&lt;script>export default {    name: 'MealRecipes',    computed: {        recipes() {            return this.$store.state.recipes;        }    }};</script><style scoped></style>

获取代码 (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 Vuex

    什么是Vuex
  • How to get recipes from an API

    如何从API获取食谱
  • How to use Axios with promises and async / await

    如何在Promise和Async / Await中使用Axios
  • How to call actions in Vuex store

    如何在Vuex商店中调用动作
  • How to mutate state in Vuex

    如何在Vuex中改变状态

下一步是什么 (What’s Next)

In the next part of this series, we will cover Firebase for authentication. Firebase allows you to develop an application without having to write server-side code.

在本系列的下一部分中,我们将介绍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.

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

Here 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.comThink outside the box with CSS shape-outsideCSS is based off a box model. If you have an image that is a circle that you want to wrap text around, it will wrap…hackernoon.comWhy Company Culture is Important to Your Career as a Software EngineerThe impact of a company’s culture is reflected in a company’s ability to achieve their goals and productivity levels…medium.freecodecamp.org

这里有5个版面,你可以与Flexbox的 CSS的灵活盒布局- Flexbox的-提供了一个简单的解决方案的设计和布局问题,设计师和... hackernoon.com 觉得跟CSS形状外箱外 CSS是基于掀起了盒子模型。 如果您要环绕的图像是一个圆圈,它将环绕在其中…… hackernoon.com 为什么公司文化对您作为软件工程师 职业很重要公司文化 的影响反映在公司的实现能力上他们的目标和生产力水平… medium.freecodecamp.org

Follow Me On Twitter!

在推特上关注我!

翻译自: https://www.freecodecamp.org/news/how-to-build-an-spa-using-vue-js-vuex-vuetify-and-firebase-use-vuex-and-access-the-api-f8036aa464ad/

vuex构建vue项目

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值