如何为Laravel的API构建Vue前端

235 阅读8分钟

上一篇文章中,我们使用Laravel框架构建了一个JSON Rest API。在本教程中,我们将使用Vuejs来构建一个前端,它可以消费我们已经到位的API。我们可以使用Vue组件和AJAX来简单地从API中获取我们需要显示的数据,而不是渲染刀片文件。我们甚至会学习一点关于使用SCSS和Laravel Mix来定制Laravel的CSS。


安装依赖性

我们需要在Laravel实例的package.json文件中安装指定的依赖项来开始工作.要做到这一点,只需运行 **npm install**像这样在项目根目录下运行.
npm install laravel dependencies
这将会下载并安装所有前端开发所需的软件.


Laravel Mix

Laravel Mix是现在安装的依赖项之一。Webpack真的很难。Laravel Mix让它更容易。通过Mix和SCSS,我们可以快速改变网站的外观。在运行Mix之前, 我们需要了解它要为我们做什么。因此,默认情况下,Mix遵循的逻辑是在 webpack.mix.js这是在Laravel项目的根目录下。

const mix = require('laravel-mix');

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for the application as well as bundling up all the JS files.
 |
 */

mix.js('resources/js/app.js', 'public/js')
   .sass('resources/sass/app.scss', 'public/css');

然而这意味着什么呢?那么, 当你运行 npm run dev例如, Mix会查看资源/js/app.js资源/sass/app.scss的内容.然后,它将把这些原始资产编译成可用的代码,放在public/jspublic/css中。


我如何定制我的样式?

比方说,你想在Laravel项目中尝试很酷的Minty Bootswatch主题。我们怎么能用Mix来实现呢?非常简单!从Bootswatch网站下载**_variables.scss**文件,然后替换在 /resources/sass/_variables.scss.现在我们可以看到原文件和新文件。

原始的_variables.scss

// Body
$body-bg: #f8fafc;

// Typography
$font-family-sans-serif: 'Nunito', sans-serif;
$font-size-base: 0.9rem;
$line-height-base: 1.6;

// Colors
$blue: #3490dc;
$indigo: #6574cd;
$purple: #9561e2;
$pink: #f66d9b;
$red: #e3342f;
$orange: #f6993f;
$yellow: #ffed4a;
$green: #38c172;
$teal: #4dc0b5;
$cyan: #6cb2eb;

Minty版本 _variables.scss

// Minty 4.3.1
// Bootswatch

//
// Color system
//

$white:    #fff !default;
$gray-100: #f8f9fa !default;
$gray-200: #f7f7f9 !default;
$gray-300: #eceeef !default;
$gray-400: #ced4da !default;
$gray-500: #aaa !default;
$gray-600: #888 !default;
$gray-700: #5a5a5a !default;
$gray-800: #343a40 !default;
$gray-900: #212529 !default;
$black:    #000 !default;

$blue:    #007bff !default;
$indigo:  #6610f2 !default;
$purple:  #6f42c1 !default;
$pink:    #e83e8c !default;
$red:     #FF7851 !default;
$orange:  #fd7e14 !default;
$yellow:  #FFCE67 !default;
$green:   #56CC9D !default;
$teal:    #20c997 !default;
$cyan:    #6CC3D5 !default;

$primary:       #78C2AD !default;
$secondary:     #F3969A !default;
$success:       $green !default;
$info:          $cyan !default;
$warning:       $yellow !default;
$danger:        $red !default;
$light:         $gray-100 !default;
$dark:          $gray-800 !default;

$yiq-contrasted-threshold: 250 !default;

// Body

$body-color:                $gray-600 !default;

// Components

$border-radius:               .4rem !default;
$border-radius-lg:            .6rem !default;
$border-radius-sm:            .3rem !default;

// Fonts

$headings-font-family:        "Montserrat", -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !default;
$headings-color:              $gray-700 !default;

// Tables

$table-border-color:          rgba(0,0,0,0.05) !default;

// Dropdowns

$dropdown-link-hover-color:         $white !default;
$dropdown-link-hover-bg:            $secondary !default;

// Navbar

$navbar-dark-color:                 rgba($white,.6) !default;
$navbar-dark-hover-color:           $white !default;

$navbar-light-color:                rgba($black,.3) !default;
$navbar-light-hover-color:          $gray-700 !default;
$navbar-light-active-color:         $gray-700 !default;
$navbar-light-disabled-color:       rgba($black,.1) !default;

