Работа с фронтендом

В первую очередь нам нужна страница, на которую будет выводиться список постов. Для этого создадим новый вид – index.blade.php (папка resources/views). Его содержимое должно быть следующим:
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="/css/app.css">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<body>
    <div id="app" class="container">
    <h1 class="text-center mt-3">Рейтинг постов</h1>

    <div class="row">
      <article v-for="post in sortedPosts"
        :key="post.id"
        class="col-sm-8 offset-sm-2 mb-3">
        <voting
          :post="post"
          :posts="posts"
          :class="{ 'border border-success': post.votes >= 30 }"
          action="/api/@{{ post.id }}/vote">
        </voting>
      </article>
    </div>
    </div>
    <script src="/js/app.js"></script>
</body>
</html>
В строчке
<article v-for="post in sortedPosts"
через цикл выводятся все посты. Цикл осуществляется с помощью Vue.js.

В следующей строчке в качестве ключа указываем поле id каждого поста. Ключ рекомендуется всегда использовать с v-for, но в данном примере можно обойтись и без него.

Далее у нас идет компонент с именем voting.
<voting
  :post="post"
  :posts="posts"
  :class="{ 'border border-success': post.votes >= 30 }">
</voting>
Здесь есть 2 входящих параметра: post и posts, а также директива :class для автоматического добавления css-классов рамки вокруг поста (border и border-success). Как уже говорилось в самом начале, рамка будет появляться, если пост наберет 30 голосов.

Теперь нам нужно установить шрифты fontawesome, vue и bootstrap. Для этого воспользуемся следующими командами:
npm install --save-dev @fortawesome/fontawesome-free
composer require laravel/ui --dev
php artisan ui vue
npm install
Теперь отредактируем файл resources/js/app.js.

Найдем строчку:
Vue.component('example-component', require('./components/ExampleComponent.vue').default);
и заменим ее на следующую:
Vue.component('voting', require('./components/Voting.vue').default);
В этой строчке мы регистрируем свой компонент из файла Voting.vue. Этот файл мы создадим позже.

Далее в файле app.js расположен блок кода:
const app = new Vue({
    el: '#app',
});
В этом блоке создается новое Vue-приложение и привязывается к веб-странице. В этот блок мы должны добавить дополнительный код и в результате должно получиться следующее:
const app = new Vue({
    el: '#app',

    data: {
        posts: [],
    },

    created: function() {
        this.read();
    },
    
    methods: {
        read() {
           axios.get('/api/posts').then(({ data }) => {
            this.posts = data;
           });
        },
    },

    computed: {
        sortedPosts() {
          return this.posts.sort((a, b) => {
            return b.votes - a.votes
          });
        }
    }
});
Здесь в методе read() мы получаем посты с сервера, с помощью axios, сразу после создания экземпляра Vue (хук created). В вычисляемом свойстве sortedPosts мы сортируем посты по количеству голосов. Напоминаю, что свойство sortedPosts используется в ранее созданном виде index.blade.php (папка resources/views) для вывода списка постов на страницу.

Осталось создать файл Voting.vue для компонента voting.

В папке resources/js/components создадим новый файл Voting.vue. Его содержимое должно быть следующим:
<template>
    <div class="card">
        <div class="card-header">
            <a href="">
                {{ post.title }}
            </a>
            
            <span style="cursor: pointer" class="float-right" 
             @click="vote(post.id)">
                <i class="fas fa-angle-up"></i>
                <strong style="color: #3273dc">{{ post.votes }}</strong>
            </span>
        </div>
        <div class="card-body">
            {{ post.body}}
        </div>
    </div>
</template>

<script>
    export default {
        props: ['post', 'posts'],
       
        methods: {
            vote(id) {
              axios.post("/api/"+id+"/vote");
              const post = this.posts.find(
                post => post.id === id
              );
              post.votes++;
            }
        }        
    }
</script>
Здесь в шаблоне по нажатию на span осуществляется вызов функции vote(id). В которой происходит инкрементация количества голосов поста как на сервере (с помощью axios) так и у клиента (Vue-свойство post).

На этом все. Осталось запустить команду для компиляции изменений:
npm run dev