Routing on Vue with SSR and Laravel - laravel

I am learning Vue and got stuck trying setup it as full front-end with Laravel, on my scenario I already have made an personal blog for test using Laravel with Blade engine and some components of Vue and seems work fine.
I am trying get on next level, removing Blade and letting Laravel as API backend and setup Vue as full front end with SSR, the basic setup works, I mean I can call Vue, render it using SSR with node or PHPv8, the problem I am having is on route systems, thinking as blade way I can't archive same result, on blade I use an default layout as master and import it for every post, page, blog, etc...
Example:
resources/views/layouts/master.blade
<!DOCTYPE html>
<html dir="ltr" lang="{{ app()->getLocale() }}">
<head>
#include('partials._head')
</head>
#if(View::hasSection('body')) {{-- Load Custom Body --}}
<body #section('body')>
#else
<body>
#endif
#yield('content')
#include ('partials._javascripts')
#section('scripts')
</body>
</html>
So I call a dynamic head per page / post, a dynamic content, an basic javascripts (bootstrap, vue, fontawesome, etc...) and custom 'scripts' per page / posts.
Using the librarie:
https://github.com/spatie/laravel-server-side-rendering
I can get SSR working with node or PHPv8, but the vue-route never call the desired page, my setup is:
resources/assets/js/app.js
import Vue from 'vue';
import App from './layouts/App';
import axios from 'axios';
import store from './store';
import router from './router';
import navbar from './components/navbar';
import posts from './components/posts';
import sidebar from './components/sidebar';
import footer from './components/footer';
import BlogIndex from './views/blog/BlogIndex';
export default new Vue({
store,
router,
navbar,
posts,
sidebar,
footer,
BlogIndex,
render: h => h(App),
});
resources/assets/js/entry-client.js
import app from './app';
app.$mount('#app');
resources/assets/js/entry-server.js
import app from './app';
import renderVueComponentToString from 'vue-server-renderer/basic';
app.$router.push(context.url);
renderVueComponentToString(app, (err, html) => {
if (err) {
throw new Error(err);
}
dispatch(html);
});
resources/assets/js/router.js
// router.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import Home from './components/Home';
import BlogIndex from './views/blog/BlogIndex';
Vue.use(VueRouter);
const routes = [
{ path: '/', name: 'home', component: Home },
{ path: '/blog', name: 'blog', component: BlogIndex },
];
export default new VueRouter({
mode: 'history',
routes,
});
resources/assets/js/store.js
import Vue from 'vue';
import uniq from 'lodash/uniq';
import Vuex, { Store } from 'vuex';
Vue.use(Vuex);
export default new Store({
state: {
},
getters: {
},
mutations: {
},
});
resources/assets/js/views/blog/BlogIndex.vue
<template>
<div class="container">
<navbar></navbar>
<posts></posts>
<sidebar></sidebar>
<footer></footer>
</div>
</template>
<script>
export default {
name: "BlogIndex",
components: {
}
}
</script>
<style scoped>
</style>
app/Http/Controllers/VueSSRController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Illuminate\Routing\Route;
class VueSSRController extends Controller
{
public function __invoke()
{
return view('layouts.vue');
}
}
resources/views/layouts/vue.blade.php
<!DOCTYPE html>
<html dir="ltr" lang="{{ app()->getLocale() }}">
<head>
#section('extrajavascripts'){{ asset('js/scripts.min.js') }}#endsection
#include('partials._head')
</head>
#if(View::hasSection('body')) {{-- Load Custom Body --}}
<body #section('body')>
#else
<body>
#endif
{{-- Begin of Vue SSR --}}
{!! ssr('resources/assets/js/server_bundle.min.js')
// Share the packages with the server script through context
//->context('packages', $packages)
// If ssr fails, we need a container to render the app client-side
->fallback('<div id="app"></div>')
->render() !!}
{{-- End of Vue SSR --}}
#include ('partials._javascripts')
#section('scripts')
#show
</body>
</html>
/resources/assets/js/layouts/App.vue
<template>
<div id ="app">
{{ message }}
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
message: 'SSR working.'
}
}
}
</script>
<style scoped>
</style>
So SSR works fine, the problem is the resources/assets/js/router.js is not loading the resources/assets/js/views/blog/BlogIndex.vue, the url/blog works, but the component rendered is always the /resources/assets/js/layouts/App.vue.
Someone would point what I am missing setup please?
Thanks in advice!!!