// Pagination

$pagination-color:                  $white !default;
$pagination-bg:                     $primary !default;
$pagination-border-color:           $primary !default;

$pagination-hover-color:            $white !default;
$pagination-hover-bg:               $secondary !default;
$pagination-hover-border-color:     $pagination-hover-bg !default;

$pagination-active-bg:              $secondary !default;
$pagination-active-border-color:    $pagination-active-bg !default;

$pagination-disabled-color:         $white !default;
$pagination-disabled-bg:            #CCE8E0 !default;
$pagination-disabled-border-color:  $pagination-disabled-bg !default;

// Breadcrumbs

$breadcrumb-bg:                     $primary !default;
$breadcrumb-divider-color:          $white !default;
$breadcrumb-active-color:           $breadcrumb-divider-color !default;

这个文件设置了所有这些变量的值,给Bootstrap一个全新的外观。现在我们可以像这样修改welcome.blade.php文件,只是为了测试一下。我们想测试一下不同的Bootstrap类,看看是否发生了新的效果。

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Laravel</title>

    <!-- Styles -->
    <link rel="stylesheet" href="{{ asset('/css/app.css') }}">
</head>
<body class="container">
<div class="jumbotron mt-3">
    <h1 class="display-3">Hello, world!</h1>
    <p class="lead">This is a simple hero unit!</p>
    <hr class="my-4">
    <p>It uses utility classes for typography and spacing to space content out within the larger container.</p>
    <button type="button" class="btn btn-primary">Primary</button>
    <button type="button" class="btn btn-secondary">Secondary</button>
    <button type="button" class="btn btn-success">Success</button>
    <button type="button" class="btn btn-info">Info</button>
    <button type="button" class="btn btn-warning">Warning</button>
    <button type="button" class="btn btn-danger">Danger</button>
    <button type="button" class="btn btn-link">Link</button>
</div>
<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>

运行混合

我们现在可以用 npm run dev运行mix,应该会有一个类似的结果。
done compiled successfully

注意:如果你在运行mix时遇到错误,请看这个帖子,应该会有帮助。

现在,我们看到的不是标准的闪屏,而是应用了新的样式。帅呆了!
laravel mix bootswatch minty


构建一个Vue前端

有了这一点点的设置和配置,我们现在可以开始为我们的API构建Vue前端了。只要有几个组件就可以了。我们将有一个简单的导航条组件和一个PostList组件。开始的时候,我们只需要把东西摆出来,然后边走边添加动态数据。


welcome.blade.php

Laravel仍然会加载这个视图文件作为主页。在这个主页上,有一个ID为 "app "的div。我们的Vue应用程序将附加或安装到这个div上。下面是该文件的开头部分。

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <script>window.Laravel = {csrfToken: '{{ csrf_token() }}'}</script>
    <title>Vue Front End For A Laravel API</title>
    <link rel="stylesheet" href="{{ asset('/css/app.css') }}">
</head>
<body class="container-fluid">
<div id="app">
    <navbar></navbar>
</div>
<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>

注意包含了csrf_token字段。如果没有这些字段,你会在控制台得到 "未找到CSRF令牌:laravel.com/docs/csrf#c… "的错误,所以一定要包括这些代码行。


添加一个导航条组件

你会注意到,在上面的第13行,有一个对组件的引用。我们需要建立和注册这个组件,以便它能够显示。我们可以导航到 resources/js/components目录并创建一个Navbar.vue文件。这个文件有一个特殊的 **.vue**扩展名,这意味着一些事情。首先,它是一个Vue的单文件组件。这些类型的文件允许你把模板、脚本、甚至自定义css放在同一个文件中。然后你通过Webpack(在这个例子中是Laravel Mix)构建文件,你就可以得到一个工作结果。非常酷!

resources/js/components/Navbar.vue

<template>
    <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
        <a class="navbar-brand" href="#">Minty Fresh</a>
    </nav>
</template>

现在, 我们把这个组件注册在 resources/js/app.js.

资源/js/app.js

require('./bootstrap');

window.Vue = require('vue');

Vue.component('navbar', require('./components/Navbar.vue').default);

const app = new Vue({
    el: '#app'
});

