
This is Part 5 of Rebuild Twitter with Laravel. Previously, you created the foundation, built profile and timeline, and introduced the tweeting feature. And the most important, you’ve upgraded to Laravel 5.4. You’re ready for the Laravel 5.5 which will be released in August of 2017 (Laravel 5.5).
In this part, you’re not bringing any new feature to your user. Instead, you improve your development process with Laravel Mix.
Previous parts of Rebuild Twitter with Laravel
Part 1 — Rebuild Twitter with Laravel — User and Authentication
Part 2 — Rebuild Twitter with Laravel — Followers
Part 3 — Rebuild Twitter with Laravel — Timeline
Part 4 — Rebuild Twitter with Laravel — Upgrade to 5.4, Post Tweet, Link Preview, URL Shortener
Offline version
If you are interested to study without the internet, you can get the FREE tutorials of this series in PDFs. Subscribe to my email list and I’ll deliver the PDF to your inbox.

Setup project for the first time
I’ve moved the setup instruction to the project README.MD.
Planning
Today we don’t talk about Twitter. We focus on organizing our frontend resources to be more manageable and reusable.
Let’s summarise your planning,
1. Replace Elixir with Mix
2. Move javascript code from blade template to javascript file
3. Version javascript and CSS files
Laravel Mix
Laravel Mix is a new project which replaced Laravel Elixir in Laravel 5.4.
Elixir and Mix are wrappers around popular frontend toolkits that aim to automate your development workflow. They are the tools that help you to combine, minify, uglify, and pre-process your javascript and CSS. Their end goal is to make developer’s life easier.
Laravel Elixir is built around Gulp and Laravel Mix is built around Webpack.
Replace Laravel Elixir with Mix
If you’ve been following Rebuild Twitter with Laravel, your project probably has no Mix even after upgrading to 5.4.
Here you manually replace Elixir with Mix.
You download the necessary files from the Laravel Github project.
Head to https://github.com/laravel/laravel/tree/v5.4.23
Replace these files in your project with Github project
1. /package.json replaces with https://github.com/laravel/laravel/blob/v5.4.23/package.json
2. /webpack.mix.js downloads from https://github.com/laravel/laravel/blob/v5.4.23/webpack.mix.js
3. /resources/assets/js/app.js replaces with https://github.com/laravel/laravel/blob/v5.4.23/resources/assets/js/app.js
4. /resources/assets/js/bootstrap.js replaces with https://github.com/laravel/laravel/blob/v5.4.23/resources/assets/js/bootstrap.js
Execute command to download packages.
// Terminal
npm install
npm run dev
When you hit errors on building scripts for development, delete the folder node_modules and package-lock.json. After that, execute the commands again.
// Terminal
npm install
npm run dev

