将 Axios 与 Vuex 结合使用

现在您已经了解了使用 Axios 的基础知识,是时候考虑如何将它与 Vuex 一起使用了。 一种简单的方法是使用 Vuex 处理对 API 的包装调用,使用 Axios 执行 HTTP 调用。

练习 10.02:在 Vuex 中使用 Axios

我们将采用之前的功能(加载影片(films)和船舶(ships)数组)并在 Vuex 存储的上下文中重建它。和以前一样,您需要使用 CLI 来构建新应用程序,并确保您要求包含 Vuex。 CLI 完成后,您也可以使用 npm 命令添加 Axios。

此练习与我们在练习 10.01 使用 Axios 从 API 加载数据中构建的第一个应用程序非常相似,但有一些细微的差别。我们先看一下用户界面。初始加载时,Films 和 Ships 都是空的:

image 2023 10 16 23 14 02 985
Figure 1. Figure 10.2: Initial application UI

请注意,Films 部分有一条加载消息。应用程序加载后,我们将立即发出获取该数据的请求。 不过,对于 Ships,我们会等待用户明确请求他们想要该数据。这是 films 数组加载后的样子:

image 2023 10 16 23 15 22 755
Figure 2. Figure 10.3: The application’s rendered films

最后,单击 Load Ships 按钮后,该按钮将禁用(以防止用户多次请求数据),然后在数据加载后删除整个按钮:

image 2023 10 16 23 15 59 580
Figure 3. Figure 10.4: The final view after everything is loaded

要访问本练习的代码文件,请参阅 https://packt.live/32pUsWy

  1. 从第一个组件 App.vue 开始。 编写 HTML。 请记住,films 显示在组件中,但 ships 只将显示在其自己的组件中。 使用 v-else 添加一条加载消息,该消息将在 Axios 发出 HTTP 请求时显示:

    <template>
        <div id="app">
            <h2>Films</h2>
            <ul v-if="films.length">
                <li v-for="film in films" :key="film.url">
                {{ film.title }} was released in {{ film.release_date }}
                </li>
            </ul>
            <div v-else>
                <i>Loading data...</i>
            </div>
            <Ships />
        </div>
    </template>
  2. 现在添加加载和注册 Ships 组件所需的代码:

    import Ships from './components/Ships.vue'
    
    export default {
        name: 'app',
        components: {
            Ships
        },
  3. 也导入 mapState:

    import { mapState } from 'vuex';
  4. 接下来,添加代码以将 films 数组从我们的存储映射到本地计算值。记得导入mapState:

    computed: {
        ...mapState(["films"])
    },
  5. 最后,created 方法用于在我们的 store 中触发一个操作:

    created() {
        this.$store.dispatch('loadFilms');
    }
  6. 接下来,在 Components/Ship.vue 中构建 Ships 组件。Ships 组件还包含一个数据列表,但使用一个按钮,以便用户可以请求加载数据。完成后该按钮应自动关闭,并在加载过程中禁用:

    <template>
        <div>
            <h2>Ships</h2>
            <div v-if="ships.length">
                <ul>
                    <li v-for="ship in ships" :key="ship.url">
                    {{ ship.name }} is a {{ ship.starship_class }}
                    </li>
                </ul>
            </div>
            <button v-else @click="loadShips" :disabled="loading">Load Ships</button>
        </div>
    </template>
  7. 添加代码来处理映射 ships 状态并触发 Vuex 的操作来加载 ships:

    <script>
    import { mapState } from 'vuex';
    
    export default {
        name: 'Ships',
        data() {
            return {
                loading:false
            }
        },
        computed: {
            ...mapState(["ships"])
        },
        methods:{
            loadShips() {
                this.loading = true;
                this.$store.dispatch('loadShips');
            }
        }
    }
    </script>
  8. 现在,建立 store。首先,定义保存 films 和 ships 数组的状态:

    import Vue from 'vue'
    import Vuex from 'vuex'
    import axios from 'axios'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
        state: {
            films:[],
            ships:[]
        },
  9. 接下来,添加加载船舶(ships)和影片(films)数据的操作。他们都应该使用突变(mutations)来为状态(state)分配值:

        mutations: {
            setFilms(state, films) {
                state.films = films;
            },
            setShips(state, ships) {
                state.ships = ships;
            }
        },
        actions: {
            loadFilms(context) {
                axios.get('https://swapi.dev/api/films')
                .then(res => {
                    context.commit('setFilms', res.data.results);
                }).catch(error => {
                    console.error(error);
                });
            },
            loadShips(context) {
                axios.get('https://swapi.dev/api/starships')
                .then(res => {
                    context.commit('setShips', res.data.results);
                }).catch(error => {
                    console.error(error);
                });
            }
        }
    })
  10. 使用以下命令运行您的应用程序:

    npm run serve