只要你已经设置了Laravel Mix来观察你的文件,现在通过运行 npm run watchnpm run watch-poll,那么你就可以访问主页,看到一个Minty导航条。
bootswatch minty navbar


列出帖子

为了列出一些帖子,我们可以在下面添加一个名为PostList.vue的新组件 resources/js/components.所以首先,继续添加该文件,就像我们在这里看到的那样。
post vue component in laravel

新的组件需要在app.js文件中注册。

资源/js/app.js

require('./bootstrap');

window.Vue = require('vue');

Vue.component('navbar', require('./components/Navbar.vue').default);
Vue.component('post-list', require('./components/PostList.vue').default);

const app = new Vue({
    el: '#app'
});

现在在PostList.vue文件中,我们需要填充模板和脚本区域。我们利用创建的Lifecycle Hook。这个函数在组件的实例被创建后被自动调用。这是在组件被装入页面之前,所以这是一个完美的时机,我们需要使用JavaScript Fetch api从API中获取数据。

resources/js/components/PostList.vue

<template>
    <div>
        <div class="card mb-2" v-for="post in posts" v-bind:key="post.id">
            <div class="card-body ">
                <h4 class="card-title">{{ post.title }}</h4>
                <p class="card-text">{{ post.body }}</p>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                posts: []
            };
        },

        created() {
            this.getPosts();
        },

        methods: {
            getPosts(api_url) {
                api_url = api_url || '/api/posts';
                fetch(api_url)
                    .then(response => response.json())
                    .then(response => {
                        this.posts = response.data;
                    })
                    .catch(err => console.log(err));
            }
        }
    };
</script>

在上面的代码中,第16行的 posts数组在第16行,它将保存包含所有帖子的api响应。这将在页面加载和 created()钩子运行时填充。当这种情况发生时 getPosts()函数被调用,该函数从api获取数据并将其分配给 **posts**变量。随着数据的到位,模板部分使用标准的Vue v-for来创建一个帖子列表。
laravel api response displayed with vue


添加一个分页器

我们在上面成功地显示了5个帖子,而且看起来非常酷!这就是我们的API。回顾一下,我们的API确实提供了关于上一个、下一个和当前链接的信息。这意味着你可以使用这些数据来创建一个分页器。下面突出显示的几行显示了显示分页器的补充内容。

<template>
    <div>
        <nav>
            <ul class="pagination justify-content-center">
                <li v-bind:class="[{disabled: !pagination.prev_page_url}]" class="page-item">
                    <a class="page-link" href="#" @click="getPosts(pagination.prev_page_url)">Previous</a>
                </li>
                <li class="page-item disabled">
                    <a class="page-link" href="#">{{ pagination.current_page }} of {{ pagination.last_page }}</a>
                </li>
                <li v-bind:class="[{disabled: !pagination.next_page_url}]" class="page-item">
                    <a class="page-link" href="#" @click="getPosts(pagination.next_page_url)">Next</a>
                </li>
            </ul>
        </nav>

        <div class="card mb-2" v-for="post in posts" v-bind:key="post.id">
            <div class="card-body ">
                <h4 class="card-title">{{ post.title }}</h4>
                <p class="card-text">{{ post.body }}</p>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                posts: [],
                pagination: {}
            };
        },

        created() {
            this.getPosts();
        },

        methods: {
            getPosts(api_url) {
                let vm = this;
                api_url = api_url || '/api/posts';
                fetch(api_url)
                    .then(response => response.json())
                    .then(response => {
                        this.posts = response.data;
                        vm.paginator(response.meta, response.links);
                    })
                    .catch(err => console.log(err));
            },

            paginator(meta, links) {
                this.pagination = {
                    current_page: meta.current_page,
                    last_page: meta.last_page,
                    next_page_url: links.next,
                    prev_page_url: links.prev
                };
            },
        }
    };
</script>

这导致了分页器的显示,它告诉我们在哪一页,并根据我们所处的位置动态地启用或禁用上一页和下一页按钮。


添加一个帖子

让我们在页面上添加一个表单,这样就可以通过向API发送POST请求来添加一个帖子。在后端,我们首先将分页改为每页3个,以便给我们一些空间。

