Ivan Reyné

Prototipatge ràpid amb Vue.js i Vuetify - tot en un sol fitxer

Prototipatge ràpids amb Vue.js i Vuetify - tot en un sol fitxer

En aquest article explicaré com podeu crear ràpidament un prototip d'una web/aplicació amb Vue.js i Vuetify.

Podeu trobar el codi complet a GitHub i també a CodePen.

Motivació

Alguna vegada heu tingut una idea per una aplicació però no estàveu segurs de si funcionaria? Alguna vegada heu hagut de començar un projecte d'un client amb poques especificacions i poc clares?!? :-D Normalment us costa fer coincidir el que "pinten" els dissenyadors amb el que s'acaba veient a l'aplicació? ... doncs bé ... ets al lloc correcte! Crear una prototips d'una web o d'una aplicació amb Vue.js i Vuetify és molt fàcil. Podreu implementar una navegació bàsica, els dissenyadors podran comprovar si el que estan pensant funciona i també podreu compartir-ho amb els vostres clients directament o penjar-lo a un lloc web de prova fàcilment i ràpida, tot sense necessitat de back-ends.

I el millor és que, com que Vuetify és una implementació de Material Design, podreu utilitzar aquesta manera de fer el prototip encara que acabeu implementant la aplicació/web un entorn de desenvolupament completament diferent.

Interessats? ... :-)

Com?

Farem servir un enfocament de "tot en un". Això vol dir que tindrem tota la nostra aplicació/web en un sol fitxer HTML. S'ha de tenir en compte que això funcionarà molt bé per a aplicacions/webs petits però per als més grans necessitarem una aproximació lleugerament diferent (ho veurem més endavant). El principal avantatge és que podreu carregar el prototip directament al navegador (fitxer -> obrir) sense necessitat de desplegar-lo a cap servidor web. I això és un avantatge molt gran!

Com que Vue.js és un "entorn web progressiu" que es pot carregar i utilitzar directament des d'una xarxa de distribució de contingut (CDN), i el mateix aplica a Vuetify, ho aprofitarem per construir el prototip en un únic fitxer HTML. Bàsicament el que farem serà:

  • Carregar les biblioteques de Vue.js i Vuetify des de les xarxes de distribució de contingut (CDN).
  • Definir cada "pàgina" de l'aplicació com a DIV separats.
  • Instanciar Vuetify amb un tema i colors adequats.
  • Configurar les rutes de navegació amb l'encaminador (router) del Vue.js perquè coincideixin amb la navegació real de l'aplicació.
  • Dissenyar les pàgines (no entrarem en detalls en aquest punt d'aquest tutorial ja que ens centrem en com crear la funcionalitat de l'esquelet).
  • Relaxar-nos i beure una tassa de te (o cervesa).

Sembla complicat, però realment és bastant fàcil, no patiu.

Seria bo si teniu una mica d'idea de com funciona el Vue.js. Si no, no us preocupeu perquè la creació de prototips és tan senzilla que amb un coneixement bàsic de la sintaxi de JavaScript en tindreu prou per entendre què estem fent en tot moment.

Planifiqueu (algunes coses) per endavant

El primer pas és saber on aneu. Si no sabeu on voleu anar qualsevol camí servirà i això no és el que volem.

En aquest cas com que la finalitat és una mica de demostració farem una senzilla aplicació amb quatre pàgines:

  • Un tauler principal amb diverses targetes.
  • Una llista de tasques.
  • Una vista detallada de la tasca.
  • Una pàgina de quant a/ajuda.

A més, l'esquelet de l'aplicació tindrà un menú desplegable. Hi haurà una barra del sistema amb notificacions i informació del perfil d'usuari. I finalment tindrem també un peu de pàgina senzill per l'aplicació.

I serà de color blau ... amb un toc de taronja!

Pot semblar una llista de requisits força simple, però estic segur que més d'un ha iniciat un projecte real amb menys especificacions que això ... m'equivoco?!? ... no crec! ... tristament! :-D

