Imagine you have a Laravel + Vue project with Posts in the DB but need to show only the first 300 characters of content in the Posts list. Where should you perform that slicing - in Vue or Laravel? Let's compare.
The main point is that if we slice text in Vue, it means we download too much data from the server, which is not needed.
For this demo project, we chose Laravel Breeze Vue+Inertia preset for the JavaScript part.
We have a Post
model with two fields: title
and content
. And imagine that content is quite long: around 5000 words.
database/factories/PostFactory.php
public function definition(): array{ return [ 'title' => fake()->sentence(), 'content' => fake()->paragraphs(250, true), ];}
Setup: API and Vue Component
The Laravel API endpoint:
routes/api.php:
use App\Models\Post; // ... Route::get('/v1/posts', function () { return Post::get(['id', 'title', 'content']);});
In Vue, here's how we get the data from the API:
resources/js/Pages/Dashboard.vue
<script setup>// ... onMounted(() => { axios.get('/api/v1/posts').then(response => { posts.value = response.data })})</script>
And here's how we present it:
resources/js/Pages/Dashboard.vue
// ...<div class="p-6 text-gray-900"> <div v-for="post in posts" :key="post.id" class="mb-8"> <Post :post="post" /> </div></div>
Slice Excerpt on the Vue Side: Computed Property
To display posts, create a Post
Component.
resources/js/Components/Post.vue
<script setup>import { computed } from 'vue'; const props = defineProps({ post: { type: Object, required: true }}) const excerpt = (value, length) => { return value.length > length ? value.substring(0, length) : value} const post_excerpt = computed(() => { return excerpt(props.post.content, 300)})</script> <template> <article> <h2 class="font-semibold text-xl mb-4">{{ post.title }}</h2> <div class="">{{ post_excerpt }}</div> </article></template>
Here we define the excerpt
function and a computed property post_excerpt
.
We download all the posts from the API and slice them on the Vue level.
Result: fetching posts with full content consumes 226.12 kB
of traffic data.
Slice Excerpt on the Laravel Side: Observer
Now let's take another approach and generate excerpts on the back end.
Create a new migration with a dedicated field called excerpt
:
Schema::table('posts', function (Blueprint $table) { $table->longText('excerpt');});
Create an Observer with the method creating()
:
php artisan make:observer PostObserver
app/Observers/PostObserver.php
use App\Models\Post; class PostObserver{ public function creating(Post $post) { $post->excerpt = str($post->content)->substr(0, 300); }}
When you create a post, Observer will fill in the excerpt
attribute before saving it into the database.
You can also use the words()
method to generate an excerpt based on word count. It can also append a string like ...
to the end of the text.
$post->excerpt = str($post->content)->words(65, '...');
For more methods, see Available String Helper Methods.
Now let's register our PostObserver
in EventServiceProvider
.
app/Providers/EventServiceProvider.php
use App\Models\Post;use App\Observers\PostObserver; // ... public function boot(): void{ // ... Post::observe(PostObserver::class);}
Now, update our API endpoint to return the excerpt
instead of content
.
routes/api.php:
Route::get('/v1/posts', function () { return Post::get(['id', 'title', 'content']); return Post::get(['id', 'title', 'excerpt']); });
And finally, we remove the computed property and excerpt function from the component. It should look as follows:
resources/js/Components/Post.vue
<template> <article> <h2 class="font-semibold text-xl mb-4">{{ post.title }}</h2> <div class="">{{ post.excerpt }}</div> </article></template>
Result: response size has been reduced drastically from 226.12 kB
to 2.99 kB
. It is 75 times.
Conclusion: response size may seem like a small amount of data, but it may compound for multiple simultaneous system users. Also, in the long run, this might result in higher costs for your traffic.
You can also watch a video from my course "Better Eloquent Performance" where I illustrate a similar concept: API Return: Kilobytes Optimization
No comments or questions yet...