<template>
    <div>
        <form @submit.prevent="addPost">
            <div class="form-group">
                <input type="text" class="form-control" placeholder="Title" v-model="post.title">
            </div>
            <div class="form-group">
                <textarea class="form-control" placeholder="Body" v-model="post.body"></textarea>
            </div>
            <button type="submit" class="btn btn-success">Save</button>
        </form>

        <nav>
            <ul class="pagination justify-content-center">
                <li v-bind:class="[{disabled: !pagination.prev_page_url}]" class="page-item">
                    <a class="page-link" href="#" @click="getPosts(pagination.prev_page_url)">Previous</a>
                </li>
                <li class="page-item disabled">
                    <a class="page-link" href="#">{{ pagination.current_page }} of {{ pagination.last_page }}</a>
                </li>
                <li v-bind:class="[{disabled: !pagination.next_page_url}]" class="page-item">
                    <a class="page-link" href="#" @click="getPosts(pagination.next_page_url)">Next</a>
                </li>
            </ul>
        </nav>

        <div class="card mb-2" v-for="post in posts" v-bind:key="post.id">
            <div class="card-body ">
                <h4 class="card-title">{{ post.title }}</h4>
                <p class="card-text">{{ post.body }}</p>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                posts: [],
                pagination: {},
                post: {
                    id: '',
                    title: '',
                    body: ''
                }
            };
        },

        created() {
            this.getPosts();
        },

        methods: {
            getPosts(api_url) {
                let vm = this;
                api_url = api_url || '/api/posts';
                fetch(api_url)
                    .then(response => response.json())
                    .then(response => {
                        this.posts = response.data;
                        vm.paginator(response.meta, response.links);
                    })
                    .catch(err => console.log(err));
            },

            paginator(meta, links) {
                this.pagination = {
                    current_page: meta.current_page,
                    last_page: meta.last_page,
                    next_page_url: links.next,
                    prev_page_url: links.prev
                };
            },

            addPost() {
                fetch('api/post', {
                    method: 'post',
                    body: JSON.stringify(this.post),
                    headers: {
                        'content-type': 'application/json'
                    }
                })
                    .then(response => response.json())
                    .then(data => {
                        this.getPosts();
                    })
                    .catch(err => console.log(err));
            }
        }
    };
</script>

现在我们有能力添加一个新的帖子,这很好 🙂


删除一个帖子

要删除一个帖子,我们可以在方法对象中添加一个新函数,名为deletePost()。它接受要删除的帖子的ID,然后向api发出一个ajax删除请求。下面的亮点显示了允许删除帖子的新代码。

<template>
    <div>
        <form @submit.prevent="addPost">
            <div class="form-group">
                <input type="text" class="form-control" placeholder="Title" v-model="post.title">
            </div>
            <div class="form-group">
                <textarea class="form-control" placeholder="Body" v-model="post.body"></textarea>
            </div>
            <button type="submit" class="btn btn-success">Save</button>
        </form>

        <nav>
            <ul class="pagination justify-content-center">
                <li v-bind:class="[{disabled: !pagination.prev_page_url}]" class="page-item">
                    <a class="page-link" href="#" @click="getPosts(pagination.prev_page_url)">Previous</a>
                </li>
                <li class="page-item disabled">
                    <a class="page-link" href="#">{{ pagination.current_page }} of {{ pagination.last_page }}</a>
                </li>
                <li v-bind:class="[{disabled: !pagination.next_page_url}]" class="page-item">
                    <a class="page-link" href="#" @click="getPosts(pagination.next_page_url)">Next</a>
                </li>
            </ul>
        </nav>

        <div class="card mb-2" v-for="post in posts" v-bind:key="post.id">
            <div class="card-body ">
                <h4 class="card-title">{{ post.title }}</h4>
                <p class="card-text">{{ post.body }}</p>
                <button type="button" @click="deletePost(post.id)" class="btn btn-secondary">Delete</button>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                posts: [],
                pagination: {},
                post: {
                    id: '',
                    title: '',
                    body: ''
                }
            };
        },

        created() {
            this.getPosts();
        },

        methods: {
            getPosts(api_url) {
                let vm = this;
                api_url = api_url || '/api/posts';
                fetch(api_url)
                    .then(response => response.json())
                    .then(response => {
                        this.posts = response.data;
                        vm.paginator(response.meta, response.links);
                    })
                    .catch(err => console.log(err));
            },

            paginator(meta, links) {
                this.pagination = {
                    current_page: meta.current_page,
                    last_page: meta.last_page,
                    next_page_url: links.next,
                    prev_page_url: links.prev
                };
            },

            addPost() {
                fetch('api/post', {
                    method: 'post',
                    body: JSON.stringify(this.post),
                    headers: {
                        'content-type': 'application/json'
                    }
                })
                    .then(response => response.json())
                    .then(data => {
                        this.getPosts();
                    })
                    .catch(err => console.log(err));
            },

            deletePost(id) {
                fetch('api/post/' + id, {
                    method: 'delete'
                })
                    .then(response => response.json())
                    .then(data => {
                        this.getPosts();
                    })
                    .catch(err => console.log(err));
            },
        }
    };
