Scaffolding

We'll be developing our SPA using Vue.js 2 inside the same Laravel project. There's a single web route defined inside routes/web.php which renders the welcome.blade.php view - and this is everything we need from Laravel for this part. Edit the view like follows:

<!doctype html>
<html lang="{{ app()->getLocale() }}">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="csrf-token" content="{{ csrf_token() }}">

        <title>SPA tutorial by Voerro</title>
    </head>
    <body>
        <div id="app">
            <vue-layout></vue-layout>
        </div>

        <script src="/js/app.js"></script>
    </body>
</html>

This is quite simple, isn't it? We're just providing the basic HTML for a page and then including a vue-layout Vue.js component, which will take control of rendering the dynamic page content. Alright, now run these commands:

npm install
npm run dev

Start the development server php artisan serve, go to http://localhost:8000 and you should see a blank page. There's going to be a Vue error in the console because we haven't created the vue-layout component yet. That's going to be the next step. Create a new file resources/assets/js/views/Layout.vue:

<template>
    <div>Hello World</div>
</template>

<script>
export default {

}
</script>

Register the component inside the resources/assets/js/app.js file like you usually do with Vue components in Laravel:

Vue.component('vue-layout', require('./views/Layout.vue'));

Re-build the assets by either running npm run dev or npm run watch (which will rebuild the assets automatically each time you make changes and save a file). Refresh the page and you should see "Hello World" at the top. If so, our component is mounted successfully.

Next we're going to install vue-router - this is going to be our front-end router, which will take care of navigation between pages. First, run:

npm install vue-router --save-dev

Then, let's create a dedicated file to store our routes at resources/assets/js/routes.js. We're going to create a couple of publicly accessible pages, a login page, and a protected page that requires authentication to access. Note how each page corresponds to a Vue component.

import VueRouter from 'vue-router';

let routes = [
    {
        path: '/',
        component: require('./views/Home.vue')
    },
    {
        path: '/about',
        component: require('./views/About.vue')
    },
    {
        path: '/login',
        component: require('./views/Login.vue')
    },
    {
        path: '/dashboard',
        component: require('./views/Dashboard.vue')
    }
];

export default new VueRouter({
    routes
});

Go back to the resources/assets/js/app.js and import our routes.js file at the top:

import router from './routes.js';

Then add it to our Vue instance below:

const app = new Vue({
    el: '#app',

    router
});

Now we also need to tell Vue to use the vue-router. Import it at the top as well:

import VueRouter from 'vue-router';

Then, after requiring the Vue, add this:

window.Vue = require('vue'); // requiring the Vue

Vue.use(VueRouter);

If you're going to refresh the page now you're still going to see the "Hello World" text. Let's edit the Layout.vue component's template like this:

<template>
    <div>
        <div>
            <router-link to="/">Home</router-link>
            <router-link to="/about">About</router-link>
            <router-link to="/dashboard">Dashboard</router-link>
        </div>

        <div>
            <router-link to="/login">Login</router-link>
        </div>

        <div style="margin-top: 2rem">
            <router-view></router-view>
        </div>
    </div>
</template>

Both router-link and router-view are components provided by the vue-router package. We've added a link to each route we have and a router-view which will generate corresponding Vue components in its place. Now, if you've noticed, each route in the routes.js file points to a Vue component, but we haven't created those yet. Go ahead and create the 4 components according to routes.js. No need to register these components as we did with Layout.vue. Put the same boilerplate code in each file for now:

<template>
    <div>Hello World</div>
</template>

<script>
export default {

}
</script>

You might replace "Hello World" with the name of each page to test the navigation. Now you can refresh the page and you'll see the content of the Home.vue component. Click on the links and you'll see the content of the page and the browser URL change without page reload.

By the way, did you notice how the paths of all routes are prepended with #/ in the URL? Adding a hash sign tells browser to treat everything after it as a name of a fragment from the same page, hence no page reloading takes place.

Next we are going to talk about authentication.