著名的JavaScript框架专家Vue.js讨论了如何创建一个执行CRUD操作的完整堆栈web应用程序。
CRUD(创建,读取,更新和删除)是数据存储的基本操作,也是您作为Laravel开发人员学习的第一件事情之一。
但是,当您将Vue.js单页应用程序添加到此堆栈的前端时会发生什么?突然之间,你必须处理异步 CRUD,因为操作现在不需要刷新页面。这需要特别注意确保数据的状态在前端后端都是一致的。
在本教程中,我将向您展示如何设置一个完整的Vue&Laravel应用程序并演示每个CRUD操作。AJAX是此体系结构的关键,因此我们将使用Axios作为HTTP客户端。我还将向您展示一些处理这种体系结构UX用户缺陷的策略。
演示应用
演示全栈应用程序允许用户创建新的“Cruds”,我决定在创造性地思考一个令人难以置信的数量后,使用陌生名称的外星生物以及从红色变为绿色和后退的能力。
Cruds显示在主页上,用户有权创建新Crud,删除它们或更新颜色。
在Laravel后端添加CRUD
我们将在Laravel后端开始进行CRUD操作的教程。由于Laravel CRUD是其他地方广泛涉及的主题,因此我将继续简要介绍这一部分。
总之,我们会:
建立一个数据库
通过使用资源控制器设置RESTful API路由
定义控制器中的方法来执行CRUD操作
数据库
首先,迁移。我们的Cruds有两个属性:我们存储为文本的名称和颜色。
create_cruds_table.php
...
class CreateCrudsTable extends Migration
{
public function up()
{
Schema::create('cruds', function (Blueprint $table) {
$table->increments('id');
$table->text('name');
$table->text('color');
$table->timestamps();
});
}
...
}
...
API
现在我们设置RESTful API路由。正面的resource方法Route将自动创建我们需要的所有操作。但是,我们不需要edit,show或者store我们会排除这些。
routes/api.php
Route::resource('/cruds', 'CrudsController', [
'except' => ['edit', 'show', 'store']
]);
有了这个,我们现在可以在我们的API中使用各种端点:
调节器
我们现在需要在控制器中执行这些操作:
app/Http/Controllers/CrudsController.php
namespace App\\Http\\Controllers;
use App\\Crud;
use Illuminate\\Http\\Request;
use Illuminate\\Http\\Response;
use Faker\\Generator;
class CrudsController extends Controller
{
// Methods
}
我们来简单介绍一下每种方法:
create. 我们使用Faker Laravel附带的软件包随机化新Crud的名称和颜色。我们将新的Crud数据作为JSON发回。
...
public function create(Generator $faker)
{
$crud = new Crud();
$crud->name = $faker->lexify('????????');
$crud->color = $faker->boolean ? 'red' : 'green';
$crud->save();
return response($crud->jsonSerialize(), Response::HTTP_CREATED);
}
index. 我们用index方法返回全套Cruds 。在更严重的应用程序中,我们使用分页,但现在让我们保持简单。
...
public function index()
{
return response(Crud::all()->jsonSerialize(), Response::HTTP_OK);
}
update. 这个动作允许客户改变Crud的颜色。
...
public function update(Request $request, $id)
{
$crud = Crud::findOrFail($id);
$crud->color = $request->color;
$crud->save();
return response(null, Response::HTTP_OK);
}
destroy. 这是我们如何删除我们的Cruds。
...
public function destroy($id)
{
Crud::destroy($id);
return response(null, Response::HTTP_OK);
}
Vue.js App
现在为我们的Vue单页应用程序。我们将首先创建一个单一文件组件来表示我们称之为Cruds CrudComponent.vue。
该组件仅用于显示,并没有太多逻辑。以下是值得注意的方面:
显示的图像取决于Crud(red.png或green.png)的颜色。
有一个删除按钮,它会触发一个del点击方法,它会发出一个delete带有Crud ID 的事件。
有一个HTML选择(用于选择颜色),它触发一个update更改方法,该方法发出一个事件,其中update包含Crud的ID和所选的新颜色。
resources/assets/js/components/CrudComponent.vue
<template>
Name: {{ name | properCase }}
<select>
<option>
v-for="col in [ 'red', 'green' ]"
:value="col"
:key="col"
:selected="col === color ? 'selected' : ''"
>{{ col | properCase }}
/<option><button>Delete/<button>
<script>
export default {
computed: {
image() {
return `/images/${this.color}.png`;
}
},
methods: {
update(val) {
this.$emit('update', this.id, val.target.selectedOptions[0].value);
},
del() {
this.$emit('delete', this.id);
}
},
props: ['id', 'color', 'name'],
filters: {
properCase(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
}
}
在这个项目的另一个组成部分是App.js。这就是所有有趣的逻辑发生的地方,所以我们将逐步完成这一步。
我们从模板开始。这有以下工作:
用crud-component上面讨论的组件显示我们的Cruds 。
循环访问Crud对象(在数组中cruds),每个映射到一个实例crud-component。我们通过对组件作为道具经过一个CRUD的所有属性和设置的听众update,并delete从组件来的事件。
我们也有一个Add按钮,通过create点击触发一个方法来创建新的Cruds 。
resources/assets/js/components/App.vue
<template>
Cruds
<crud-component>
v-for="crud in cruds"
v-bind="crud"
:key="crud.id"
@update="update"
@delete="del"
>
/<crud-component><button>Add/<button>
下面是script从App.js。我们也来谈谈这个:
我们从一个Crud创建用于表示我们的Crud的新对象开始。每个人都有一个ID,颜色和名称。
我们导入相邻 CrudComponent
组件定义包含数组cruds作为数据属性。我还为每个CRUD操作存储了方法,这些操作将在下一节中介绍。
resources/assets/js/components/App.vue
<template>.../<template>
<script>
function Crud({ id, color, name}) {
this.id = id;
this.color = color;
this.name = name;
}
import CrudComponent from './CrudComponent.vue';
export default {
data() {
return {
cruds: []
}
},
methods: {
create() {
// To do
},
read() {
// To do
},
update(id, color) {
// To do
},
del(id) {
// To do
}
},
components: {
CrudComponent
}
}
使用AJAX从前端触发CRUD
由于这是数据库所在的位置,因此全栈应用程序中的所有CRUD操作都将在后端执行。但是,CRUD操作的触发通常会发生在前端。
因此,HTTP客户端(可以通过互联网在我们的前端和后端之间进行通信的东西)在这里很重要。Axios是一个很棒的HTTP客户端,它预装了默认的Laravel前端。
让我们再看看我们的资源表,因为每个AJAX调用都需要定位到相关的API端点:
读
我们从这个read方法开始。这个方法负责从后端检索我们的Cruds,并且将针对index我们的Laravel控制器的行为,从而使用GET endpoint/api/cruds。
我们可以设置一个GET调用window.axios.get,因为window在默认的Laravel前端设置中,Axios库已经作为对象的属性。
Axios的方法,例如get,post等等都会返回一个承诺。我们then用一个回调链接一个方法来访问响应。解析的对象可以被解构以允许方便地访问data回调中的属性,这是AJAX响应的主体。
resources/assets/js/components/App.vue
...
methods() {
read() {
window.axios.get('/api/cruds').then(({ data }) => {
// console.log(data)
});
},
...
}
/*
Sample response:
[
{
"id": 0,
"name": "ijjpfodc",
"color": "green",
"created_at": "2018-02-02 09:15:24",
"updated_at": "2018-02-02 09:24:12"
},
{
"id": 1,
"name": "wjwxecrf",
"color": "red",
"created_at": "2018-02-03 09:26:31",
"updated_at": "2018-02-03 09:26:31"
}
]
*/
正如你所看到的,Cruds是以JSON数组的形式返回的。Axios会自动分析JSON并为我们提供JavaScript对象,这很好。让我们在回调中遍历这些内容,然后使用我们的Crud工厂函数创建新的Cruds ,然后将它们推送到cruds数组数据属性即this.cruds.push(...)。
resources/assets/js/components/App.vue
...
methods() {
read() {
window.axios.get('/api/cruds').then(({ data }) => {
data.forEach(crud => {
this.cruds.push(new Crud(crud));
});
});
},
},
...
created() {
this.read();
}
注意:我们需要read在应用程序加载时以编程方式触发该方法。我们从created钩子上做到了这一点,但效果不是很好。read完全摆脱该方法会更好,只需在第一次加载时将应用程序的初始状态内联到文档头中即可。
完成后,我们现在可以在加载时在应用中看到Cruds:
更新(和同步状态)
该update行动要求我们发送表单数据,即color控制器知道要更新什么。Crud的ID在终点中给出。
这是讨论我在本文开头提到的问题的好时机:在全栈应用程序中,您必须确保数据的状态在前端和后端都一致。
在该update方法的情况下,我们可以在AJAX调用之前立即更新前端应用程序中的Crud对象,因为我们已经知道新状态。
但是,在AJAX调用完成之前,我们不会执行此更新。为什么?原因是该操作可能因某种原因失败:互联网连接可能会丢失,更新后的值可能会被数据库拒绝或其他原因。
如果我们等到服务器在更新前端状态之前做出响应,我们可以确定该操作已成功完成,并且前端和后端数据已同步。
resources/assets/js/components/App.vue
methods: {
read() {
...
},
update(id, color) {
window.axios.put(`/api/cruds/${id}`, { color }).then(() => {
// Once AJAX resolves we can update the Crud with the new color
this.cruds.find(crud => crud.id === id).color = color;
});
},
...
}
你可能会认为它的糟糕的用户体验等待AJAX解决,然后在不需要的时候显示更改的数据,但是我认为用户误导用户认为已经完成更改会更糟糕,实际上,我们并没有不知道它是否完成。
创建和删除
既然您已经理解了架构的关键点,那么您将能够理解这些最后两个操作,而无需我的评论:
resources/assets/js/components/App.vue
methods: {
read() {
...
},
update(id, color) {
...
},
create() {
window.axios.get('/api/cruds/create').then(({ data }) => {
this.cruds.push(new Crud(data));
});
},
del(id) {
window.axios.delete(`/api/cruds/${id}`).then(() => {
let index = this.cruds.findIndex(crud => crud.id === id);
this.cruds.splice(index, 1);
});
}
}
加载指标并禁用交互
如您所知,我们的CRUD操作是异步的,因此在等待AJAX呼叫到达服务器时,服务器会作出响应并接收响应时会有小的延迟。
为了改进用户体验,在我们等待当前操作解决的同时,拥有某种视觉加载指示器并禁用任何交互性会很好。这可以让用户知道正在发生的事情,并且可以让他们确定数据的状态。
对于Vue.js加载状态有一些很好的插件,但我只是想在这里做一些快速和肮脏的事情:在AJAX进行时,我将覆盖整个屏幕,div在应用程序的顶部半透明。这将用一块石头杀死上述两只鸟。
resources/views/index.blade.php
为此,我们将mute在AJAX正在进行时将布尔值从false 切换到true,并使用此值来显示/隐藏div。
resources/assets/js/components/App.vue
export default {
data() {
return {
cruds: [],
mute: false
}
},
...
}
下面是我们如何实现的触发mute的update方法。当该方法被调用时,mute被设置为true。当承诺解决时,AJAX已完成,因此用户再次与应用程序交互是安全的,因此我们将其设置mute为false。
resources/assets/js/components/App.vue
update(id, color) {
this.mute = true;
window.axios.put(`/api/cruds/${id}`, { color }).then(() => {
this.cruds.find(crud => crud.id === id).color = color;
this.mute = false;
});
},
您需要在每个CRUD方法中实现相同的功能,但为简洁起见,我不会在此显示。
为了使我们的加载指示器标记和CSS,我们
直接在我们的mount元素上添加元素 。从内联样式中可以看到,当类on添加时
resources/views/index.blade.php
<title>Cruds/<title><style>
html, body {
margin: 0;
padding: 0;,
height: 100%;
width: 100%;
background-color: #d1d1d1
}
#mute {
position: absolute;
}
#mute.on {
opacity: 0.7;
z-index: 1000;
background: white;
height: 100%;
width: 100%;
}
最后一块难题是on通过使用一个watch值来切换课程,mute每次mute更改时都会调用此方法:
export default {
...
watch: {
mute(val) {
document.getElementById('mute').className = val ? "on" : "";
}
}
}
完成后,您现在可以使用带有加载指示器的全功能Vue / Laravel CRUD应用程序。这里又一次充满荣耀:
不要忘记抓住这个GitHub仓库中的代码,如果您有任何想法或问题,请留下评论!
閱讀更多 愛碼農 的文章