</script>

当一个帖子被删除时,页面会自动刷新。


更新一个帖子

我们可以为每个帖子添加一个新的按钮,它将提供更新该帖子的选项。这将是一个两步的过程。第一步是点击更新按钮,将帖子的数据加载到表单中。然后我们可以对表单中的数据进行修改,最后点击保存。这意味着**addPost()**函数将需要新的逻辑来说明这一点。但首先,让我们看看如何添加按钮,以允许通过加载正确的数据到表单中进行更新。

<template>
    <div>
        <form @submit.prevent="addPost">
            <div class="form-group">
                <input type="text" class="form-control" placeholder="Title" v-model="post.title">
            </div>
            <div class="form-group">
                <textarea class="form-control" placeholder="Body" v-model="post.body"></textarea>
            </div>
            <button type="submit" class="btn btn-success">Save</button>
        </form>

        <nav>
            <ul class="pagination justify-content-center">
                <li v-bind:class="[{disabled: !pagination.prev_page_url}]" class="page-item">
                    <a class="page-link" href="#" @click="getPosts(pagination.prev_page_url)">Previous</a>
                </li>
                <li class="page-item disabled">
                    <a class="page-link" href="#">{{ pagination.current_page }} of {{ pagination.last_page }}</a>
                </li>
                <li v-bind:class="[{disabled: !pagination.next_page_url}]" class="page-item">
                    <a class="page-link" href="#" @click="getPosts(pagination.next_page_url)">Next</a>
                </li>
            </ul>
        </nav>

        <div class="card mb-2" v-for="post in posts" v-bind:key="post.id">
            <div class="card-body ">
                <h4 class="card-title">{{ post.title }}</h4>
                <p class="card-text">{{ post.body }}</p>
                <button type="button" @click="deletePost(post.id)" class="btn btn-secondary">Delete</button>
                <button type="button" @click="updatePost(post)" class="btn btn-success">Update</button>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                posts: [],
                pagination: {},
                post: {
                    id: '',
                    title: '',
                    body: ''
                },
                update: false,
                post_id: ''
            };
        },

        created() {
            this.getPosts();
        },

        methods: {
            getPosts(api_url) {
                let vm = this;
                api_url = api_url || '/api/posts';
                fetch(api_url)
                    .then(response => response.json())
                    .then(response => {
                        this.posts = response.data;
                        vm.paginator(response.meta, response.links);
                    })
                    .catch(err => console.log(err));
            },

            paginator(meta, links) {
                this.pagination = {
                    current_page: meta.current_page,
                    last_page: meta.last_page,
                    next_page_url: links.next,
                    prev_page_url: links.prev
                };
            },

            addPost() {
                fetch('api/post', {
                    method: 'post',
                    body: JSON.stringify(this.post),
                    headers: {
                        'content-type': 'application/json'
                    }
                })
                    .then(response => response.json())
                    .then(data => {
                        this.getPosts();
                    })
                    .catch(err => console.log(err));
            },

            deletePost(id) {
                fetch('api/post/' + id, {
                    method: 'delete'
                })
                    .then(response => response.json())
                    .then(data => {
                        this.getPosts();
                    })
                    .catch(err => console.log(err));
            },

            updatePost(post) {
                this.update = true;
                this.post.id = post.id;
                this.post.post_id = post.id;
                this.post.title = post.title;
                this.post.body = post.body;
            }
        }
    };
</script>

根据你点击更新按钮的帖子,该数据会被加载到表单中,这样我们就可以对它采取行动。因此,如果你想更新帖子2,你可以点击那个特定的更新按钮,改变数据,然后保存。


修改addPost()函数