You should place <router-view></router-view> where you want the router to load. I think in your case its below {{message}} in App.vue

Related

Using Vue component inside blade file with vitejs and laravel

I'm a beginner in VueJS and I wonder how to use a vuejs component from my home.blade.php for example.
Here is my code (I use ViteJS and not Mix)
Card.vue
<template>
<h1>Hello world</h1>
<p>Id : {{ filmId }}</p>
</template>
<script>
export default {
props: ['filmId']
}
</script>
app.js
import './bootstrap';
import { createApp } from "vue";
import App from './App.vue';
createApp(App).mount('#app')
And my home.blade.php :
<div id="app">
<Card />
</div>
#vite('./resources/js/app.js')
You can't use vue components inside of blade files.
If you're using vue, you use vue for ALL of your views.
The only blade file would be your template which loads vue via vite.

My Vue root file/component is not showing up in Laravel

I'm following this simple Crud tutorial for Vue + Laravel and everything in Laravel part seems fine (from creating controllers & models) seems fine but I can't make vue show up in Laravel welcome blade. Here's my code:
app.js file
require('./bootstrap');
window.Vue = require('vue');
import App from './App.vue';
import VueAxios from 'vue-axios';
import VueRouter from 'vue-router';
import axios from 'axios';
import { routes } from './routes';
Vue.use(VueRouter);
Vue.use(VueAxios, axios);
const router = new VueRouter({
mode: 'history',
routes: routes
});
const app = new Vue({
el: '#app',
router: router,
render: h => h(App),
});
App.vue file:
<template>
<div class="container">
<h2>Vue app</h2>
<router-view> </router-view>
</div>
</template>
<script>
export default {}
</script>
and this is the welcome.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<style>
body {
font-family: 'Nunito', sans-serif;
}
</style>
</head>
<body class="antialiased">
<div id="app"> </div>
<script src="{{ mix('js/app.js') }}" type="text/javascript"></script>
<h2>The welcome blade</h2>
</body>
</html>
edit: answer added below, basically followed another tutorial
Looking at the code after composer require laravel/ui and php artisan ui vue I see small change that may affect the execution of code.
Originally the Laravel setup creates window.Vue = require('vue').default;, but your code has window.Vue = require('vue')
Firstly, try this:
<script src="{{ asset('js/app.js') }}" defer></script>
Secondly, you may need a router view element inside the div with id app. I can't say for sure since I don't know your routes. If the change above doesn't solve the problem, add router view like
<div id="app">
<router-view />
</div>
UPDATE
Another thing to try. Remove these lines
import VueAxios from 'vue-axios';
import axios from 'axios';
Vue.use(VueAxios, axios);
because you have axios on bootstrap.js file.
And make sure there is no error message on the console.
So it must have been something wrong with my installation process because I followed another tutorial and now it's working video

Failed to resolve component: router-link and router-view

router-view and router-link components cannot be resolved in my app
I don't know what the problem is
attached the app code
it's a home page app with something like a navbar to route to the vue components like states
App\resources\js\router.js
import States from './components/States.vue'
import Cities from './components/Cities.vue'
import Areas from './components/Areas.vue'
import Addresses from './components/Addresses.vue'
const router =createRouter({
history:createWebHistory,
routes :[
{
name:'states',
path:'/states',
Commponent: States
},
{
name:'cities',
path:'/cities',
Commponent: Cities
},
{
name:'areas',
path:'/areas',
Commponent: Areas
},
{
name:'addresses',
path:'/addresses',
Commponent: Addresses
}
]
})
export default new VueRouter({
router
});
App\resources\js\app.js
import App from './components/App.vue'
import router from './router'
import VueAxios from 'vue-axios'
import axios from 'axios'
const app = createApp(App)
app.use(VueAxios,axios,router)
app.mount('#MyApp')
App\resources\js\components\App.vue
<div class="container">
<h2>welcome to vue</h2>
<div class="navbar-nav">
<router-link to="/states">States</router-link>
<router-link to="/cities">Cities</router-link>
<router-link to="/areas">Areas</router-link>
<router-link to="/addresses">Addresses</router-link>
</div>.
<router-view />
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
C:\Users\Kareem\App\resources\js\components\States.vue
<h3>welcome to states component</h3>
</template>
<script>
export default {
}
</script>
<style>
</style>
Try like this for Vue3:
import { createApp } from "vue";
createApp(App)
.use(VueAxios, axios)
.use(router)
.mount('#MyApp')