Preparar el fitxer HTML

Necessiteu 1 fitxer HTML, així que creem un fitxer HTML molt bàsic, no ens fa falta res més.

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
</body>

Carregar les biblioteques de Vue.js i Vuetify

Ara afegim el Vue.js, el vue-router, el Vuetify i els tipus de lletra que necessitarem:

<!DOCTYPE html>
<html>
<head>
    <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
    <link href="https://cdn.materialdesignicons.com/4.4.95/css/materialdesignicons.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
    <div id="app">
    </div>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-router.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.js"></script>
    <script>
    new Vue(
        {
            el: '#app'
        }
    );
    </script>
</body>

Ho creieu o no, si no esteu familiaritzat amb Vue.js, ara teniu l'entorn (a més d'una biblioteca de components de Material Design) carregat i llest per fer servir!

Definir cada "pàgina" de l'aplicació

Aquest és el punt clau del procés: com que estem "empaquetant" una aplicació de pàgina única (SPA) sencera en una única pàgina HTML (sense compilar ni res) necessitem una manera de definir el contingut de cada pàgina/component. Utilitzarem DIV ocults com a forma d'emmagatzemar les plantilles dels components.

Primer definim un estil personalitzat per amagar aquests DIV:

    <style>
    .vue-component-def { display: none; }
    </style>

Com sabem la quantitat de pàgines que necessitem, podem definir cadascuna com a DIV amb un identificador adequat. Més endavant tindrem una funció JavaScript que cercarà aquests DIV i en llegirà el seu "innerHTML" per tal de crear les plantilles Vue.js per als components.

    <div id="app">
    </div>
    ...
    <div id="home" class="vue-component-def">
        ...
    </div>

    <div id="tasks" class="vue-component-def">
        ...
    </div>

    <div id="tasks-detail" class="vue-component-def">
        ...
    </div>

    <div id="about" class="vue-component-def">
        ...
    </div>

Òbviament, necessiteu contingut per a aquestes pàgines. Trobareu tot el contingut d'exemple a la part final de l'article i també als repositoris de GitHub i CodePen. Ara ens centrem en com cal organitzar les "parts funcionals" del prototip perquè llavors el pugueu adaptar a les vostres necessitats.

Instanciar Vuetify

Una altra cosa que cal fer és crear una instancia de Vuetify i carregar el seu entorn de components visuals. Com que Vue.js està dissenyat per ser un "entorn progressiu" que es pot carregar al vol, és bastant fàcil:

    <script>
    new Vue(
        {
            el: '#app',
            vuetify: new Vuetify({
                theme: {
                    options: {
                        customProperties: true,
                    },
                    themes: {
                        light: {
                            primary: '#4dd0e1',
                            secondary: '#ffb300',
                            accent: '#ff5722',
                            error: '#f44336',
                            warning: '#ffc107',
                            info: '#03a9f4',
                            success: '#4caf50'
                        },
                    },
                },
                icons: {
                    iconfont: 'mdi',
                }
            }),
            ...
        }
    );
    </script>

