I have a website written in Laravel and InertiaJS (VueJS).
It has more than 60 pages.
InertiaJS stores all pages and components in these three files:
/js/manifest.js
/js/vendor.js
/js/app.js
The problem is the size of these files (Specially app.js) are getting so huge! The app.js is about 5MB.
I removed useless plugins and libraries, Also i refactored my code and it's all clean.
When i was only using Laravel for web development, I could load each page javascript and css files in it's own page. So speed of the page was pretty good. But when i migrated to VueJS it loads app.js and styles.css at once! It also extracts all styles in one file which is not ideal.
Also i use CDN, gzip compression, SSR and they are not helping too much to have better performance.
I want extract all components and pages into different javascript/css files and load them in their own pages when needed.
How can i do that ?
Here's your app.js using code splitting. See if it'll reduce your load size:
import Vue from 'vue';
const App = () => import('./App.vue');
const Link = () => import('#inertiajs/inertia-vue/Link');
const plugin = () => import('#inertiajs/inertia-vue/plugin');
const BootstrapVue = () => import('bootstrap-vue/dist/bootstrap-vue.esm');
const IconsPlugin = () => import('bootstrap-vue/dist/bootstrap-vue-icons.esm');
const Ziggy = () => import('ziggy-js');
const InertiaProgress = () => import('#inertiajs/progress');
Vue.component('inertia-link', Link)
Vue.use(BootstrapVue);
Vue.use(IconsPlugin);
Vue.use(plugin)
InertiaProgress.init()
Vue.mixin({
methods: {
route: (name, params, absolute) => Ziggy().then(ziggy => ziggy.route(name, params, absolute)),
},
});
const app = document.getElementById('app')
if (app.dataset.page) {
new Vue({
render: h => h(App, {
props: {
initialPage: JSON.parse(app.dataset.page),
resolveComponent: name => import(`./Pages/${name}.vue`).then(m => m.default),
},
})
}).$mount(app)
} else {
const app = new Vue({
el: '#app'
});
}
See this: Inertia doc
Are you loading all the pages at once?
I think you can try loading components only if required. We can use lazy loading and reduce app.js. Ref: https://router.vuejs.org/guide/advanced/lazy-loading.html#with-webpack
render: h => h(App, {
props: {
initialPage: JSON.parse(app.dataset.page),
resolveComponent: name => require(`./Pages/${name}`).default,
},
})
}).$mount(app)
This is my app.js file.
require('./bootstrap');
import {App, Link, plugin} from '#inertiajs/inertia-vue'
import Vue from 'vue'
import {BootstrapVue, IconsPlugin} from 'bootstrap-vue'
import route from 'ziggy-js';
import {Ziggy} from 'ziggy-js';
import {InertiaProgress} from '#inertiajs/progress'
Vue.component('inertia-link', Link)
Vue.use(BootstrapVue);
Vue.use(IconsPlugin);
Vue.use(plugin)
InertiaProgress.init()
Vue.mixin({
methods: {
route: (name, params, absolute) => route(name, params, absolute, Ziggy),
},
});
const app = document.getElementById('app')
if (app.dataset.page) {
new Vue({
render: h => h(App, {
props: {
initialPage: JSON.parse(app.dataset.page),
resolveComponent: name => require(`./Pages/${name}`).default,
},
})
}).$mount(app)
} else {
const app = new Vue({
el: '#app'
});
}
Related
In the previous way of setting up inertia in a laravel app, I could tweak the resolve property in the `createInertiaApp function from:
{
...,
resolve: name => import("./Pages/${name}"),
...
}
To
{
...,
resolve: name => {
const page = require("./Pages/${name}").default
if(!page.layout) {
page.layout = DefaultLayoutFile
}
},
...
}
To allow me manually pass a default layout file to be used in pages.
But with Vite becoming the default asset bundler and according to the docs, I must use a resolvePageComponent function which takes in import.meta.glob as a second argument to instruct Vite which files to bundle.
Problem here is the import gets returned from this resolvePageComponent so I cannot access the default object like I normally will from a require function.
So I have not been able to attach a default layout file to imported pages.
Has anyone been able to find a workaround for this?
Assuming you imported your default layout file like this (remember to not use # in imports anymore as only relative paths work for static analysis reasons):
import DefaultLayoutFile from './Layouts/DefaultLayoutFile.vue'
You can use the following code to get default layout working with Inertia and Vite:
resolve: (name) => {
const page = resolvePageComponent(
`./Pages/${name}.vue`,
import.meta.glob("./Pages/**/*.vue")
);
page.then((module) => {
module.default.layout = module.default.layout || DefaultLayoutFile;
});
return page;
},
[UPDATE 2022-08-01]
Since this is still receiving views, I though it would be useful to show how to get the # working in imports in Vite.
Require path below your imports in vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '#vitejs/plugin-vue';
const path = require('path')
And then add resolve into your config below:
export default defineConfig({
resolve:{
alias:{
'#' : path.resolve(__dirname, './src')
},
},
})
Now # will point to your Laravel root and you can import components dynamically from anywhere:
For example import LayoutTop from '#/Layouts/LayoutTop.vue'
will now point to
/resources/js/Layouts/LayoutTop.vue
Remember that Vite needs the .vue extension when importing Vue SFC files.
The async await version of the accepted answer
resolve: async name => {
const page = await resolvePageComponent(`./Pages/${name}.vue`, import.meta.glob("./Pages/**/*.vue"));
page.default.layout ??= DefaultLayoutFile;
return page;
},
This worked from me using the vite with inertia preset
import { createApp, h } from 'vue'
import { createInertiaApp } from '#inertiajs/inertia-vue3'
import { resolvePageComponent } from 'vite-plugin-laravel/inertia'
import DefaultLayout from '#/views/layouts/default.vue'
import SimpleLayout from '#/views/layouts/simple.vue'
import UserLayout from '#/views/layouts/user.vue'
createInertiaApp({
resolve: (name) => {
const page = resolvePageComponent(
name,
import.meta.glob('../views/pages/**/*.vue'),
DefaultLayout
);
page.then((module) => {
if (name.startsWith('auth.')) module.layout = SimpleLayout;
if (name.startsWith('user.')) module.layout = [DefaultLayout, UserLayout];
});
return page
},
setup({ el, app, props, plugin }) {
createApp({ render: () => h(app, props) })
.use(plugin)
.mount(el)
},
})
I am working on Laravel Inertia project.
app.js on local is 4MB
When I load the same file on live server then it becomes 13-15MB.
due to which page load time increases to 10-15 seconds.
I push file after compressing it by npm run prod
Local Response time
Live Response time
Everything is working great on local and live except file size issue.
app.js
require('./bootstrap');
// Import modules...
import { createApp, h } from 'vue';
import mitt from 'mitt';
const emitter = mitt();
import { App as InertiaApp, plugin as InertiaPlugin } from '#inertiajs/inertia-vue3';
import { InertiaProgress } from '#inertiajs/progress';
const el = document.getElementById('app');
const app = createApp({
render: () =>
h(InertiaApp, {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: (name) =>
require(`./Pages/${name}`).default,
}),
})
.mixin({ methods: { route } })
.use(InertiaPlugin)
});
app.config.globalProperties.emitter = emitter;
app.mount(el);
InertiaProgress.init({ color: '#124664',showSpinner:false });
webpack.config.js
const path = require('path');
module.exports = {
resolve: {
alias: {
'#': path.resolve('resources/js'),
},
},
};
webpack.mix.js
const mix = require('laravel-mix');
mix.js('resources/js/app.js', 'public/js')
.vue()
.postCss('resources/css/app.css', 'public/css/tailwind_css', [
require('postcss-import'),
// require('tailwindcss'),
require('autoprefixer'),
])
.webpackConfig(require('./webpack.config'));
if (mix.inProduction()) {
mix.version();
}
app.js is added in main blade in head tag file like this
<script src="{{ mix('js/app.js') }}" defer></script>
The problem is probably because you haven't configured Code Splitting correctly, which is causing all your files to be bundled into a single huge file.
You need to replace the synchronous require statement in you app.js with a dynamic import:
render: () =>
h(InertiaApp, {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: (name) =>
require(`./Pages/${name}`).default, // This line is bundling all your files together
}),
})
render: () =>
h(InertiaApp, {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: (name) =>
import(`./Pages/${name}`), // Now, each page will have its respective file
}),
})
Laravel mix will probably handle the tooling need for Webpack to handle dynamic imports, but in case you're having troubles, take a look at the docs which explains what you'll need in details.
I am trying to integrate Highcharts with app.js in Laravel 8 with Vue and Inertia. I am trying to figure out how to pass HighchartsVue. I am trying to pass it to the use function for the createApp. However, I can't access it in the templates.
App.js
require("./bootstrap");
// Import modules...
import { createApp, h } from "vue";
import HighCharts from ""
import {
App as InertiaApp,
plugin as InertiaPlugin,
} from "#inertiajs/inertia-vue3";
const el = document.getElementById("app");
createApp({
render: () =>
h(InertiaApp, {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: (name) => require(`./Pages/${name}`).default,
}),
})
.mixin({ methods: { route } })
.use(InertiaPlugin)
.mount(el);
My template that tries to access the Vue Template. I haven't included the entire template here.
Vue Template
<div>
<highcharts :options="indexOptions"></highcharts>
</div>
For a global registration:
After you have installed "highcharts-vue" using:
npm install highcharts-vue
Register it globally as a plugin in your app.js with:
import HighchartsVue from 'highcharts-vue'
Next register it as a plugin in your vue object with:
Vue.use(HighchartsVue)
Please see the documentation here for more detailed instructions (and how to register it locally in the component).
After installing, your app.js would look something like this:
require("./bootstrap");
// Import modules...
import { createApp, h } from "vue";
import HighchartsVue from 'highcharts-vue'
import {
App as InertiaApp,
plugin as InertiaPlugin,
} from "#inertiajs/inertia-vue3";
Vue.use(HighchartsVue);
const el = document.getElementById("app");
createApp({
render: () =>
h(InertiaApp, {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: (name) => require(`./Pages/${name}`).default,
}),
})
.mixin({ methods: { route } })
.use(InertiaPlugin)
.mount(el);
I would like to use nwidart/laravel-modules and inertia together. The problem I'm having is that my Index.vue that is in my Users module isn't showing up and there is no errors.
When I have this line of code in my app.js
resolveComponent: (name) => require(`../../Modules/Users/Resources/assets/Pages/${name}`).default
then my Index.vue shows up, but when I try to loop through it so that I can make it dynamic nothing shows up and there is no errors
This is what I have in my app.js at the moment
require('./bootstrap');
require('moment');
import Vue from 'vue';
import { InertiaApp } from '#inertiajs/inertia-vue';
import { InertiaForm } from 'laravel-jetstream';
import PortalVue from 'portal-vue';
Vue.mixin({ methods: { route } });
Vue.use(InertiaApp);
Vue.use(InertiaForm);
Vue.use(PortalVue);
const app = document.getElementById('app');
const modulesJson = require("../../modules_statuses.json");
var modulesObject = Object.keys(modulesJson);
new Vue({
render: (h) =>
h(InertiaApp, {
props: {
initialPage: JSON.parse(app.dataset.page),
resolveComponent: (name) => {
// LOOP IS HERE
for(let i = 0; i < modulesObject.length; i++){
require('../../Modules/'+modulesObject[i]+'/Resources/assets/Pages/'+name).default
}
}
},
}),
}).$mount(app);
Write the path of a file in your module when you want to run it through inertia. Then put a condition in the app.js file that if it is sent through the module, this code will be executed, otherwise it will return to the desired pages in the folder pages.
Like the following code:
Route::get('test',function () {
return \Inertia\Inertia::render('modules/Faq/resources/js/Pages/Admin/Index');
});
createInertiaApp({
title: (title) => `${title} - ${appName}`,
resolve: (name) => name.toString().indexOf('modules') > -1 ? require(`../../${name}.vue`) : require(`./Pages/${name}.vue`),
});
Recently I did this tutorial.
In this app I can successfully run a vuejs app and axios.then.catch.finally or this.form.then.catch.finally.
But I added npm to my old project that was recently upgraded to laravel 5.7.
In my app I can't add finally function. If i do, it says:
Uncaught TypeError: axios.post(...).then(...).catch(...).finally is not a function
My app.js:
/**
* First we will load all of this project's JavaScript dependencies which
* includes Vue and other libraries. It is a great starting point when
* building robust, powerful web applications using Vue and Laravel.
*/
// require('./bootstrap');
import Vue from 'vue'
window.Vue = Vue
window.axios = require('axios');
import { Form, HasError, AlertError } from 'vform'
window.Form = Form;
Vue.component(HasError.name, HasError)
Vue.component(AlertError.name, AlertError)
Vue.component('pagination', require('laravel-vue-pagination'));
//select 2
import vSelect from 'vue-select'
Vue.component('v-select', vSelect)
//sweet alert 1
import swal1 from 'sweetalert';
window.swal1 = swal1;
import swal from 'sweetalert2'
window.swal = swal;
const toast = swal.mixin({
toast: true,
position: 'top-end',
showConfirmButton: false,
timer: 3000
});
window.toast = toast;
/**
* Next, we will create a fresh Vue ap
*
*
* plication instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
Vue.component('example-component', require('./components/ExampleComponent.vue'));
Vue.component('timetable-component', require('./components/TimetableComponent.vue'))
/* filter active and inactive */
Vue.filter('activeInactive', function (value) {
if(value==1){
return 'Active'
}else{
return 'Inactive'
}
})
const app = new Vue({
el: '#app',
data:{
},
methods:{
},
mounted(){
}
});
I think my app.js is exactly the same as the tutorial app.js for Form request.
Another thing is that in the tutorial he did not use axios import anywhere but he smoothly used it. But without import, I can't use axios.then.
How did he use it without importing axios?
How can I use finally with then.catch?
a componenet:
loadSite() {
axios
.get("/api/site/list")
.then(({ data }) => {
console.log(data);
this.siteList = data;
})
.catch(function(error) {
console.log(error);
}).finally(()=>{
});
},
As document, to make finally works, you need to add promise.prototype.finally
npm install axios promise.prototype.finally --save
and then
const axios = require('axios');
require('promise.prototype.finally').shim();
axios
.get('http://www.example.com/user')
.then((response) => {
console.log(response.data);
return response;
})
.finally(() => {
console.log('this will always be called');
});
I have the same issue, just solved it by includeing this file
<script src="https://polyfill.io/v3/polyfill.min.js?features=Promise.prototype.finally" defer></script>