您的输出将如下所示:

image 2023 10 16 23 29 42 491
Figure 4. Figure 10.5: The final output

总而言之,与没有 Vuex 的初始版本相比,这并不是一个巨大的变化(如果我们忽略 UI 更改),但现在我们所有的 API 使用都由 store 处理。如果由于某种原因我们决定停止使用 Axios 并改用 Fetch,可以在这里完成。无论我们决定添加缓存系统还是存储数据以供离线使用,都可以在 store 中完成。通过运行 npm run serve 并在浏览器中打开 URL 来自行测试此版本。

现在是时候将您所学到的知识运用到下一个活动中了!

活动 10.01:使用 Axios 和 Vuex 进行身份验证

使用 Vuex 可以做的更有趣的事情之一是管理身份验证。我们这么说是什么意思? 在许多 API 中,在使用服务之前需要进行身份验证。用户进行身份验证后,他们将获得一个令牌。 在将来调用 API 时,令牌通常作为标头传递,让远程服务知道这是授权用户。 Vuex 可以为您处理所有这些,而 Axios 可以轻松处理标头,因此让我们考虑一个实际的示例。

构建具有身份验证和授权的服务器远远超出了本书的范围,因此我们将伪造它。我们将有两个端点,它们是 JSONBin.io 的简单使用,这是我们在第 9 章使用 Vuex 中使用的服务 – 状态(State)、获取器(Getters)、操作(Actions)和突变(Mutations)。第一个端点url将返回一个 token 令牌:

{
  "token": 123456789
}

第二个端点将返回猫(cats)数组:

[
    {
        "name": "Luna",
        "gender": "female"
    },
    {
        "name": "Pig",
        "gender": "female"
    },
    {
        "name": "Cracker",
        "gender": "male"
    },
    {
        "name": "Sammy",
        "gender": "male"
    },
    {
        "name": "Elise",
        "gender": "female"
    }
]

在本活动中,我们将使用 Vue Router 来处理表示应用程序的两个视图(views):登录屏幕和猫显示屏幕。

步骤:

  1. 显示应用程序初始视图的登录屏幕。它应该提示输入用户名和密码。

  2. 将登录凭据传递到端点并获取令牌。这部分将是伪造的,因为我们没有构建一个完整的、真实的身份验证系统。

  3. 从远程端点加载 cats 并将令牌作为身份验证标头传递。

您最初应该得到以下输出:

1697470629975
Figure 5. Figure 10.6: Initial login screen

登录后,您将看到数据,如下:

image 2023 10 16 23 37 55 531
Figure 6. Figure 10.7: Successfully displaying the data after login

总结

在本章中,您学习了 Vuex 的一个非常重要的用例——使用远程 API。远程 API 可以为您的应用程序提供数量惊人的附加功能,有时开发人员只需支付很少甚至无需支付额外费用。您了解了如何使用 Axios 使网络调用更加轻松,以及如何将其与 Vuex 的状态管理功能相结合。最后,将其与 Vue Router 结合起来创建一个简单的登录/授权演示。

在下一章中,我们将讨论如何使用模块构建更复杂的 Vuex 存储。