Indíce de contenidos
Introducción
En esta publicación, abordaremos todo lo que necesitas saber para establecer un proyecto utilizando Laravel 10, Vue 3 y Webpack. Exploraremos desde la fase inicial de la creación del proyecto en Composer hasta la implementación de Vue route y la resolución de posibles problemas que puedan surgir en el proceso.
Instalación
Para iniciar el proyecto, la primera tarea será generar el proyecto en Laravel, y para lograrlo, utilizaremos el siguiente comando.
php composer.phar create-project laravel/laravel laravel10-y-vue3
Este comando nos creara un proyecto con Laravel en la carpeta laravel10-y-vue3
una vez que la instalación haya terminado entraremos a la carpeta que se nos creó.
cd laravel10-y-vue3
Para el caso de Laravel 10, por defecto viene Vite, y tendremos que ejecutar el siguiente comando para remover esas dependencias que no vamos a ocupar
npm uninstall vite laravel-vite-plugin
e instalar las siguientes dependencias
npm install --save-dev laravel-mix
npm install --save-dev vue
npm install --save-dev vue-loader
Otra cosa que debemos tomar en cuenta es que necesitamos remover lo siguiente del package.json
ya que si no lo removemos nos generara errores al correr Laravel mix
"type": "module",
y modificar la sección de scripts del package.json
, para que nos quede de esta forma.
"scripts": {
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"production": "mix --production"
},
Configuración
Para comenzar a configurar el proyecto primero debemos de irnos a nuestro archivo webpack.mix.js y le añadimos las siguientes líneas
.
let mix = require('laravel-mix');
mix.css('resources/css/app.css', 'public/css')
.js('resources/js/app.js', 'public/js/')
.vue();
mix.browserSync('http://127.0.0.1:8000');
Esto nos generara dos archivos app.js y app.css en la carpeta publica, y browserSync nos ayudara a que cada vez que modifiquemos un archivo se recargue la página para que podamos visualizar los cambios que hicimos.
Ahora creamos el siguiente archivo en /resources/js/Components/App.vue y colocaremos lo siguiente.
<template>
<div>
Hola desde App.vue
</div>
</template>
<script>
export default {
data: () => ({
}),
};
</script>
Después nuestro archivo /resources/js/app.js nos debería quedar de la siguiente forma.
import './bootstrap';
import { createApp } from 'vue'
import App from './Components/App.vue'
const app = createApp(App);
app.mount('#app')
Y crearemos el archivo blade /resources/views/vue.blade.php , con lo siguiente.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="{{ asset(mix('css/app.css')) }}">
</head>
<body>
<div id="app">
</div>
<script src="{{asset(mix('js/app.js'))}}"></script>
</body>
</html>
Ahora añadiremos la siguiente ruta en el archivo /routes/web.php.
Route::get('/inicio', fn() => view('vue'));
Y en diferentes terminales correremos los siguientes comandos, uno es para iniciar el servidor php y el otro es para que webpack trate los archivos js y los sirva en la página local.
php artisan serve
npm run watch
Al ser la primera vez npm instalara browser-sync y veremos lo siguiente
Si volvemos a ejecutar npm run watch
tendríamos que ver los siguiente.
si todo salió bien hasta aquí y nos dirigimos a http://localhost:3000/inicio, nos debería mostrar lo siguiente:
Hasta aquí ya tenemos funcionando vue con laravel, pero solo la página de /inicio tendrá vue, si queremos que haya otra página con otro componente vue tendríamos que crear otro archivo app.js con otro nombre y crear otra vista blade para este nuevo app.js, pero esto no es muy optimo si queremos crear una página con múltiples vistas, por lo que ahora veremos como instalar Vue Router
Instalar Vue Router
Comenzaremos ejecutando el siguiente comando que nos instalará la última versión de Vue Router, Cuando se hizo este post la versión fue la 4.2
npm install --save-dev vue-router
Y añadiremos los siguientes componentes
<template>
<!-- /resources/js/Components/Inicio.vue -->
<div>
<h1>Hola desde inicio</h1>
</div>
</template>
<script>
export default {
}
</script>
<template>
<!-- /resources/js/Components/Nosotros.vue -->
<div>
<h1>Hola desde nosotros</h1>
</div>
</template>
<script>
export default {
}
</script>
Modificaremos el archivo App.vue
<template>
<div>
<header>
<nav>
<ul>
<li>
<router-link to="/inicio">Inicio</router-link>
</li>
<li>
<router-link to="/nosotros">Nosotros</router-link>
</li>
</ul>
</nav>
</header>
<main>
<router-view></router-view>
</main>
</div>
</template>
<script>
export default {
data: () => ({}),
};
</script>
Y crearemos el siguiente archivo /resources/js/router.js, con el siguiente contenido
import { createRouter, createWebHistory } from "vue-router";
import Inicio from "./components/Inicio.vue";
import Nosotros from "./components/Nosotros.vue";
const routes = [
{ path: "/inicio", component: Inicio },
{ path: "/nosotros", component: Nosotros },
];
const history = createWebHistory();
const router = createRouter({
history,
routes,
});
export default router;
En nuestro archivo /resources/js/app.js añadiremos las siguientes líneas, que nos ayudara a que Vue pueda usar Vue Router
import './bootstrap';
import { createApp } from 'vue';
import App from './Components/App.vue';
import router from "./router";
const app = createApp(App);
app.use(router);
app.mount('#app');
Y en el web.php quitamos la ruta que habíamos creado previamente y añadiremos la siguiente ruta, de tal forma que nuestro archivo web.php debe quedar así
<?php
use Illuminate\Support\Facades\Route;
Route::get('/{page}', function () {
return view('vue');
});
Con esto tanto si nos dirigimos a http://localhost:3000/inicio, y a http://localhost:3000/nosotros nos debe mostrar su componente correspondiente.
La primera vez que se accede a la página se descargaran todos los archivos necesarios para el funcionamiento de la página como si fuera una SPA
Ya una vez que se descargó la página si y solo si el usuario da clic en un <router-link>
o por medio de la API de Vue route this.$router.push().
Vue route se encargará del cambio de página y mostrar el componente correcto con respeto las rutas que añadimos en el routes.js
En caso contrario si el usuario da clic en un <a>
o cambiamos de página por medio de windows.location.href = "https://localhost:3000/nosotros"
la petición se volverá a mandar al servidor afectando en rendimiento de nuestra página
Cosas importantes
Error 404
Algo importante a considerar es que si dejamos la ruta como se mencionó anteriormente
<?php
use Illuminate\Support\Facades\Route;
/* ESTA RUTA */
Route::get('/{page}', function () {
return view('vue');
});
Si accedemos a la siguiente ruta http://localhost:3000/tienda, nos devolverá lo siguiente.
y si seguimos con cualquier otro valor en la ruta nos seguirá mostrando lo mismo, esto es incorrecto ya que nos debería de indicar que esta página no existe, para esto debemos de añadir un par de líneas extra a nuestro router.js
import { createRouter, createWebHistory } from "vue-router";
import Inicio from "./components/Inicio.vue";
import Nosotros from "./components/Nosotros.vue";
import NotFound from "./components/NotFound.vue";
const routes = [
{ path: "/inicio", component: Inicio },
{ path: "/nosotros", component: Nosotros },
{ path: "/:pathMatch(.*)*", component: NotFound },
];
const history = createWebHistory();
const router = createRouter({
history,
routes,
});
export default router;
Y crear el componente NotFound.vue
<template>
<!-- /resources/js/Components/NotFound.vue -->
<div>
<h1>No se encontro la pagina 404</h1>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
Ahora si accedemos a http://localhost:3000/tienda ya nos debería mostrar lo siguiente
Si bien esto es visual, ya que internamente el servidor nos sigue devolviendo el código 200. Para solucionar esto hay dos opciones que yo recomiendo
Crear las rutas en Laravel y mostrar la vista 404 de Laravel
Lo primero por hacer es remplazar la ruta que creamos por las siguientes rutas.
<?php
use Illuminate\Support\Facades\Route;
Route::get('/inicio', fn() => view('vue'));
Route::get('/nosotros', fn() => view('vue'));
De esta manera siempre y cuando una ruta no exista en Laravel nos mostrara la vista 404 que trae por defecto Laravel.
Personalizando la vista 404 de Laravel con nuestro componente de Vue
Para esto debemos de crear la vista /resources/views/errors/404.blade.php es importante que sea en esa ruta ya que desde ahí Laravel tomara la vista cuando una ruta no se encuentre, y le pondremos lo siguiente a la vista
@include('vue')
De esta manera ya no nos mostrará la vista por defecto de Laravel y nos mostrará nuestro componente de NotFound.vue y la pagina devolverá el estatus 404, de esta manera el impacto en el SEO será menor ya que los buscadores les estará llegando el estatus correcto
Rutas con más niveles
Si tu deseo es crear rutas con más de una diagonal, también es posible. Partiendo de que en el punto anterior se indica la mejor manera de añadir las rutas para no tener un impacto en el SEO por el estatus 404
Lo único que haría falta seria modificar nuestro archivo web.php y router.js
<?php
use Illuminate\Support\Facades\Route;
/* web.php */
Route::get('/inicio/usuarios', fn() => view('vue'));
Route::get('/nosotros', fn() => view('vue'));
/* router.js */
import { createRouter, createWebHistory } from "vue-router";
import Inicio from "./components/Inicio.vue";
import Nosotros from "./components/Nosotros.vue";
import NotFound from "./components/NotFound.vue";
const routes = [
{ path: "/inicio/usuarios", component: Inicio },
{ path: "/nosotros", component: Nosotros },
{ path: "/:pathMatch(.*)*", component: NotFound },
];
const history = createWebHistory();
const router = createRouter({
history,
routes,
});
export default router;
de este modo ya nos mostrara el componente de Inicio.vue
Rutas con parametros
Si queremos que nuestra ruta tengo la posibilidad de aceptar parámetros tenemos que cambiar los siguientes archivos
<?php
use Illuminate\Support\Facades\Route;
/* web.php */
Route::get('/inicio/{usuario}', fn() => view('vue'));
Route::get('/nosotros', fn() => view('vue'));
/* router.js */
import { createRouter, createWebHistory } from "vue-router";
import Inicio from "./components/Inicio.vue";
import Nosotros from "./components/Nosotros.vue";
import NotFound from "./components/NotFound.vue";
const routes = [
{ path: "/inicio/:usuario", component: Inicio },
{ path: "/nosotros", component: Nosotros },
{ path: "/:pathMatch(.*)*", component: NotFound },
];
const history = createWebHistory();
const router = createRouter({
history,
routes,
});
export default router;
<template>
<!-- /resources/js/Components/Inicio.vue -->
<div>
<h1>Hola desde el inicio del {{ usuario }}</h1>
</div>
</template>
<script>
export default {
computed: {
usuario(){
return this.$route.params.usuario;
}
}
}
</script>
Con estos cambios podemos ver que siempre que se cambie la url cambiara el texto del componente Inicio.vue
Pasar datos entre Laravel y Vue
Lamentablemente no hay una manera fácil de pasar datos. Por lo que siempre necesitaremos usar AJAX para traer datos de nuestro backend a Vue.
Para realizar esto tendremos que modificar los siguientes archivos
<?php
use Illuminate\Support\Facades\Route;
/* web.php */
Route::get('/datos/usuarios/{usuario}', function ($usuario) {
return response()->json([
'datos' => match ($usuario) {
'usuario' => [
'nombre' => 'José Usuario'
],
'admin' => [
'nombre' => 'Manuel Admin'
],
'editor' => [
'nombre' => 'Sonia Editor'
],
'ejecutivo' => [
'nombre' => 'Diego Ejecutivo'
],
default => [
'nombre' => 'Usuario no registrado'
],
}
], 200);
});
Route::get('/inicio/{usuario}', fn() => view('vue'));
Route::get('/nosotros', fn() => view('vue'));
<template>
<!-- /resources/js/Components/Inicio.vue -->
<div>
<h1>Hola desde el inicio del {{ usuario }}</h1>
<p>
El nombre del usuario es: <strong>{{ nombreUsuario }}</strong>
</p>
</div>
</template>
<script>
export default {
computed: {
usuario() {
return this.$route.params.usuario;
},
},
data: () => ({
nombreUsuario: "",
}),
created() {
//Se ejecutara al crear el componente
this.obtenerUsuario();
},
methods: {
obtenerUsuario() {
const _this = this;
//Traera los datos desde el backend
axios.get(`/datos/usuarios/${this.usuario}`).then(function (res) {
_this.nombreUsuario = res.data.datos.nombre;
});
},
},
watch: {
usuario(newValue, oldValue) {
/*
Ayudara a actualizar si es que se cambia por la API de Vue Router
o por el componente de <router-link/>
*/
if (newValue != oldValue) {
this.obtenerUsuario();
}
},
},
};
</script>
Ya con esto nos actualizara el nombre del usuario que añadimos en la url
Continuar usando vistas de Blade
Si tienen un proyecto que ya tenga vistas blade, y requieras seguir usándolas o quisieras integrar otras vistas que solo dependan de blade. también es posible, solo te en cuenta que la ruta no debe existir con un componente Vue, por ejemplo
Añadiremos la siguiente ruta
Route::get('panel',function(){
return view('panel');
});
y crearemos la vista panel.blade.php
{{-- /resources/views/panel.blade.php --}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<h1>Hola desde el panel sin VUE</h1>
</body>
</html>
</html>
Si vamos a la ruta http://localhost:3000/panel veremos que está funcionando sin problema
Lo bueno de esto es que se puede navegar entre las vistas que posean Vue y las que no, pero si hay que tener en cuenta lo siguiente
-
Si se va a navegar entre vistas que posean Vue, se debe hacer por medio del componente
<router-link/>
o la API de Vue Router, en caso contrario podríamos ver problemas de rendimiento en nuestra página -
Si vamos a navegar de una vista de que NO contiene Vue a una vista que SI contiene Vue, solo podremos hacerlo mediante la etiqueta
<a>
o mediantewindows.location.href
-
Si vamos a navegar de una vista que, SI contiene Vue a una vista que NO contiene Vue, debemos hacerlo mediante la etiqueta
<a>
o mediantewindows.location.href
ya que si lo hacemos con el<router-link/>
o con la API de Vue router, nos mandara al componente NotFound.vue
Conclusiones
Si bien en este artículo hemos explorado la óptima forma de establecer un proyecto utilizando Laravel 10, Vue 3 y Webpack, con todas las características disponibles para la creación de tu página web, es posible que la gestión de las rutas en Laravel y las rutas en Vue Router pueda resultar un tanto compleja y difícil de mantener. Si así lo deseas, puedes mantener tu proyecto en este enfoque. Sin embargo, es importante señalar que la comunidad ya había identificado este problema, lo que llevó al desarrollo de InertialJS. Esta herramienta ha sido creada para facilitar la conexión entre Laravel y Vue de una manera más sencilla.
Te animo a que explores nuestro artículo que aborda cómo concebir un proyecto utilizando Laravel 10, Vue 3, Webpack e InertialJs. En él encontrarás información detallada sobre cómo aprovechar esta combinación de tecnologías de manera efectiva.
Ivan
Publicado a las 09:04, el 02 APR 2024Hola y muchas gracias por el articulo. Me veo en la obligación (créeme que no lo hago por gusto) de crear un proyecto con Laravel10, webpack y Vue2 . ¿Crees que haciendo un uninstall de Vue e instalando la versión 2 (y siguiendo el tutorial claro) será suficiente?