通过 Laravel 创建一个 Vue 单页面应用(二)

Laravel and Vue
在这个教程中,我们通过学习怎样从 Vue 组件中的 Laravel API 加载异步数据,来继续在 Laravel 中创建一个 Vue 单页应用(SPA)。我们也会看看相关的错误处理,比如当 API 返回错误,接口如何响应。

如果你没有学习第一部分,我们通过 Vue Router 和 Laravel 后端组建的 Vue 单页应用(SPA)。如果你想继续跟着学习的话,你应该先去完整地学习一下第一部分!

保持服务端数据简单,我们的 API 将返回假数据。在第三部分,我们将让 API 通过控制器从数据库中返回测试数据。

API 路由

Vue 单页应用是无状态的,这就要求我们发起 API 请求到 Laravel 路由的时候,需要通过定义在 routes/api.php 中的路由。API 路由没有使用 session 状态,表示应用在后端是真的没有状态。

示例中,假设我们需要一个用户列表,来演示从 Vue 应用发起一个异步请求到后端:

Route::get('/users', function () {
    return factory('App\User', 10)->make();
});

我们的临时路由采用 模型工厂 来创建一个还没有保存到数据库的 Eloquent 模型集合。使用 make() 方法, 不会将测试数据存入数据库,反而它会返回一个新的还没有存入数据库的 App\User 实例。

在 routes/api.php 定义一个路由,意味着请求都会有一个 /api 前缀,因为这个前缀定义在应用的 RouteServiceProvider 类中:

protected function mapApiRoutes()
{
    Route::prefix('api')
         ->middleware('api')
         ->namespace($this->namespace)
         ->group(base_path('routes/api.php'));
}

结果是 GET /api/users,示例会类似下面这样:

[
   {
      "name":"Roel Rosenbaum I",
      "email":"catharine.kreiger@example.net"
   },
   {
      "name":"Prof. Clarissa Osinski",
      "email":"wilfrid.kiehn@example.com"
   },
   {
      "name":"Myrtle Wyman",
      "email":"dan31@example.com"
   },
   ...
]

客户端路由

在 第一部分 中,我们在 resources/assets/js/app.js 中新建了几个路由来演示SPA 的导航。任何时候想要添加一个新路由,我们可以在 routes 数组中新建一个定义了路径,名称以及组件的对象。最后一个路由就是新建的 /users 路由:

import UsersIndex from './views/UsersIndex';

const router = new VueRouter({
    mode: 'history',
    routes: [
        {
            path: '/',
            name: 'home',
            component: Home
        },
        {
            path: '/hello',
            name: 'hello',
            component: Hello,
        },
        {
            path: '/users',
            name: 'users.index',
            component: UsersIndex,
        },
    ],
});

UsersIndex 组件

有一个路由使用 UsersIndex 组件;当前我们希望它(位于 resources/assets/js/views/UsersIndex.vue )像下面这样:

<template>
    <div class="users">
        <div class="loading" v-if="loading">
            Loading...
        </div>

        <div v-if="error" class="error">
            {{ error }}
        </div>

        <ul v-if="users">
            <li v-for="{ name, email } in users">
                <strong>Name:</strong> {{ name }},
                <strong>Email:</strong> {{ email }}
            </li>
        </ul>
    </div>
</template>
<script>
import axios from 'axios';
export default {
    data() {
        return {
            loading: false,
            users: null,
            error: null,
        };
    },
    created() {
        this.fetchData();
    },
    methods: {
        fetchData() {
            this.error = this.users = null;
            this.loading = true;
            axios
                .get('/api/users')
                .then(response => {
                    console.log(response);
                });
        }
    }
}
</script>

如果你刚接触 Vue,可能会碰到一些不熟悉的概念。建议读一读一下 Vue 组件 文档来熟悉一下 Vue 的生命周期钩子(新建,加载,等等)。