在上面的部分中,点击某个帖子的更新按钮可以将该帖子的数据加载到表单中。然而,点击表单的 "保存",目前会创建一个新的帖子,而不是更新刚刚加载到表单中的帖子。我们可以在addPost()函数中使用一些条件逻辑来解决这个问题。我们还可以为两个目的添加一个clearForm()函数。首先是允许用户在决定不做更新时清除表单,其次是一旦有新的帖子加入,就自动清除表单。

<template>
    <div>
        <form @submit.prevent="addPost">
            <div class="form-group">
                <input type="text" class="form-control" placeholder="Title" v-model="post.title">
            </div>
            <div class="form-group">
                <textarea class="form-control" placeholder="Body" v-model="post.body"></textarea>
            </div>
            <button type="submit" class="btn btn-success">Save</button>
            <button @click.prevent="clearForm()" class="btn btn-warning">Clear Form</button>
        </form>

        <nav>
            <ul class="pagination justify-content-center">
                <li v-bind:class="[{disabled: !pagination.prev_page_url}]" class="page-item">
                    <a class="page-link" href="#" @click="getPosts(pagination.prev_page_url)">Previous</a>
                </li>
                <li class="page-item disabled">
                    <a class="page-link" href="#">{{ pagination.current_page }} of {{ pagination.last_page }}</a>
                </li>
                <li v-bind:class="[{disabled: !pagination.next_page_url}]" class="page-item">
                    <a class="page-link" href="#" @click="getPosts(pagination.next_page_url)">Next</a>
                </li>
            </ul>
        </nav>

        <div class="card mb-2" v-for="post in posts" v-bind:key="post.id">
            <div class="card-body ">
                <h4 class="card-title">{{ post.title }}</h4>
                <p class="card-text">{{ post.body }}</p>
                <button type="button" @click="deletePost(post.id)" class="btn btn-secondary">Delete</button>
                <button type="button" @click="updatePost(post)" class="btn btn-success">Update</button>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                posts: [],
                pagination: {},
                post: {
                    id: '',
                    title: '',
                    body: ''
                },
                update: false,
                post_id: ''
            };
        },

        created() {
            this.getPosts();
        },

        methods: {
            getPosts(api_url) {
                let vm = this;
                api_url = api_url || '/api/posts';
                fetch(api_url)
                    .then(response => response.json())
                    .then(response => {
                        this.posts = response.data;
                        vm.paginator(response.meta, response.links);
                    })
                    .catch(err => console.log(err));
            },

            paginator(meta, links) {
                this.pagination = {
                    current_page: meta.current_page,
                    last_page: meta.last_page,
                    next_page_url: links.next,
                    prev_page_url: links.prev
                };
            },

            addPost() {
                if (this.update === false) {
                    fetch('api/post', {
                        method: 'post',
                        body: JSON.stringify(this.post),
                        headers: {
                            'content-type': 'application/json'
                        }
                    })
                        .then(response => response.json())
                        .then(data => {
                            this.clearForm();
                            this.getPosts();
                        })
                        .catch(err => console.log(err));
                } else {
                    fetch('api/post', {
                        method: 'put',
                        body: JSON.stringify(this.post),
                        headers: {
                            'content-type': 'application/json'
                        }
                    })
                        .then(response => response.json())
                        .then(data => {
                            this.clearForm();
                            this.getPosts();
                        })
                        .catch(err => console.log(err));
                }
            },

            deletePost(id) {
                fetch('api/post/' + id, {
                    method: 'delete'
                })
                    .then(response => response.json())
                    .then(data => {
                        this.getPosts();
                    })
                    .catch(err => console.log(err));
            },

            updatePost(post) {
                this.update = true;
                this.post.id = post.id;
                this.post.post_id = post.id;
                this.post.title = post.title;
                this.post.body = post.body;
            },

            clearForm() {
                this.update = false;
                this.post.id = null;
                this.post.post_id = null;
                this.post.title = '';
                this.post.body = '';
            }
        }
    };
</script>

因此,对于最终的结果,我们可以在前端使用Vue,在后端使用Laravel来完成Post的完整创建,读取,更新和删除。


为Laravel的API构建Vue前端 摘要

就这样, 我们现在有了一个Vue前端,用于Laravel的API资源,我们在之前的教程中已经建立了。我们在Laravel内部使用Mix工具建立了一个构建过程,将.vue文件编译为可生产的JavaScript。