文章转发自专业的Laravel开发者社区,原始连接: https://learnku.com/laravel/t...
咱们将经过演示在 vue-router
进入一个路由以前,如何异步加载数据来继续使用 Laravel
构建咱们的 Vue SPA
。php
以前在 经过 Laravel 建立一个 Vue 单页应用(二) 中完成了 UsersIndex
组件异步地从 API
中加载用户。 简化了从数据库构建一个真实的后端 API
,选择经过 Laravel
的 factory()
方法在 API
返回中模拟假数据。html
若是你尚未读过经过 Laravel
构建 Vue
单页应用的 第一部分 和 第二部分,我建议你先去看看,再回到这里。我会在这里等你。vue
这篇教程,咱们将把模拟的 /users
返回替换为真正的由数据库支撑的。我习惯使用 MySQL
,可是你可使用任何你想用的数据库驱动!mysql
UsersIndex.vue
路由组件在生命周期 created()
中经过 API
加载数据。下面是第二部分结尾的 fetchData()
方法示例:ios
created() { this.fetchData(); }, methods: { 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; }); } }
我将演示若是经过组件的前置导航从 API
中提取数据,可是这以前咱们须要让 API
输出一些真实数据。laravel
咱们将建立一个 UsersController
使用 Laravel 5.5 新的 API 资源 来返回 JSON 数据。git
在建立控制器和 API 资源以前, 让咱们首先设置一个数据库而且进行数据填充,以便为咱们的 SPA 提供一些测试数据。github
咱们使用 make:seeder
命令来建立一个用户填充:vue-router
php artisan make:seeder UsersTableSeeder
UsersTableSeeder
很是简单。咱们使用模型工厂建立 50 个用户:sql
<?php use Illuminate\Database\Seeder; class UsersTableSeeder extends Seeder { public function run() { factory(App\User::class, 50)->create(); } }
接下来,咱们将 UsersTableSeeder
添加到 database/seeds/DatabaseSeeder.php
文件中:
<?php use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { $this->call([ UsersTableSeeder::class, ]); } }
若是不先建立和配置数据库,咱们将不能使用数据填充。
是时候给咱们的Vue SPA Laravel应用链接一个真实的数据库了。你能够经过使用相似TablePlus 的GUI工具来使用SQLite或者MySQL。若是你是Laravel的新手,你能够查阅在数据库入门上的大量文档。
若是你有一个运行在你设备上的MySQL实例,你可使用如下命令行至关快速建立一个新数据库(假设你本地环境没有设置密码):
mysql -u root -e"create database vue_spa;" # 或者经过-p参数来输入密码 mysql -u root -e"create database vue_spa;" -p
当你有了数据库,在 .env
文件添加配置DB_DATABASE=vue_spa
。若是你遇到了问题,请遵循文档,这样可使您的数据库更容易地工做。
一旦你配置好了数据库链接,你能够迁移你的数据表和添加填充数据。Laravel附带了一个Users表的迁移,咱们使用它来填充数据:
# 确保数据库seeders自动加载 composer dump-autoload php artisan migrate:fresh --seed
若是你愿意,你也可使用单独的artisan db:seed
命令!就像这样;你应该有一个包含50个用户的数据库,咱们能够经过api查询和返回。
第二章, 模拟的 /users
在 routes/api.php
中长下面这样:
Route::get('/users', function () { return factory('App\User', 10)->make(); });
咱们来新建一个控制器类,这样能够在生产环境使用 php artisan route:cache
来得到必定的益处,这种方式不支持闭包。咱们在命令行中同时建立控制器和 User API
资源类:
php artisan make:controller Api/UsersController php artisan make:resource UserResource
第一命令是在 app/Http/Controllers/Api
目录中建立一个 User
控制器,第二个命令在 app/Http/Resources
目录中建立 UserResource
。
下面控制器和 Api
命名空间对应的的新 routes/api.php
代码:
Route::namespace('Api')->group(function () { Route::get('/users', 'UsersController@index'); });
控制很直接;返回一个带分页的Eloquent API
:
<?php namespace App\Http\Controllers\Api; use App\User; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use App\Http\Resources\UserResource; class UsersController extends Controller { public function index() { return UserResource::collection(User::paginate(10)); } }
下面是一个 JSON
响应的例子,和以前 UserResource
的 API
格式相似:
{ "data":[ { "name":"Francis Marquardt", "email":"schamberger.adrian@example.net" }, { "name":"Dr. Florine Beatty", "email":"fcummerata@example.org" }, ... ], "links":{ "first":"http:\/\/vue-router.test\/api\/users?page=1", "last":"http:\/\/vue-router.test\/api\/users?page=5", "prev":null, "next":"http:\/\/vue-router.test\/api\/users?page=2" }, "meta":{ "current_page":1, "from":1, "last_page":5, "path":"http:\/\/vue-router.test\/api\/users", "per_page":10, "to":10, "total":50 } }
很奇妙,Laravel
自动加上了分页数据,而且将用户信息分配到 data
属性!
下面是 UserResource
类:
<?php namespace App\Http\Resources; use Illuminate\Http\Resources\Json\Resource; class UserResource extends Resource { /** * Transform the resource into an array. * * @param \Illuminate\Http\Request $request * @return array */ public function toArray($request) { return [ 'name' => $this->name, 'email' => $this->email, ]; } }
UserResource
将集合中的每一个 User
模型转换为数组,提供 UserResource::collection()
方法将用户的集合转换为 JSON
格式。
到如今,你应该有一个 /api/users
接口能够用在单页应用中,若是你继续学看下去,你会注意到新的返回已经不知足当前的组件。
咱们能够经过调整 then()
来调用用户数据所在的 data
键,来很快的让 UsersIndex.vue
组件从新工做。刚开始的时候它看起来有点新颖,可是 response.data
是一个响应对象,所以咱们能够这样设置用户数据:
this.users = response.data.data;
fetchData()
和新 API 配合调整后的方法以下:
fetchData() { this.error = this.users = null; this.loading = true; axios .get('/api/users') .then(response => { this.loading = false; this.users = response.data.data; }).catch(error => { this.loading = false; this.error = error.response.data.message || error.message; }); }
咱们的组件经过咱们新的API来运做,如今是演示如何在导航到组件以前获取用户信息的绝佳时机。
经过使用这种方法,咱们能够在获取数据以后导航到新路线。咱们能够经过使用beforeRouteEnter
守卫在进入组件以前实现。例子Vue路由文档以下:
beforeRouteEnter (to, from, next) { getPost(to.params.id, (err, post) => { next(vm => vm.setData(err, post)) }) },
查阅文档有完整的示例,但只需说咱们将异步获取用户数据,而且只有在完成以后咱们才会触发 next()
和在组件里设置数据(变量 vm
)
检查文档以得到完整的示例,但只需说咱们将异步获取用户数据,一旦完成,而且只有在完成以后,咱们才会触发next(,并在组件上设置数据(变量vm
)。
如下是getUsers
函数可能看起来像是异步从API获取用户,而后触发对组件的回调:
const getUsers = (page, callback) => { const params = { page }; axios .get('/api/users', { params }) .then(response => { callback(null, response.data); }).catch(error => { callback(error, error.response.data); }); };
注意,该方法不返回Promise,而是在完成或失败时触发回调。回调传递两个参数:一个错误和来自API调用的响应。
咱们的getUsers()
方法接受一个 page
变量,该变量最终做为查询字符串参数出如今请求中。若是为空(路由中没有传递页码),则API将默认设为page=1
。
最后我要指出的是const params
值。它其实是这样的:
{ params: { page: 1 } }
下面是咱们的beforeRouteEnter
守卫如何使用getUsers
函数获取异步数据,而后在组件上调用next()
设置它:
beforeRouteEnter (to, from, next) { const params = { page: to.query.page }; getUsers(to.query.page, (err, data) => { next(vm => vm.setData(err, data)); }); },
这是从API返回数据后的 getUsers()
调用中的callback
参数:
(err, data) => { next(vm => vm.setData(err, data)); }
而后在API成功响应时,在getUsers()
中这样调用:
callback(null, response.data);
当组件已经处于渲染状态,而且路由更改时,将调用beforeRouteUpdate
,而且Vue会在新路由中复用组件。例如,当咱们的用户从/users?page=2
跳转到 /users?page=3
。
beforeRouteUpdate
调用相似于beforeRouteEnter
。可是,前者能够在组件中使用 this
,所以在样式上会略有不一样:
// 当路由更改而且组件已经渲染时, // 逻辑会略有不一样。 beforeRouteUpdate (to, from, next) { this.users = this.links = this.meta = null getUsers(to.query.page, (err, data) => { this.setData(err, data); next(); }); },
因为组件处于渲染状态,咱们须要在从API获取下一组用户以前重置一些数据属性。咱们能够访问组件。所以,咱们能够先调用this.setData()
(我尚未向您展现),而后不须要回调就调用next()
。
最后,这是在UsersIndex
组件中的setData
方法:
setData(err, { data: users, links, meta }) { if (err) { this.error = err.toString(); } else { this.users = users; this.links = links; this.meta = meta; } },
setData()
方法经过使用对象析构来获取。data
, links
和 meta
键来自于API的响应。咱们清晰地使用data: users
将 data
赋值给新变量users
。
我已经向您展现了该UsersIndex组件的各个部分,咱们已经准备好将全部组件捆绑在一块儿,并进行一些很是基本的分页。本教程未向您展现如何构建分页,所以您能够本身找到(或建立)奇特的分页!
分页是一种很好的方式,向您展现如何以vue-router编程方式浏览SPA 。
这是带有咱们新的挂钩和方法的完整组件,这些新挂钩和方法可以使用路由器挂钩获取异步数据:
<template> <div class="users"> <div v-if="error" class="error"> <p>{{ error }}</p> </div> <ul v-if="users"> <li v-for="{ id, name, email } in users"> <strong>Name:</strong> {{ name }}, <strong>Email:</strong> {{ email }} </li> </ul> <div class="pagination"> <button :disabled="! prevPage" @click.prevent="goToPrev">Previous</button> {{ paginatonCount }} <button :disabled="! nextPage" @click.prevent="goToNext">Next</button> </div> </div> </template> <script> import axios from 'axios'; const getUsers = (page, callback) => { const params = { page }; axios .get('/api/users', { params }) .then(response => { callback(null, response.data); }).catch(error => { callback(error, error.response.data); }); }; export default { data() { return { users: null, meta: null, links: { first: null, last: null, next: null, prev: null, }, error: null, }; }, computed: { nextPage() { if (! this.meta || this.meta.current_page === this.meta.last_page) { return; } return this.meta.current_page + 1; }, prevPage() { if (! this.meta || this.meta.current_page === 1) { return; } return this.meta.current_page - 1; }, paginatonCount() { if (! this.meta) { return; } const { current_page, last_page } = this.meta; return `${current_page} of ${last_page}`; }, }, beforeRouteEnter (to, from, next) { getUsers(to.query.page, (err, data) => { next(vm => vm.setData(err, data)); }); }, // when route changes and this component is already rendered, // the logic will be slightly different. beforeRouteUpdate (to, from, next) { this.users = this.links = this.meta = null getUsers(to.query.page, (err, data) => { this.setData(err, data); next(); }); }, methods: { goToNext() { this.$router.push({ query: { page: this.nextPage, }, }); }, goToPrev() { this.$router.push({ name: 'users.index', query: { page: this.prevPage, } }); }, setData(err, { data: users, links, meta }) { if (err) { this.error = err.toString(); } else { this.users = users; this.links = links; this.meta = meta; } }, } } </script>
若是更容易理解,这里是做为GitHub Gist的UsersIndex.vue。
这里有不少新事物,所以我将指出一些更重要的观点。该goToNext()
和goToPrev()
方法演示了如何使用导航vue-router
使用this.$router.push
:
this.$router.push({ query: { page: `${this.nextPage}`, }, });
咱们正在将新页面推送到触发的查询字符串beforeRouteUpdate
。我还要指出的是,我向您展现<button>
了上一个和下一个动做的元素,主要是为了演示经过编程方式进行导航的过程vue-router
,您极可能会使用它<router-link />
来自动在分页路线之间导航。
我引入了三个计算属性(nextPage
,prevPage
和paginatonCount
)来肯定下一页和上一页的页码,并paginatonCount
显示了当前页码的可视计数和总页数。
下一个和上一个按钮使用计算出的属性来肯定是否应禁用它们,而goTo
方法使用这些计算出的属性将page
查询字符串参数推入下一页或上一页。当下一页或上一页在第一页和最后一页的边界处为空时,将禁用这些按钮。
代码中可能有一些冗余,可是此组件说明vue-router
了在进入路由以前用于获取数据的方法!
不要忘记确保经过运行Laravel Mix构建最新版本的JavaScript:
# NPM npm run dev # Watch to update automatically while developing npm run watch # Yarn yarn dev # Watch to update automatically while developing yarn watch
最后,这是咱们更新完整的UsersIndex.vue
组件后显示出的SPA结果:
咱们如今有一个有效的API,能够从数据库中获取真实数据,还有一个简单的分页组件,该组件在后端使用Laravel的API模型资源进行简单的分页连接并将数据包装在数据
键中。
接下来,咱们将致力于建立,编辑和删除用户。 一个/ users
资源将被锁定在一个实际的应用程序中,可是目前,咱们只是在构建CRUD功能来学习如何与vue-router
一块儿使用来异步导航和提取数据。
咱们还能够将axios客户端代码从组件中抽象出来,可是如今,这很简单,所以咱们将其保留在组件中,直到第4部分。一旦添加了其余API功能,咱们将想要建立专用的 HTTP客户端的模块。
您能够继续进行第4部分-编辑现有用户