Vue & Laravel problem with loading the components

I have a problem with my simple application using Vue 3, Vue router 4 & Laravel 8, my problem is that the main component is not loading I need to Ctrl + R to be able to see the main component and for the other component using Vue router nothing is loading properly this is my full code after downloading all the packages and run npm install , npm run dev & npm run watch :
app.js
require("./bootstrap");
import { createApp } from "vue";
import Main from "./components/App.vue";
import router from "./router";
const app = createApp({});
app.component("main-app", Main);
app.use(router);
app.mount("#app");
router.js
import { createRouter, createWebHistory, routerKey } from "vue-router";
import Home from "./components/pages/Home.vue";
import About from "./components/pages/About.vue";
import NotFound from "./components/pages/NotFound.vue";
const routes = [
{
path: "/Home",
name: "Home",
component: Home,
},
{
path: "/About",
name: "About",
component: About,
},
{
path: "/:catchAll(.*)",
component: NotFound,
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
export default router;
// export default createRouter({
// history: createWebHistory(),
// routes,
// });
welcome.blade.php I named app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
</head>
<body>
<div id="app">
<main-app />
</div>
<script src="{{ mix('js/app.js') }}"></script>
</body>
</html>
App.vue
<template>
<h1>Hello from the main app</h1>
<div id="nav">
<router-link to="/Home">Home</router-link> |
<router-link to="/About">About</router-link>
</div>
<router-veiw />
</template>
<script>
export default {};
</script>
About.vue, Home.vue & NotFound.vue
They have the same code so no need to write them again in each file I change x
<template>
<p>Hello from X component</p>
</template>
<script>
export default {};
</script>
and finally web.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Back\DashboardController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
// Call main path
Route::get('/', function () {
return view('app');
});

Vuejs register component issue in laravel

I registered App.vue as the main component in Vue.js but when I use it in Laravel blade I get this console error
[Vue warn]: Unknown custom element: <app> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
this is my code in app.js
require('./bootstrap');
import Vue from 'vue';
import VueRouter from 'vue-router';
import Vuex from 'vuex';
import routes from './routes'
import App from './App'
Vue.use(VueRouter);
Vue.use(Vuex);
const router = new VueRouter({
routes,
mode: history
});
Vue.config.devtools = true;
new Vue({
router,
render: h => h(App)
}).$mount('#app');
and my blade
<div id="app">
<App></App>
</div>
<script src="{{ asset('js/app.js') }}"></script>
this is the code I write in App.vue
<template>
<div class="app">
<h1 class="display-4">Hello World from App.vue</h1>
<router-view />
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
import App from './views/App'
import Hello from './views/Hello'
import Home from './views/Home'
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/hello',
name: 'hello',
component: Hello,
},
],
});
then in your blade file
<!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>Vue SPA</title>
</head>
<body>
<div id="app">
<app></app>
</div>
<script src="{{ mix('js/app.js') }}"></script>
</body>
</html>
also make sure to add this route
Route::get('/{any}', 'SpaController#index')->where('any', '.*');
You can register components like this:
Vue.component("Name", require("./components/Name.vue").default);
app.js:
require('./bootstrap');
import Vue from 'vue';
import VueRouter from 'vue-router';
import Vuex from 'vuex';
import routes from './routes'
import App from './App'
Vue.use(VueRouter);
Vue.use(Vuex);
const router = new VueRouter({
routes,
mode: history
});
Vue.config.devtools = true;
//import components
Vue.component(
'app',
require('./components/App.vue').default);
new Vue({
router,
render: h => h(App)
}).$mount('#app');

Resources