Configurar les rutes de navegació (amb l'enrutador de Vue.js) perquè coincideixin amb la navegació real de l'aplicació.

L'última cosa, i bastant important per cert, que queda per fer és definir les rutes d'aplicació. Cal, no només, definir l'enrutador de l'aplicació sinó també assignar-los el corresponent component de Vue.js ... i crear aquest component. Si normalment treballeu amb Vue.js, sabeu es sol definir cada component com un fitxer independent. Però en aquest cas pretenem definir-ho tot en un sol fitxer HTML ... com? Bastant senzill, un component "de fitxer" no és més que un objecte JavaScript amb un conjunt de claus predefinides. Es pot trobar tota la informació a la documentació de Vue.js. Per tant, podem simplement crear un objecte JavaScript, a mà, que tingui les mateixes claus que tindria si el carreguéssim des d'un fitxer de component de Vue:

    <script>
    new Vue(
        {
            el: '#app',
            ...
            router: new VueRouter({
                routes: [
                    { path: '/home', component: { template: document.querySelector('#home').innerHTML } },
                    { path: '/tasks', component: { template: document.querySelector('#tasks').innerHTML } },
                    { path: '/tasks/detail', component: { template: document.querySelector('#tasks-detail').innerHTML } },
                    { path: '/about', component: { template: document.querySelector('#about').innerHTML } },
                    { path: '*', redirect: '/home' }
                ]
            }),
            ...
        }
    );
    </script>

Això ja funciona!

Dissenyar les pàgines

Com s'ha dit anteriorment, no entrarem en detalls sobre com s'ha de fer el disseny, ja que estem centrant l'article en proporcionar l'esquelet que es necessita per provar diferents dissenys. A més, cada aplicació és diferent i els vostres clients la voldran d'una manera específica. Tot i així, proporcionarem un exemple complet amb un disseny i components de mostra.

Com que estem utilitzant Vuetify com a base per al disseny i la maquetació, és bastant fàcil crear un disseny simplement seguint la seva documentació. No tindrem "gairebé cap" funcionalitat, així que tot seran components HTML de Vuetify.

Si mireu l'aplicació de mostra, veureu que l'hem organitzat de manera que la "v-app" conté una maquetació bàsica de pàgina amb "v-navigation-drawer", "v-app-bar" i un "v-footer". A continuació, dins tenim la "vista de la ruta" que canviarà el contingut de l'àrea principal en funció de les nostres rutes i components. Això té la limitació que només podem tenir "una sola" maquetació per l'aplicació ... tot i que si volguéssiu tenir a la "v-app" únicament la "vista de la ruta" i després repetir els menús de navegació i altres elements de la maquetació per cada pàgina podríeu tenir més d'una maquetació. Ja escriurem un altre article sobre com crear un prototip més complex amb diferents dissenys on es puguin reutilitzar components.

L'altra cosa que val la pena fixar-s'hi és que per canviar de ruta estem utilitzant l'estàndard "to=ruta". I finalment podeu veure que es pot tenir una mica de JavaScript incrustat dins de les plantilles HTML i avaluat quan es crea el component, mireu l'atribut image src:

<v-img class="white--text" height="200px" :src="'https://placeimg.com/400/200/nature?rand=' + Math.random()">

Aquesta manera de fer pot ser molt útil en cas que necessiteu alguna cosa una mica més avançada per al vostre prototip.

Hem utilitzat components Vuetify estàndard, una mica de Lorem Ipsum, un Lorem Ipsum d'imatges... res massa estrany, ja que la intenció de l'article és mostrar-vos la base perqué veieu les possibilitats que té aquesta manera de fer. Tot i així, ja podeu veure que en un moment podeu tenir un prototip decent d'una aplicació Material Design en funcionament i llest per ser enviat als vostres clients perquè s'ho mirin.

Podeu trobar el codi complet a la secció següent, a CodePen i a GitHub .

Salut!

La cosa completa

Això necessita una mica de treball per part vostra, perquè haureu d'entendre com funciona i com es fan servir els components de Vuetify. En cas de dubte mireu la seva documentació.

<!DOCTYPE html>
<html>
<head>
    <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
    <link href="https://cdn.materialdesignicons.com/4.4.95/css/materialdesignicons.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
    <style>
    .vue-component-def { display: none; }
    </style>
</head>
<body>

    <div id="app">
        <v-app>
            <v-navigation-drawer app fixed v-model="drawer">
                <v-list dense nav>

                    <v-list-item to="/home">
                        <v-list-item-action>
                            <v-icon>mdi-home</v-icon>
                        </v-list-item-action>
                        <v-list-item-content>
                            <v-list-item-title>Home</v-list-item-title>
                        </v-list-item-content>
                    </v-list-item>

                    <v-list-item to="/tasks">
                        <v-list-item-action>
                            <v-icon>mdi-calendar-check</v-icon>
                        </v-list-item-action>
                        <v-list-item-content>
                            <v-list-item-title>Tasks</v-list-item-title>
                        </v-list-item-content>
                    </v-list-item>

                    <v-list-item to="/about">
                        <v-list-item-action>
                            <v-icon>mdi-help-circle</v-icon>
                        </v-list-item-action>
                        <v-list-item-content>
                            <v-list-item-title>About</v-list-item-title>
                        </v-list-item-content>
                    </v-list-item>

                    <v-list-item>
                        <v-list-item-action>
                            <v-icon>mdi-logout</v-icon>
                        </v-list-item-action>
                        <v-list-item-content>
                            <v-list-item-title>Sign Out</v-list-item-title>
                        </v-list-item-content>
                    </v-list-item>

                </v-list>
            </v-navigation-drawer>

            <v-app-bar app fixed color="primary">
                <v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon>

                <v-toolbar-title>Vuetify Prototype</v-toolbar-title>

                <div class="flex-grow-1"></div>

                <v-btn icon>
                    <v-badge color="accent" overlap>
                        <template v-slot:badge>4</template>
                        <v-icon>mdi-bell</v-icon>
                    </v-badge>
                </v-btn>

                <v-btn icon>
                    <v-badge color="accent" overlap>
                        <template v-slot:badge>3</template>
                        <v-icon>mdi-email</v-icon>
                    </v-badge>
                </v-btn>

                <div class="d-flex">
                    <v-avatar>
                        <img :src="'https://stevensegallery.com/100/100/?rand=' + Math.random()" />
                    </v-avatar>
                    <div class="d-none d-sm-flex flex-row align-center mx-2">
                        <div class="flex-column">
                            <div class="body-1">Steven Seagal</div>
                            <div class="body-2 font-weight-light">Head of Security</div>
                        </div>
                    </div>
                </div>
            </v-app-bar>

            <v-content>
                <router-view></router-view>
            </v-content>

            <v-footer app inset color="primary" height="auto">
                <v-container fluid>
                    <v-row align="start" justify="center" no-gutters>
                        <v-col cols="12">
                            <div class="text-center">
                                <a class="black--text" href="https://ivanreyne.ninja">Made by Ivan Reyné, enjoy!</a>
                            </div>
                        </v-col>
                    </v-row>
                </v-container>
            </v-footer>
        </v-app>
    </div>

    <div id="home" class="vue-component-def">
        <v-container fluid>
            <v-row align="start" justify="center">
                <v-col cols="12">
                    <h1 class="text-center">Home Dashboard</h1>
                </v-col>
            </v-row>
            <v-row align="start" justify="start">
                <v-col md="3">

                    <v-card class="mx-auto">
                        <v-img class="white--text" height="200px" :src="'https://placeimg.com/400/200/nature?rand=' + Math.random()">
                            <v-card-title class="align-end fill-height">Lorem ipsum</v-card-title>
                        </v-img>

                        <v-card-text>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus.</v-card-text>

                        <v-card-actions>
                            <v-btn>Action</v-btn>
                            <div class="flex-grow-1"></div>
                            <v-btn icon>
                                <v-icon>mdi-heart-outline</v-icon>
                            </v-btn>
                        </v-card-actions>
                    </v-card>

                </v-col>
                <v-col md="6">

                    <v-card class="mx-auto">
                        <v-img class="white--text" height="200px" :src="'https://placeimg.com/400/200/nature?rand=' + Math.random()">
                            <v-card-title class="align-end fill-height">Lorem ipsum dolor sit amet</v-card-title>
                        </v-img>

                        <v-card-text>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus. Sed lacinia ut arcu eget dignissim. Phasellus eget dapibus mauris. Duis et interdum ex, eu pellentesque lectus. Phasellus gravida ligula in purus tincidunt vehicula nec sit amet orci. Donec et auctor sapien. Cras lobortis ante nisl, non faucibus sem luctus id. Quisque tristique ante eget pharetra posuere.</v-card-text>

                        <v-card-actions>
                            <v-btn color="primary">Action</v-btn>
                            <v-btn color="accent" outlined>Action</v-btn>
                            <div class="flex-grow-1"></div>
                            <v-btn icon>
                                <v-icon>mdi-email-outline</v-icon>
                            </v-btn>
                            <v-btn icon>
                                <v-icon>mdi-heart-outline</v-icon>
                            </v-btn>
                        </v-card-actions>
                    </v-card>

                </v-col>
                <v-col md="3">

                    <v-card class="mx-auto">
                        <v-card-title>
                            <p class="headline">Card List</p>
                        </v-card-title>

                        <v-list two-line>
                            <v-list-item>
                                <v-list-item-content>
                                    <v-list-item-title>Item 1</v-list-item-title>
                                    <v-list-item-subtitle>Item 1 description - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus.</v-list-item-subtitle>
                                </v-list-item-content>
                                <v-list-item-action>
                                    <v-btn icon>
                                        <v-icon color="accent">mdi-star</v-icon>
                                    </v-btn>
                                </v-list-item-action>
                            </v-list-item>
                            <v-list-item>
                                <v-list-item-content>
                                    <v-list-item-title>Item 2</v-list-item-title>
                                    <v-list-item-subtitle>Item 2 description - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus.</v-list-item-subtitle>
                                </v-list-item-content>
                                <v-list-item-action>
                                    <v-btn icon>
                                        <v-icon color="accent">mdi-star-half</v-icon>
                                    </v-btn>
                                </v-list-item-action>
                            </v-list-item>
                            <v-list-item>
                                <v-list-item-content>
                                    <v-list-item-title>Item 3</v-list-item-title>
                                    <v-list-item-subtitle>Item 3 description - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus.</v-list-item-subtitle>
                                </v-list-item-content>

                                <v-list-item-action>
                                    <v-btn icon>
                                        <v-icon color="grey lighten-1">mdi-star-outline</v-icon>
                                    </v-btn>
                                </v-list-item-action>
                            </v-list-item>
                        </v-list>

                        <v-card-actions>
                            <v-btn color="accent" outlined>Action</v-btn>
                            <div class="flex-grow-1"></div>
                            <v-btn icon>
                                <v-icon>mdi-email-outline</v-icon>
                            </v-btn>
                            <v-btn icon>
                                <v-icon>mdi-heart-outline</v-icon>
                            </v-btn>
                        </v-card-actions>
                    </v-card>

                </v-col>
            </v-row>
            <v-row align="start" justify="start">
                <v-col md="6">

                    <v-card class="mx-auto">
                        <v-img class="white--text" height="200px" :src="'https://placeimg.com/600/200/nature?rand=' + Math.random()">
                            <v-card-title class="align-end fill-height">Lorem ipsum dolor sit amet</v-card-title>
                        </v-img>

                        <v-card-text>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus.</v-card-text>

                        <v-card-actions>
                            <v-btn color="primary">Action</v-btn>
                            <v-btn>Action</v-btn>
                            <div class="flex-grow-1"></div>
                            <v-btn icon>
                                <v-icon>mdi-heart-outline</v-icon>
                            </v-btn>
                        </v-card-actions>
                    </v-card>

                </v-col>
            </v-row>
        </v-container>
    </div>

    <div id="tasks" class="vue-component-def">
        <v-container fluid>
            <v-row align="start" justify="center">
                <v-col cols="12">

                    <v-card class="mx-auto">
                        <v-img class="white--text" height="200px" :src="'https://placeimg.com/600/200/nature?rand=' + Math.random()">
                            <v-card-title class="align-end fill-height">Lorem ipsum dolor sit amet</v-card-title>
                        </v-img>

                        <v-card-text>
                            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus. Sed lacinia ut arcu eget dignissim. Phasellus eget dapibus mauris. Duis et interdum ex, eu pellentesque lectus. Phasellus gravida ligula in purus tincidunt vehicula nec sit amet orci. Donec et auctor sapien. Cras lobortis ante nisl, non faucibus sem luctus id. Quisque tristique ante eget pharetra posuere.
                        </v-card-text>

                        <v-list two-line>
                            <v-list-item to="/tasks/detail">
                                <v-list-item-content>
                                    <v-list-item-title>Item 1</v-list-item-title>
                                    <v-list-item-subtitle>Item 1 description - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus. Sed lacinia ut arcu eget dignissim.</v-list-item-subtitle>
                                </v-list-item-content>
                                <v-list-item-action>
                                    <v-btn icon>
                                        <v-icon color="grey lighten-1">mdi-star-outline</v-icon>
                                    </v-btn>
                                </v-list-item-action>
                            </v-list-item>
                            <v-list-item to="/tasks/detail">
                                <v-list-item-content>
                                    <v-list-item-title>Item 2</v-list-item-title>
                                    <v-list-item-subtitle>Item 2 description - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus. Sed lacinia ut arcu eget dignissim.</v-list-item-subtitle>
                                </v-list-item-content>
                                <v-list-item-action>
                                    <v-btn icon>
                                        <v-icon color="grey lighten-1">mdi-star-outline</v-icon>
                                    </v-btn>
                                </v-list-item-action>
                            </v-list-item>
                            <v-list-item to="/tasks/detail">
                                <v-list-item-content>
                                    <v-list-item-title>Item 3</v-list-item-title>
                                    <v-list-item-subtitle>Item 3 description - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus. Sed lacinia ut arcu eget dignissim.</v-list-item-subtitle>
                                </v-list-item-content>

                                <v-list-item-action>
                                    <v-btn icon>
                                        <v-icon color="grey lighten-1">mdi-star-outline</v-icon>
                                    </v-btn>
                                </v-list-item-action>
                            </v-list-item>
                        </v-list>

                        <v-card-actions>
                            <v-btn color="primary">Action</v-btn>
                            <v-btn outlined>Action</v-btn>
                            <div class="flex-grow-1"></div>
                            <v-btn icon>
                                <v-icon>mdi-heart-outline</v-icon>
                            </v-btn>
                        </v-card-actions>
                    </v-card>

                </v-col>
            </v-row>
        </v-container>
    </div>

    <div id="tasks-detail" class="vue-component-def">
        <v-container fluid>
            <v-row align="start" justify="center">
                <v-col cols="12">

                    <v-card class="mx-auto">
                        <v-img class="white--text" height="100px" :src="'https://placeimg.com/600/100/nature?rand=' + Math.random()">
                            <v-card-title class="align-end fill-height">Task detail</v-card-title>
                        </v-img>

                        <v-card-text>
                            <p>Task detail - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus. Sed lacinia ut arcu eget dignissim. Phasellus eget dapibus mauris. Duis et interdum ex, eu pellentesque lectus. Phasellus gravida ligula in purus tincidunt vehicula nec sit amet orci. Donec et auctor sapien. Cras lobortis ante nisl, non faucibus sem luctus id. Quisque tristique ante eget pharetra posuere.</p>
                            <p>Nunc in erat at nisl mattis sagittis. Sed viverra ipsum quis nisl venenatis, non tincidunt lectus viverra. Phasellus placerat molestie pellentesque. Fusce non enim eleifend, sagittis risus a, cursus ante. Donec sagittis lobortis magna sed imperdiet. Suspendisse ultrices tellus id ex vehicula, eu mattis nisl vulputate. Maecenas quis sapien nibh. Quisque nec eros nibh. Mauris accumsan finibus quam, eget viverra magna molestie et. Suspendisse accumsan porta mi. Morbi eu dui euismod turpis ultrices placerat vitae ac magna. Quisque elementum id quam vel viverra. Fusce sit amet nunc nec nunc dignissim semper. Proin imperdiet, felis vitae aliquam vulputate, tortor nisi feugiat eros, eget molestie mi mi nec dui. Sed vel risus sit amet mi pretium dictum id ac mauris. Morbi vitae quam eget nulla semper lacinia.</p>
                        </v-card-text>

                        <v-card-actions>
                            <v-btn color="primary">Action</v-btn>
                            <v-btn outlined>Action</v-btn>
                            <div class="flex-grow-1"></div>
                            <v-btn icon>
                                <v-icon>mdi-heart-outline</v-icon>
                            </v-btn>
                        </v-card-actions>
                    </v-card>

                </v-col>
            </v-row>
        </v-container>
    </div>

    <div id="about" class="vue-component-def">
        <v-container fluid>
            <v-row align="start" justify="center">
                <v-col cols="12">

                    <v-card class="mx-auto">
                        <v-img class="white--text" height="200px" :src="'https://placeimg.com/600/200/nature?rand=' + Math.random()">
                            <v-card-title class="align-end fill-height">Lorem ipsum dolor sit amet</v-card-title>
                        </v-img>

                        <v-card-text>
                            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus. Sed lacinia ut arcu eget dignissim. Phasellus eget dapibus mauris. Duis et interdum ex, eu pellentesque lectus. Phasellus gravida ligula in purus tincidunt vehicula nec sit amet orci. Donec et auctor sapien. Cras lobortis ante nisl, non faucibus sem luctus id. Quisque tristique ante eget pharetra posuere.</p>
                            <p>Nunc in erat at nisl mattis sagittis. Sed viverra ipsum quis nisl venenatis, non tincidunt lectus viverra. Phasellus placerat molestie pellentesque. Fusce non enim eleifend, sagittis risus a, cursus ante. Donec sagittis lobortis magna sed imperdiet. Suspendisse ultrices tellus id ex vehicula, eu mattis nisl vulputate. Maecenas quis sapien nibh. Quisque nec eros nibh. Mauris accumsan finibus quam, eget viverra magna molestie et. Suspendisse accumsan porta mi. Morbi eu dui euismod turpis ultrices placerat vitae ac magna. Quisque elementum id quam vel viverra. Fusce sit amet nunc nec nunc dignissim semper. Proin imperdiet, felis vitae aliquam vulputate, tortor nisi feugiat eros, eget molestie mi mi nec dui. Sed vel risus sit amet mi pretium dictum id ac mauris. Morbi vitae quam eget nulla semper lacinia.</p>
                        </v-card-text>

                        <v-card-actions>
                            <v-btn color="primary">Action</v-btn>
                            <v-btn outlined>Action</v-btn>
                            <div class="flex-grow-1"></div>
                            <v-btn icon>
                                <v-icon>mdi-heart-outline</v-icon>
                            </v-btn>
                        </v-card-actions>
                    </v-card>

                </v-col>
            </v-row>
        </v-container>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-router.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.js"></script>
    <script>
    new Vue(
        {
            el: '#app',
            vuetify: new Vuetify({
                theme: {
                    options: {
                        customProperties: true,
                    },
                    themes: {
                        light: {
                            primary: '#4dd0e1',
                            secondary: '#ffb300',
                            accent: '#ff5722',
                            error: '#f44336',
                            warning: '#ffc107',
                            info: '#03a9f4',
                            success: '#4caf50'
                        },
                    },
                },
                icons: {
                    iconfont: 'mdi',
                }
            }),
            router: new VueRouter({
                routes: [
                    { path: '/home', component: { template: document.querySelector('#home').innerHTML } },
                    { path: '/tasks', component: { template: document.querySelector('#tasks').innerHTML } },
                    { path: '/tasks/detail', component: { template: document.querySelector('#tasks-detail').innerHTML } },
                    { path: '/about', component: { template: document.querySelector('#about').innerHTML } },
                    { path: '*', redirect: '/home' }
                ]
            }),
            data: {
                drawer: null
            }
        }
    );
    </script>
</body>
</html>

Relaxeu-vos i beveu una tassa de te (o cervesa).

Ja era hora! :-)