Contenido
Bienvenido a la segunda publicación sobre el uso de Vue.js y Flask para el desarrollo web de pila completa. El tema principal de este artículo será sobre Vue Router, pero también cubriré la directiva v-model, así como los métodos Vue y las propiedades calculadas. Dicho esto, toma algo con cafeína y consume un poco de bondad Vue. El código de esta publicación está en mi GitHub .
Contenido de la serie
- Configuración y familiarización con VueJS
- Navegando Vue Router (estás aquí)
- Gestión de estado con Vuex
- API RESTful con Flask
- Integración AJAX con API REST
- Autenticación JWT
- Implementación en un servidor privado virtual
Familiarizarse con el enrutador Vue
Como la mayoría de los otros aspectos del marco Vue.js, usar Vue Router para navegar por las distintas páginas y los componentes posteriores es increíblemente fácil.
Aparte 1: instancias independientes de Vue y vue-router
Algunos de los temas presentados en esta publicación se describirán mejor con ejemplos de juguetes más pequeños, por lo que habrá ocasiones en las que salga a un lado para hacerlo. Aparte de esto, demostraré lo que se requiere para colocar una instancia y un enrutador de Vue independientes. Si bien Vue es absolutamente fenomenal para crear aplicaciones SPA completas, también hay un valor real en la capacidad de colocarlo en una página HTML normal.
<!-- index.html -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<div>
<h2>Cartoon characters</h2>
<div v-for="(character, i) in characters" v-bind:key="i">
<h4>{{ character.name }}</h4>
<p><img v-bind:src="character.imgSrc" v-bind:alt="character.name"/></p>
</div>
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
characters: [{
name: 'Scooby',
imgSrc: 'https://www.wbkidsgo.com/Portals/4/Images/Content/Characters/Scooby/characterArt-scooby-SD.png'
}, {
name: 'Shaggy',
imgSrc: 'https://upload.wikimedia.org/wikipedia/en/thumb/8/87/ShaggyRogers.png/150px-ShaggyRogers.png'
} ]
}
})
</script>
Esto muestra los personajes de dibujos animados Scooby y Shaggy. El ejemplo presenta la v-bind:
directiva para vincular dinámicamente datos de la characters
matriz a los atributos src
y alt
del img
elemento, lo que permite que los datos dirijan el contenido. Esto es similar a cómo se realiza la interpolación de texto {{}}
, excepto que v-bind
interpola los datos en atributos. Puede encontrar un ejemplo práctico de esto aquí .
En lugar de mostrar ambos personajes, cambiemos nuestro enfoque, permitiéndonos hacer clic en un enlace para que cada personaje muestre un componente específico «Scooby» o «Shaggy». Para lograr esto, extraeré la biblioteca vue-router y realizaré los siguientes cambios:
<!-- index.html -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<p>
<router-link to="/scooby">Scooby</router-link>
<router-link to="/shaggy">Shaggy</router-link>
</p>
<router-view></router-view>
</div>
<script>
const Scooby = {
template: `
<div>
<h4>Scooby</h4>
<p>
<img src="https://www.wbkidsgo.com/Portals/4/Images/Content/Characters/Scooby/characterArt-scooby-SD.png" alt="scooby"/>
</p>
</div>`
}
const Shaggy = {
template: `
<div class="character">
<h4>Shaggy</h4>
<p><img src="https://upload.wikimedia.org/wikipedia/en/thumb/8/87/ShaggyRogers.png/150px-ShaggyRogers.png" alt="shaggy"/></p>
</div>`
}
const router = new vue-router({
routes: [
{ path: '/scooby', component: Scooby },
{ path: '/shaggy', component: Shaggy }
]
})
const app = new Vue({ router: router }).$mount('#app')
</script>
Como probablemente pueda ver, esta implementación está impulsada por componentes codificados, lo que no es un cambio beneficioso desde el punto de vista de la reutilización. Sin embargo, muestra un uso fácil de seguir de vue-router. Además de obtener la biblioteca vue-router, el HTML contiene dos nuevos elementos, componentes en realidad, específicos de vue-router.
El primer componente vue-router es <router-link>
el que recibe una ruta de ruta a través del to
atributo, que en realidad se denomina «parámetro» en un componente Vue. El <router-link>
componente produce un elemento de hipervínculo que responde a eventos de clic y le dice a Vue que muestre el componente asociado con su to
parámetro, ya sea «/ scooby» o «/ shaggy».
A continuación <router-link>
se muestra otro componente nuevo <router-view>
, que es donde vue-router le dice a Vue que inyecte los componentes de la interfaz de usuario, Scooby y Shaggy. Los componentes de la plantilla personalizada de Scooby y Shaggy se definen en el script
elemento al final de esta página de ejemplo index.html.
A continuación, se crea una instancia de un routes
objeto vue-router con un objeto que define una matriz de rutas similar a la que vimos en la aplicación Survey del primer artículo. Aquí, las rutas de las rutas se asignan a los componentes Scooby y Shaggy. Lo último que debe hacer es crear una instancia de Vue, darle un objeto de enrutador como propiedad de su objeto de opciones y vincular la instancia a la aplicación div
.
Puede hacer clic en el enlace del enrutador Scooby o Shaggy para mostrarlos, como se muestra a continuación. El código de este ejemplo se puede encontrar aquí .
Uso de vue-router para mostrar una encuesta individual
De vuelta en la aplicación de encuestas, comencemos nuestra discusión echando un vistazo al archivo route / index.js.
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
Vue.use(Router)
export default new Router({
routes: [
{
path: "https://Pharos.sh.com/",
name: 'Home',
component: Home
}
]
})
La página de inicio muestra las encuestas de la aplicación cuando se solicita la URL raíz en localhost: 8080 porque está asignada al Home
componente a través de las rutas del enrutador.
También necesito conectarme a Vue a través de la Vue.use(Router)
función para una aplicación SPA modular como esta. Además, necesito incluir el enrutador en el objeto de opciones, que se alimenta a la instancia de Vue en main.js de manera similar al ejemplo del juguete Scooby / Shaggy.
Continuando con la aplicación Encuesta, agrego una nueva ruta al routes
objeto que mapeará cada encuesta y sus preguntas a un componente reutilizable basado en el archivo Survey.vue. En el archivo routes / index.js, importe un Survey
componente y luego agregue una ruta para mapear cada encuesta por su ID al Survey
componente.
// ... omitted for brevity
import Survey from '@/components/Survey'
// ... omitted for brevity
export default new Router({
routes: [
{
// ... omitted for brevity
}, {
path: '/surveys/:id',
name: 'Survey',
component: Survey
}
]
})
Tenga en cuenta la :id
parte de la nueva ruta /surveys/:id
. Esto se conoce como un segmento dinámico que puede considerar como una variable dentro de una ruta. En este caso, estoy diciendo que :id
se usará para identificar una encuesta específica para mostrar en el Survey
componente que se construirá a continuación.
En el directorio «src / components /», cree un archivo llamado Survey.vue, luego ábralo y agregue la plantilla estándar, el script y las secciones de estilo junto con el código que se muestra a continuación:
<template>
<div>
<h2>I'm a Survey Component</h2>
</div>
</template>
<script>
export default {
data() {
return {
survey: {}
}
},
beforeMount() {
console.log('Survey.beforeMount -> :id === ', this.$route.params.id)
}
}
</script>
<style>
</style>
Guardando todos los archivos e inicio el servidor de desarrollo con npm run dev
, luego ingrese la siguiente URL en el navegador: localhost: 8080 / # / survey / 23. En la consola de las herramientas de desarrollo de mi navegador, veo la imagen a continuación.
Entonces, ¿qué acaba de pasar?
En la template
sección, agregué un código sin sentido para dejar en claro que el enrutador está sirviendo el componente de encuesta. En la script
sección, inicialicé un objeto de encuesta que eventualmente contendrá datos de encuesta. En el beforeMount
gancho del ciclo de vida está sucediendo algo muy bueno. En esta función estoy accediendo a la ruta de la ventana actual y al :id
parámetro posterior definido en el route
módulo.
Esta última parte es posible porque el Survey
objeto Vue del componente hace referencia a la instancia del enrutador vue que da acceso a la ruta que me permite acceder con ella this.$route.params.id
. Una ruta puede tener múltiples segmentos dinámicos y todos son accesibles en sus componentes correspondientes a través del params
miembro del this.$route
objeto.
A continuación, definiré una función AJAX simulada en api / index.js, a la que llamo desde el gancho Survey
del componente beforeMount
para obtener una encuesta :id
. En api / index.js agregue la siguiente función:
const surveys = [{
id: 1,
name: 'Dogs',
// ... omitted for brevity
}, {
id: 2,
name: 'Cars',
// ... omitted for brevity
}]
// ... omitted for brevity
export function fetchSurvey (surveyId) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const survey = surveys.find(survey => survey.id === surveyId)
if (survey) {
resolve(survey)
} else {
reject(Error('Survey does not exist'))
}
}, 300)
})
}
Ahora, de vuelta en el Survey
componente, necesito importarlo fetchSurvey
y usarlo beforeMount
para recuperar la encuesta solicitada. Una vez más, con fines visuales, mostraré el nombre de la encuesta en la plantilla como un encabezado de Bulma Hero.
<template>
<div>
<section class="hero is-primary">
<div class="hero-body">
<div class="container has-text-centered">
<h2 class="title">{{ survey.name }}</h2>
</div>
</div>
</section>
</div>
</template>
<script>
import { fetchSurvey } from '@/api'
export default {
data() {
return {
survey: {}
}
},
beforeMount() {
fetchSurvey(parseInt(this.$route.params.id))
.then((response) => {
this.survey = response
})
}
}
</script>
<style>
</style>
Actualizando la URL del navegador a localhost: 8080 / survey / 2, veo lo que se muestra a continuación:
Bastante bien, ¿verdad?
A continuación, me gustaría hacer algo un poco más útil con mi Survey
componente e incluir las preguntas y opciones.
<template>
<div>
<!-- omitted survey name header for brevity -->
<section class="section">
<div class="container">
<div class="columns">
<div class="column is-10 is-offset-1">
<div v-for="question in survey.questions" v-bind:key="question.id">
<div class="column is-offset-3 is-6">
<h4 class="title has-text-centered">{{ question.text }}</h4>
</div>
<div class="column is-offset-4 is-4">
<div class="control">
<div v-for="choice in question.choices" v-bind:key="choice.id">
<label class="radio">
<input type="radio" v-model="question.choice" :value="choice.id">
{{ choice.text }}
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</template>
Nuevamente, al guardar y actualizar el navegador con la URL localhost: 8080 / # / survey / 2 ahora se muestra una lista de preguntas y las opciones disponibles para la encuesta de autos.
Te puede interesar:Funciones de flecha en JavaScriptPermítanme intentar analizar algunas de las nuevas funciones de Vue que se están utilizando. Ya estamos familiarizados con el uso de la v-for
directiva para impulsar la generación de las preguntas y opciones de la encuesta, por lo que es de esperar que pueda realizar un seguimiento de cómo se muestran. Sin embargo, si se concentra en cómo se generan los botones de opción para las opciones de una pregunta, notará que estoy haciendo dos cosas nuevas o ligeramente diferentes.
<div v-for="choice in question.choices" v-bind:key="choice.id">
<label class="radio">
<input type="radio" v-model="question.choice" :value="choice.id">
{{ choice.text }}
</label>
</div>
Para la entrada de radio he utilizado la v-model
directiva y la he proporcionado con un valor de question.choice
. Lo que hace esto es que crea un nuevo miembro en el question
objeto llamado elección y lo registra con la entrada de radio, lo que permite que los datos fluyan desde la entrada de radio real a la question.choice
propiedad del objeto. También estoy usando una sintaxis abreviada de en :value
lugar de v-bind:value
vincular el valor de esta entrada de radio al valor de las opciones de pregunta que se repiten a través de v-for
.
Apartado 2: uso de la directiva del modelo v
Me doy cuenta de que el v-model
concepto probablemente sea un poco confuso, así que permítanme hacerme a un lado y dar otro ejemplo simple para demostrar el punto. Considere el ejemplo trivial a continuación. Nuevamente, puede ver un ejemplo funcional de este código aquí .
<!-- index.html -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<div>
<label for="name">What is your name</label>
<input id="name" type="text" v-model="textInput" />
<span>Hello {{ textInput }}</span>
</div>
<h4>Which do you like better?</h4>
<div v-for="choice in radioChoices" :key="choice">
<label>{{ choice }}</label>
<input name="fruit" type="radio" v-model="favoriteFruit" :value="choice"/>
</div>
<h4>So you like {{ favoriteFruit }}</h4>
</div>
<script>
new Vue({
el: '#app',
data: {
textInput: '',
radioChoices: ['apples', 'oranges'],
favoriteFruit: ''
}
})
</script>
La primera entrada es una entrada de texto que solicita el nombre del usuario. Esta entrada de texto tiene un v-model
registro con la propiedad de datos textInput
adjunta, lo que mantiene la entrada de texto sincronizada con la textInput
propiedad de datos de la Vue
instancia. Tómese un segundo para escribir su nombre en la entrada de texto y observe cómo se actualiza en la <span>Hello {{ textInput }}</span>
salida del HTML.
Increíble verdad?
La segunda entrada es una entrada de radio llamada «fruta» que muestra las frutas «manzanas» y «naranjas» y le pide al usuario que seleccione su favorito. La entrada de radio se registra en la favoriteFruit
propiedad de datos de la Vue
instancia a través de v-model
, que asocia el valor asociado con cada entrada de radio a través de la :value="choice"
sintaxis de enlace de atributos para mantener la favoriteFruit
sincronización con la entrada de radio seleccionada. Nuevamente, puede observar el valor de favoriteFruit
actualización en la <h4>So you like {{ favoriteFruit }}</h4>
salida del elemento.
A continuación muestro el resultado de algunos ejemplos. Te animo a que juegues con este ejemplo hasta que la noción de v-model
sea clara.
Completar la experiencia de realización de encuestas
Ok, volvamos a la aplicación de encuestas. Piense en el caso en el que una encuesta tiene muchas más preguntas debajo de la altura de pantalla predeterminada. En general, queremos evitar que las personas tengan que desplazarse hacia abajo para ver su contenido más importante. Una mejor opción sería paginar las preguntas que muestran una pregunta y sus respuestas, a la vez.
Te puede interesar:Envío de solicitudes AJAX en Vue.jsEl Survey
componente actualizado logra esto a continuación.
<template>
<div>
<!-- omitted for brevity -->
<section class="section">
<div class="container">
<div class="columns">
<div class="column is-10 is-offset-1">
<div
v-for="(question, idx) in survey.questions" <!-- modified v-for -->
v-bind:key="question.id"
v-show="currentQuestion === idx"> <!-- new v-show directive -->
<div class="column is-offset-3 is-6">
<!-- <h4 class="title">{{ idx }}) {{ question.text }}</h4> -->
<h4 class="title has-text-centered">{{ question.text }}</h4>
</div>
<div class="column is-offset-4 is-4">
<div class="control">
<div v-for="choice in question.choices" v-bind:key="choice.id">
<label class="radio">
<input type="radio" v-model="question.choice" :value="choice.id">
{{ choice.text }}
</label>
</div>
</div>
</div>
</div>
<!-- new pagination buttons -->
<div class="column is-offset-one-quarter is-half">
<nav class="pagination is-centered" role="navigation" aria-label="pagination">
<a class="pagination-previous" @click.stop="goToPreviousQuestion"><i class="fa fa-chevron-left" aria-hidden="true"></i> Back</a>
<a class="pagination-next" @click.stop="goToNextQuestion">Next <i class="fa fa-chevron-right" aria-hidden="true"></i></a>
</nav>
</div>
<!-- new submit button -->
<div class="column has-text-centered">
<a v-if="surveyComplete" class="button is-focused is-primary is-large"
@click.stop="handleSubmit">
Submit
</a>
</div>
</div>
</div>
</div>
</section>
</div>
</template>
<script>
import { fetchSurvey, saveSurveyResponse } from '@/api' // new AJAX func
export default {
data() {
return {
survey: {},
currentQuestion: 0 // new data prop
}
},
beforeMount() {
// omitted for brevity
},
methods: { // new Vue obj member
goToNextQuestion() {
if (this.currentQuestion === this.survey.questions.length - 1) {
this.currentQuestion = 0
} else {
this.currentQuestion++
}
},
goToPreviousQuestion() {
if (this.currentQuestion === 0) {
this.currentQuestion = this.survey.questions.lenth - 1
} else {
this.currentQuestion--
}
},
handleSubmit() {
saveSurveyResponse(this.survey)
.then(() => this.$router.push("https://Pharos.sh.com/"))
}
},
computed: { // new Vue obj member
surveyComplete() {
if (this.survey.questions) {
const numQuestions = this.survey.questions.length
const numCompleted = this.survey.questions.filter(q => q.choice).length
return numQuestions === numCompleted
}
return false
}
}
}
</script>
Estos cambios funcionan en conjunto para completar la experiencia de realización de encuestas. Como se generan los nodos de preguntas v-for="(question, idx) in survey.questions"
, estoy usando la v-show="currentQuestion === idx"
directiva para probar si el valor de la propiedad de datos currentQuestion
coincide con el valor de idx
. Esto hace que la pregunta sea div
visible solo si currentQuestion
es igual al idx
valor de esa pregunta . Dado que el valor de currectQuestion
se inicializa a cero, la pregunta cero se mostrará de forma predeterminada.
Debajo de las preguntas y respuestas, los botones de paginación permiten al usuario paginar las preguntas. El elemento de botón «siguiente» tiene @click="goToNextQuestion"
dentro de él, que es un controlador de eventos de clic de Vue que responde llamando a la goToNextQuestion
función dentro de la nueva methods
propiedad del objeto Vue. La methods
sección de un objeto componente de Vue es donde se pueden definir funciones para hacer una serie de cosas, la mayoría de las veces para cambiar el estado del componente. Aquí goToNextQuestion
aumenta currentQuestion
en uno, avanzando la pregunta que se muestra, o la restablece a la primera pregunta. El botón de retroceso y su goToPreviousQuestion
método asociado hacen exactamente lo contrario.
El último cambio es la funcionalidad para enviar la respuesta de la encuesta. El botón se usa v-show
nuevamente para determinar si el botón debe mostrarse en función del valor de una propiedad calculada llamada surveyCompleted
. Las propiedades computadas son otro rasgo asombroso de Vue. Son propiedades que generalmente controlan cómo se muestran los componentes de la interfaz de usuario que son útiles cuando la lógica es un poco más compleja que verificar un solo valor de una propiedad de datos. De esta manera, el código de la plantilla está limpio y puede enfocarse en la presentación mientras la lógica permanece en el código JavaScript.
Un detector de eventos de clic,, @click="handleSubmit"
se registra en el botón de anclaje de envío, que llama al handleSubmit
método. Este método llama a la función simulada AJAX saveSurveyResponse
, que devuelve una promesa y pasa el control a la «cadena entonces». La «cadena entonces» tiene una devolución de llamada, .then(() -> this.$router.push("https://Pharos.sh.com/"))
que llama al objeto enrutador del componente y enruta mediante programación la aplicación de regreso a la ruta raíz que muestra el Home
componente.
En api / index.js agregue la saveSurveyResponse
función al final del archivo. Esta función recibe un objeto de respuesta a la encuesta y simplemente registra en la consola «guardando la respuesta de la encuesta …» hasta que conecte el front-end a la API REST en el futuro.
export function saveSurveyResponse (surveyResponse) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("saving survey response...")
})
resolve()
}, 300)
})
}
Guardando todos los archivos y actualizando el navegador con la URL localhost: 8080: / # / survey / 2 Veo lo que hay a continuación. Recomiendo hacer clic en la aplicación y asegurarse de que pueda rastrear lógicamente el flujo de control a través del Survey
componente.
Aparte 3 – Enrutamiento programático
Al igual que antes, quiero demostrar algunas cosas que acabo de discutir con una variación de uno de los ejemplos de juguetes anteriores. A continuación, he modificado el ejemplo de navegación que muestra Scooby o Shaggy para que ya no utilicen el <router-link>
componente.
<!-- index.html -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<p>
<a @click="toScooby">Scooby</a>
<a @click="toShaggy">Shaggy</a>
</p>
<router-view></router-view>
</div>
<script>
const Scooby = {
template: `
<div>
<h4>Scooby</h4>
<p>
<img src="https://www.wbkidsgo.com/Portals/4/Images/Content/Characters/Scooby/characterArt-scooby-SD.png" alt="scooby"/>
</p>
</div>`
}
const Shaggy = {
template: `
<div class="character">
<h4>Shaggy</h4>
<p>
<img src="https://upload.wikimedia.org/wikipedia/en/thumb/8/87/ShaggyRogers.png/150px-ShaggyRogers.png" alt="shaggy"/>
</p>
</div>`
}
const router = new vue-router({
routes: [
{ path: '/characters/scooby', component: Scooby },
{ path: '/characters/shaggy', component: Shaggy }
]
})
const app = new Vue({
router: router,
methods: {
toScooby() { this.$router.push('/characters/scooby') },
toShaggy() { this.$router.push('/characters/shaggy') }
}
}).$mount('#app')
</script>
El ejemplo se comporta exactamente de la misma manera que antes, pero ahora el enrutamiento se realiza mediante una combinación de detectores de eventos de clic, métodos Vue y llamadas manuales this.$router.push('/path')
. Esto es realmente lo que <router-link>
hace detrás de escena usando el to="/path"
valor. Te animo a jugar con este ejemplo en vivo aquí .
Agregar enlaces de enrutador al componente de inicio
Lo último que se puede hacer con el Survey
componente es proporcionar la capacidad de navegar a una encuesta desde el Home
componente. Como se mostró anteriormente con el ejemplo de Scooby y Shaggy, vue-router hace que esto sea increíblemente fácil con <router-link>
.
De vuelta en el Home
componente, realice la siguiente modificación:
<div class="card" v-for="survey in surveys" v-bind:key="survey.id">
<div class="card-content">
<p class="title">{{ survey.name}}</p>
<p class="subtitle">{{survey.created_at.toDateString()}}</p>
</div>
<div class="card-foooter">
<router-link :to="`surveys/${survey.id}`" class="card-footer-item">Take Survey</router-link>
</div>
</div>
<router-link>
Agregué un componente dentro de un pie de página de tarjeta bulma y construí dinámicamente la ruta a cada encuesta. Esto es diferente de las rutas de cadena literales que proporcioné en mi ejemplo anterior. Para producir dinámicamente las rutas usando las cadenas de plantilla de JavaScript y las ID de encuesta que se iteran, prefijo el to
parámetro con dos puntos («:»), que es la abreviatura de la v-bind
directiva.
Guardo todos los archivos y busco la ruta de la URL raíz, localhost: 8080, en mi navegador para asegurarme de que funciona. Debería poder hacer clic en el enlace «Realizar encuesta» de cada encuesta y se mostrará la Survey
interfaz de usuario del componente.
Para completar la experiencia, agrego una barra de navegación simple con una pestaña «Inicio» usando <router-link>
y un to
parámetro que apunta a la ruta raíz de la aplicación dentro de un nuevo archivo de componente llamado Header.vue en el directorio del componente.
<template>
<nav class="navbar is-light" role="navigation" aria-label="main navigation">
<div class="navbar-menu">
<div class="navbar-start">
<router-link to="/" class="navbar-item">
Home
</router-link>
</div>
</div>
</nav>
</template>
<script>
</script>
<style>
</style>
Para asegurarme de que esté incluido en todas las páginas de la aplicación, lo coloco en el componente App.vue. Para hacer esto, primero importo el Header
componente como AppHeader
en la script
sección y luego lo registro agregando una propiedad llamada components
en el objeto Vue del componente de la aplicación y lo configuro igual a un objeto que contiene el AppHeader
componente.
Luego agrego el componente en la plantilla colocándolo <app-header>
justo encima del <router-view>
componente. Al nombrar componentes, es común usar el caso Pascal concatenando las palabras que lo describen juntas, donde la primera letra de cada palabra está en mayúscula. Luego lo incluyo en la plantilla en minúsculas con guiones entre cada palabra que comienza con una letra en mayúscula.
Al guardar los archivos y actualizar el navegador, ahora veo el Header
componente que contiene la barra de navegación en cada página de la aplicación.
Conclusión
Si todavía está leyendo esto, ha consumido bastante bondad de Vue. Ciertamente cubrimos mucho material en esta publicación y si es nuevo en Vue o en los SPA de componentes de un solo archivo, probablemente valga la pena una segunda lectura y un viaje a los excelentes documentos de Vue para una inmersión más profunda.
Como siempre, gracias por leer y no dude en comentar o criticar a continuación..