I am looking for help in understanding what are the best practices people are using for a VueJs app that is not an SPA. In my project I have multiple pages and currently I'm treating every page as an SPA. But I'm noticing that my app.js size is increases after adding a new page. This is what I have so far
require('./bootstrap');
var Vue = require('vue');
import GlobalMixins from './mixins/GlobalMixins'
import VueFlashMessage from 'vue-flash-message'
import PortalVue from 'portal-vue'
import Treeselect from '#riophae/vue-treeselect'
Vue.prototype.$eventHub = new Vue(); //global event bus
Vue.mixin(GlobalMixins);
Vue.use(PortalVue);
Vue.use(VueFlashMessage,{
messageOptions: {
timeout: 7000,
important: false,
autoEmit: true,
pauseOnInteract: true,
createShortcuts: false,
}
});
// shared components
Vue.component('navigation', require('./components/navigation.vue').default);
Vue.component('loader',require('./components/shared/custom_loader.vue').default);
Vue.component('treeselect',Treeselect);
//parent components only for each page
Vue.component('goals', require('./components/goals/index.vue').default);
Vue.component('dashboard', require('./components/dashboard/index.vue').default);
Vue.component('twitter', require('./components/twitter/index.vue').default);
Vue.component('trends', require('./components/trends/index.vue').default);
Vue.component('settings', require('./components/settings/index.vue').default);
..........
..........
..........
require('./filters');
window.onload = function () {
new Vue ({
el: '#app'
});
}
I'm compiling my assets using laravel-mix and even after running npm run prod app.js size is around 2MB. How can I optimize this?
PS: gzip compression is already enabled on my servers.
Related
Im trying to load vue-native-websockets in quasar but Im kind of lost.
I tried to run this as a boot file in quasar:
import VueNativeSock from 'vue-native-websocket'
export default async ({ app, router, store, Vue }) => {
Vue.use(VueNativeSock, 'ws://127.0.0.1:5679', {
store: store,
//format: 'json',
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 3000
})
}
and then:
this.$socket.send("my value");
in a function in my component but I guess I missunderstand the concept. Im pretty new to quasar so a working example would be great indeed ...
I can run this by just creating a connection locally in the component I want to use by putting this in the create method:
this.connection = new WebSocket("ws://127.0.0.1:5679/");
and then using that in a method like:
this.connection.send("myValue")
but the goal is to be able to use the connection in whatever component I like without creating it locally in the create function in that component ...
How can I achieve this?
UPDATE
when using:
https://www.npmjs.com/package/vue-native-websocket-vue3
I get this errormessage after implementing it in main.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from './routes'
Vue.use(VueRouter)
import VueNativeSock from "vue-native-websocket-vue3" ;
Vue.use( VueNativeSock , 'ws://127.0.0.1:5679' );
Errormessage:
property '$socket' of undefined
at Object.install (vueNativeWebsocket.common.js?9cca:3701)
at Function.Vue.use (vue.runtime.esm.js?5593:5107)
at eval (index.js?a18c:10)
at Module../src/router/index.js (app.js:1147) 1: https://quasar.dev/
I would like to integrate intertiaJS into my Quasar app so that I can communicate with my Laravel backend. My problem now is that the general stuff is taken over by the Quasar CLI, which is good in principle, but in this case it takes away my entry point as described at https://inertiajs.com/client-side-setup:
import { createApp, h } from 'vue'
import { App, plugin } from '#inertiajs/inertia-vue3'
const el = document.getElementById('app')
createApp({
render: () => h(App, {
initialPage: JSON.parse(el.dataset.page),
resolveComponent: name => require(`./Pages/${name}`).default,
})
}).use(plugin).mount(el)
My thought is that I could use a boot file like the offered in Quasar (https://quasar.dev/quasar-cli/boot-files), but I have to admit that I don't have the right approach.
When I look at the app.js that is automatically generated, I see that nothing special happens in the rendering:
/**
* THIS FILE IS GENERATED AUTOMATICALLY.
* DO NOT EDIT.
*
* You are probably looking on adding startup/initialization code.
* Use "quasar new boot <name>" and add it there.
* One boot file per concern. Then reference the file(s) in quasar.conf.js > boot:
* boot: ['file', ...] // do not add ".js" extension to it.
*
* Boot files are your "main.js"
**/
import Vue from 'vue'
import './import-quasar.js'
import App from 'app/src/App.vue'
import createStore from 'app/src/store/index'
import createRouter from 'app/src/router/index'
export default async function () {
// create store and router instances
const store = typeof createStore === 'function'
? await createStore({Vue})
: createStore
const router = typeof createRouter === 'function'
? await createRouter({Vue, store})
: createRouter
// make router instance available in store
store.$router = router
// Create the app instantiation Object.
// Here we inject the router, store to all child components,
// making them available everywhere as `this.$router` and `this.$store`.
const app = {
router,
store,
render: h => h(App)
}
app.el = '#q-app'
// expose the app, the router and the store.
// note we are not mounting the app here, since bootstrapping will be
// different depending on whether we are in a browser or on the server.
return {
app,
store,
router
}
}
I.e. in principle I should be able to link in without it causing any conflict situations. The question is, how would that look?
I have to link into the rendering afterwards and overwrite it as described in the code example. I would like to stay with the Quasar Cli, because it is very useful and the situation described here is the only exception.
p7
the boot files is the right place to inject and initialize your own dependencies or just configure some startup code for your application.
I have not had the opportunity to use the library you mention, but I detail a little how you could implement
create your boot file
import { plugin } from '#inertiajs/inertia-vue';
export default async({ app, Vue }) => {
Vue.use(plugin);
}
until there you have 50%. On the other hand, you cannot do a mixin to the main instance but you could do it for each page, however I recommend that you make a component part to which you add the data you need and make a mixin of the library you need
<template>
<div />
</template>
<script>
import { App } from '#inertiajs/inertia-vue';
export default {
mixins: [App],
props: ['initialPage', 'resolveComponent'],
}
</script>
In order to do this, modify according to how the library you use works.
using the vue-cli-service which uses webpack under the hood, is there a way to set things up so on hot reload the browsers console is cleared?
It is not very efficient to constantly see old messages in the console log that are not relevant to the current state of the app on last save.
Simple keep clearing the console every time a hot reload happen by adding this to the main.js file
if (module.hot) {
module.hot.accept() // already had this init code
module.hot.addStatusHandler(status => {
if (status === 'prepare') console.clear()
})
}
main.js file now would be
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App)
}).$mount('#app')
if (module.hot) {
module.hot.accept() // already had this init code
module.hot.addStatusHandler(status => {
if (status === 'prepare') console.clear()
})
}
I am designing an architecture for the Vue 3 app with distributed module-based ownership. Module system will be represented with plugins (seems like the most appropriate solution allowing vuex module and vue-router dynamic injects). Each such module/plugin will be developed by dedicated team working within isolated repos. We cannot use npm package-per-plugin approach as deployment process should be isolated as well, and with npm approach core app team will have to rebuild app each time npm package plugin has updates. This means we will have to load such plugins/pages at runtime via http.
So far this approach by Markus Oberlehner seems like some sort of the way to go - it uses custom Promise based solution for webpack's missing "load external url script at runtime" functionality. While it works fine with Vue 2, Vue 3 gives VNode type: undefined error.
The above mentioned article offers the following webpack external component loading solution:
// src/utils/external-component.js
export default async function externalComponent(url) {
const name = url.split('/').reverse()[0].match(/^(.*?)\.umd/)[1];
if (window[name]) return window[name];
window[name] = new Promise((resolve, reject) => {
const script = document.createElement('script');
script.async = true;
script.addEventListener('load', () => {
resolve(window[name]);
});
script.addEventListener('error', () => {
reject(new Error(`Error loading ${url}`));
});
script.src = url;
document.head.appendChild(script);
});
return window[name];
}
But above, as I said, does not work with Vue 3 defineAsyncComponent mechanism.
// 2.x version WORKS
const oldAsyncComponent = () => externalComponent('http://some-external-script-url.js')
// 3.x version DOES NOT WORK
const asyncComponent = defineAsyncComponent(
() => externalComponent('http://some-external-script-url.js')
)
So I have two questions:
Are there any known better solutions/suggestions for above architectural specification?
Is there any working webpack dynamic external import solutions tested with Vue 3 out there?
UPD: Here is small reproduction repo
We solved this problem together via chat.
Components built via the Vue 3 vue-cli rely on Vue being available in the global scope. So in order to render components loaded via the technique described in my article, you need to set window.Vue to a reference to Vue itself. Then everything works as expected.
update:
If import vue from vue/dist/vue.esm-bundler and set to global, then no need to change webpack / Vite config, and no need to load vue from cdn.
import * as Vue from 'vue/dist/vue.esm-bundler';
window.Vue = Vue;
Besides setting window.Vue, some other webpack or Vite configuration should also be set, otherwise some error is presented in console: vue warn invalid vnode type symbol(static) (symbol)
Vue3 + webpack:(https://github.com/vuejs/vue-next/issues/2913#issuecomment-753716888)
// index.html:
<script src="https://cdn.jsdelivr.net/npm/vue#3.0.4"></script>
// vue.config.js
configureWebpack: config => {
...
config.externals = { vue: 'Vue' }
...
}
Vue3 + vite:(https://github.com/crcong/vite-plugin-externals)
// vite.config.js
import { viteExternalsPlugin } from 'vite-plugin-externals'
export default {
plugins: [
viteExternalsPlugin({
vue: 'Vue'
}),
]
}
I'm trying (unsuccessfully) to pre-render the HTML of multiple Vue apps within the same project scaffolded with Vue CLI. I do not want to use Vue Router or Nuxt etc for multiple reasons.
I've tried using prerender-spa-plugin, but since I don't use routes, it only pre-renders the index.
My vue.config.js looks like this:
const path = require('path');
const PrerenderSPAPlugin = require('prerender-spa-plugin');
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer;
module.exports = {
pages: {
index: 'src/index.js',
about: 'src/about.js',
},
configureWebpack: {
plugins: [
new PrerenderSPAPlugin({
staticDir: path.join(__dirname, 'dist'),
routes: ['/'],
postProcess(route) {
route.html = route.html.replace('</script><div>', '</script><div id="app" data-server-rendered="true">');
return route;
},
renderer: new Renderer({
headless: true,
renderAfterDocumentEvent: 'render-event',
}),
}),
],
},
};
and my index.js and about.js essentially look like this:
import Vue from 'vue';
import App from './Index.vue';
new Vue({
render: h => h(App),
mounted() {
document.dispatchEvent(new Event('render-event'));
},
}).$mount('#app');
I also have unique public/ index.html and about.html pages.
The routes parameter of prerender-spa-plugin doesn't seem to recognise things like '/about.html'. Is there an easy way of achieving multiple pre-rendered pages?
Do I have to wrestle with the SSR module?
Thanks in advance.
The solution I've found is to call new PrerenderSPAPlugin multiple times, one for each route.
I'm also facing the same issue, i have static html uses vue component and i want to pre-render the vue component in output build directory. I'm using laravel-mix package for build process.
Could you post the full solution for this i.e calling new PrerenderSPAPlugin multiple times, one for each route.
If i can get the full webpack.config.js, it would easy for me to understand and implement the same using laravel-mix.