在这个组件中,在组件 创建 的时候获取异步数据。定义一个 fechData() 方法来初始化 error 和 users 属性为 null,设置 loading 为 true。

 fetchData() 方法中最后一行使用 Axios 库来向 Laravel API 发起一个 HTTP 请求。 Axios 是一个 promise-based HTTP 客户端,通过链式调用  then() 回调来记录返回并且最终赋值给 users 数据属性。

下面是在应用中加载 /users (客户端页面,而非 API ),控制台数据的样子:

Log the users response to the console

另一个希望你注意的是 解构 ,如下:

<li v-for="{ name, email } in users">
    <strong>Name:</strong> {{ name }},
    <strong>Email:</strong> {{ email }}
</li>

解构是一种有效的方式来为某个对象来提取 props,而且简洁/易懂。

完成路由组件

我们现在有一个 /users组件和路由,让我们创建一个导航链接给App组件,指向users从而实现设置用户数据:

在 resources/assets/js/views/App.vue中, 添加一个指向用户路由的链接:

<template>
    <div>
        <h1>Vue Router Demo App</h1>

        <p>
            <router-link :to="{ name: 'home' }">Home</router-link> |
            <router-link :to="{ name: 'hello' }">Hello World</router-link> |
            <router-link :to="{ name: 'users.index' }">Users</router-link>
        </p>

        <div class="container">
            <router-view></router-view>
        </div>
    </div>
</template>
<script>
    export default {}
</script>

接下来,让我们更新 UsersIndex.vue文件来设置用户数据:

fetchData() {
    this.error = this.users = null;
    this.loading = true;
    axios
        .get('/api/users')
        .then(response => {
            this.loading = false;
            this.users = response.data;
        });
}

现在,如果刷新页面,您应该会看到类似以下内容:

The Vue Router /api/users route with users listed

错误处理

组件通常要运行地符合预期,但是我们还没有针对 API 错误的处理。我们在 API 中模拟一个服务端错误:

Route::get('/users', function () {
    if (rand(1, 10) < 3) {
        abort(500, 'We could not retrieve the users');
    }

    return factory('App\User', 10)->make();
});

使用 rand() ,在其值小于 3 的时候来终端请求。如果你刷新页面几次,你可能会看到“加载中…”, 如果你检查开发者工具,你会发现一个没有捕获的来之 Axios 请求的错误:

Uncaught API exception from Laravel

我们可以处理这个失败的请求通过在 Axios prpmise 上链式调用 catch():

fetchData() {
    this.error = this.users = null;
    this.loading = true;
    axios
        .get('/api/users')
        .then(response => {
            this.loading = false;
            this.users = response.data;
        }).catch(error => {
            this.loading = false;
            this.error = error.response.data.message || error.message;
        });
}

设置 loading 属性值为 false,使用响应的错误异常来设置 message。这个错误信息在返回给 exception.message 属性。

为了好的用户体验,在这个条件下,我们在 UsersIndex.vue 模版中设置一个 “再来一次” 的按钮,这个按钮会简单的调用 fetchData 方法来刷新 users 属性:

<div v-if="error" class="error">
    <p>{{ error }}</p>

    <p>
        <button @click.prevent="fetchData">
            Try Again
        </button>
    </p>
</div>

现在如果失败了,UI 应该像下面这样:

Failed Response with a "try again" button

总结

在这个简短的文章中,我们添加了一个新的路由从无状态的 Laravel API 中来获取一些假的用户。我们使用 “后置导航” 来针对性的获取数据。 或者采用其他的方式,比如在组件创建的时候从 API 中获取。

在 第三部分 我们尝试在 Vue Router 中使用一个回调来获取数,在导航到组件之前,让你看看如何在渲染 router view 之前获取数据。

我们也会转换 API 为从已经初始化的数据库表获取数据,因此我们可以通过设置路由参数来导航到一个具体的用户。

现在,来看看通过 Laravel 创建一个 Vue 单页面应用的 第三部分 !

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://laravel-news.com/building-vue-sp...

译文地址:https://learnku.com/laravel/t/34281

本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 1

我是第一个评论的吗? 哈哈, 写的非常好, 受用 。

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!