It’s all set.
Move javascript code from blade template to javascript file
First of all, you remove all the javascript dependencies from blade file.
// resources/views/layouts/profile.blade.php
<script src="https://unpkg.com/vue@2.1.10/dist/vue.js"></script>
<script src="https://unpkg.com/vue-resource@1.2.0/dist/vue-resource.min.js"></script>
// resources/views/layouts/app.blade.php
<script src="https://unpkg.com/vue@2.1.10/dist/vue.js"></script>
// resources/views/home.blade.php
<script src="https://unpkg.com/vue-resource@1.2.0/dist/vue-resource.min.js"></script>
<script src="https://unpkg.com/vue-infinite-scroll@2.0.0"></script>
Then, you install the javascript dependencies with NPM.
// Terminal
npm install vue-resource --save-dev
npm install vue-infinite-scroll --save-dev
Require the dependencies in the app.js.
// resources/assets/js/app.js
...
window.Vue = require('vue');
require('vue-resource');
require('vue-infinite-scroll');
…
Next, move the tweet-component to Tweet.vue.
// resources/assets/js/components/Tweet.vue
<template>
<div v-if="tweet.author" class="list-group-item">
<h4 class="list-group-item-heading">{{ tweet.author.name }}</h4>
<p v-if="tweet.link && tweet.link.short_url">{{ tweet.link.url }}</p>
<p v-else>@{{ tweet.body }}</p>
<div class="list-group-item-text panel panel-default" v-if="tweet.link">
<a v-bind:href="tweet.link.short_url || tweet.link.url" target="_blank" style="text-decoration: none;">
<div class="media">
<div class="media-middle">
<img class="media-object center-block" style="max-width: 100%;" v-bind:src="tweet.link.cover">
</div>
<div class="media-body panel-body">
<h3 class="media-heading">
{{ tweet.link.title }}
</h3>
<div>
{{ tweet.link.description }}
</div>
</div>
</div>
</a>
</div>
<p class="list-group-item-text">{{ tweet.created_at }}</p>
</div>
</template>
<script>
export default {
props: ['tweet']
}
</script>
Declare the component.
// resources/assets/js/app.js
Vue.component('tweet-component', require('./components/Tweet.vue'));
Remove the app constant from app.js
// resources/assets/js/app.js
const app = new Vue({
el: '#app'
});
This constant will override the Vue object in home page.
Oh, wait. What about the javascript code on view file?
Move your javascript code in home.blade.php to resources/assets/js/pages/home.js.
// resources/assets/js/pages/home.js
var page = 1;
new Vue({
el: '#list-1',
data: {
page: 1,
items: [],
busy: false
},
methods: {
loadMore: function() {
this.busy = true;
var url = '/timeline' + (this.page > 1 ? '?page=' + this.page : '');
this.$http.get(url)
.then(response => {
var data = response.body;
// Push the response data into items
for (var i = 0, j = data.length; i < j; i++) {
this.items.push(data[i]);
}
// If the response data is empty,
// disable the infinite-scroll
this.busy = (j < 1);
// Increase the page number
this.page++;
});
}
}
});
Then, update the webpack build script.
// webpack.mix.js
let mix = require('laravel-mix');
// Extract the vendor, e.g. vue, from application's JS
// So that your user don't need to download the vendor libraries every time you updated application's JS
// Find more at https://laravel.com/docs/5.4/mix#vendor-extraction
mix.js('resources/assets/js/app.js', 'public/js')
.extract(['vue']);
// Add your on page JS here
mix.js('resources/assets/js/pages/home.js', 'public/js/pages');
// Your application's CSS
mix.sass('resources/assets/sass/app.scss', 'public/css');
Alright, now remove all the unnecessary javascript code and import the home.js.
// home.blade.php
@section('script')
<script src="/js/pages/home.js"></script>
@endsection
// resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
...
<!-- Scripts -->
<script src="/js/manifest.js"></script>
<script src="/js/vendor.js"></script>
<script src="/js/app.js"></script>
@yield('script')
</body>
</html>
Move the javascript code from Profile layout to a single javascript file.
// resources/assets/js/pages/layout_profile.js
new Vue({
el: '#app',
data: {
username: '{{ $user->username }}',
isFollowing: {{ $is_following ? 1 : 0 }},
followBtnTextArr: ['Follow', 'Unfollow'],
followBtnText: ''
},
methods: {
follows: function (event) {
var csrfToken = Laravel.csrfToken;
var url = this.isFollowing ? '/unfollows' : '/follows';
this.$http.post(url, {
'username': this.username
}, {
headers: {
'X-CSRF-TOKEN': csrfToken
}
})
.then(response => {
var data = response.body;
if (!data.status) {
alert(data.message);
return;
}
this.toggleFollowBtnText();
});
},
toggleFollowBtnText: function() {
this.isFollowing = (this.isFollowing + 1) % this.followBtnTextArr.length;
this.setFollowBtnText();
},
setFollowBtnText: function() {
this.followBtnText = this.followBtnTextArr[this.isFollowing];
}
},
mounted: function() {
this.setFollowBtnText();
}
});
Not the least of which, import the important javascript files on layout file.
// resources/views/layouts/profile.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
...
<!-- Scripts -->
<script src="/js/manifest.js"></script>
<script src="/js/vendor.js"></script>
<script src="/js/app.js"></script>
<script src="/js/pages/layout_profile.js"></script>
</body>
</html>
Well. It doesn’t work yet because the username and isFollowing are from PHP blade template.
username: '{{ $user->username }}',
isFollowing: {{ $is_following ? 1 : 0 }},
How do we inject them into Javascript?
Here I have a simple hack, which probably not an elegant solution. If you have better suggestion, let me know.
// resources/views/layouts/profile.blade.php
<!-- Scripts -->
<script src="/js/manifest.js"></script>
<script src="/js/vendor.js"></script>
<script src="/js/app.js"></script>
<script>
window.App = <?php echo json_encode([
'username' => $user->username,
'isFollowing' => $is_following ? 1 : 0,
]); ?>
</script>
<script src="/js/pages/layout_profile.js"></script>
Refer to the App.
// resources/assets/js/pages/layout_profile.js
data: {
username: App.username,
isFollowing: App.isFollowing,
followBtnTextArr: ['Follow', 'Unfollow'],
followBtnText: ''
},
You injected the variables into the javascript through window.App.
Run all Mix tasks.
// Terminal
npm run dev

Development
During development, you can automatic the build steps by watching the javascript and css files.
// Terminal
npm run watch
Build steps will run every time you save the changes to the file.
Versioning
Versioning is crucial in web development. Browser cache JS and CSS files for better performance. However, the downside is it won’t detect the changes in the file and download the newer version. Unless the user clears the cache in the browser. This is not a common practice for most of the people, especially not a web developer.
In a simple way, you can manually versioning your files by appending the ‘?version=1’ to the link to the file.
Laravel Mix provides a handy way for you. It version the file by appending a hash value to the file name to prevent repeating file name by mistake.
// webpack.mix.js
mix.version();
// Terminal
npm run dev

// resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
…
<link href="{{ mix('/css/app.css') }}" rel="stylesheet">
...
</head>
<body>
...
<!-- Scripts -->
<script src="{{ mix('/js/manifest.js') }}"></script>
<script src="{{ mix('/js/vendor.js') }}"></script>
<script src="{{ mix('/js/app.js') }}"></script>
</body>
</html>
// resources/views/layouts/profile.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
…
<link href="{{ mix('/css/app.css') }}" rel="stylesheet">
...
</head>
<body>
...
<!-- Scripts -->
<script src="{{ mix('/js/manifest.js') }}"></script>
<script src="{{ mix('/js/vendor.js') }}"></script>
<script src="{{ mix('/js/app.js') }}"></script>
...
<script src="{{ mix('/js/pages/layout_profile.js') }}"></script>
</body>
</html>
// resources/views/home.blade.php
@section('script')
<script src="{{ mix('/js/pages/home.js') }}"></script>
@endsection

You can version the files only for production.
// webpack.mix.js
if (mix.inProduction()) {
mix.version();
}
// Terminal
npm run production
There are two things you can do
1. Subscribe to my email list, I will email you when the next post is ready.
2. If you find this guide helpful, share this guide to your friend. I feel grateful if you do that, I’m sure your friend will thank you as well.
Originally published at I Teach You How To Code.