Async components Vue 2 - javascript

I'm trying to use async components. Here is my configuration:
Vue 2 using Single File Component approach
Webpack 2
Vue Router
The app is pretty basic, I have an "everyone" section contained in App and an "admin" section contained in Admin. I would like to load the component and all the .js related to the Admin if and only if I'm visiting the corresponding route.
After reading the vue-router docs on Lazy Loading, and the one of Vue2 on async components, I'm still not sure how to do that especially with the Single File Component approach.
Here is what I did for the moment but I don't know if it is ok since in the documentation of Vue2 they said :
Vue.component(
'async-webpack-example',
() => import('./my-async-component')
)
Also what do I have to do with webpack so it creates a chunk of everything related to Admin so that adminChunk.jsis just loaded when reaching admin route ?
What is the syntax to make a single file component a async component ?
app.js
const Admin = resolve => {
// require.ensure is Webpack's special syntax for a code-split point.
require.ensure(['./components/admin/Admin.vue'], () => {
resolve(require('./components/admin/Admin.vue'))
})
};
const routes = [
{ path: '/', component: App },
{ path: '/admin', meta: { requiresAdmin: true }, component: Admin},
];
Admin.vue
<template>
<admin-menu></admin-menu>
<child></child>
</template>
<script>
import AdminMenu from './Admin-Menu.vue'
import Child from './child.vue
export default{
data () {
},
components: {
AdminMenu,
Child,
},
}
</script>

You can pass a third parameter to the require.ensure function with the name of the chunk.

Related

In Angular, how can one component to have multiple HTML templates?

I am developing an ecommerce application, and one major feature is that this app should have multiple themes. The total number of themes could be 100 or even more. However, these themes all have the same data (For example: all home page have same banner images, new product, feature product data.) .
I know I can use ng-template or TemplateRef to determine which piece of HTML should display. But since I have over 100 themes, both ng-template or TemplateRef methods will load a lot of extra files. So I think I need some sort of lazy load, when a component loads the data then lazy loads the correct HTML template. So how can I have this work?
Looks like it is possible, all our routes are handled by lazy loaded modules. This is our out-of-the-box route config:
const routes: Routes = [
{ path: '', loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule) }
];
While module lazy has this route config:
const routes: Routes = [
{ path: 'home', component: HomeComponent },
]
While HomeComponent is taken from the declarations of module lazy.
Then define another module, called for example lazy-two with the same route config, and its own HomeComponent.
Finally, you can switch between the modules by using this code:
lazyLoad() {
const routes: Routes = [
{
path: '',
loadChildren: () => import('./lazy-two/lazy-two.module')
.then(m => m.LazyTwoModule)
}
];
this.router.resetConfig(routes);
this.router.navigateByUrl('/home');
}
This will lazy load module lazy-two and refresh the route to /home - you will see the component of the new module displayed.
I couldn't create a stackblitz, some errors occurred probably because of lazy loading. So I ran it locally on my machine and pushed the code to GitHub
EDIT I managed to make a StackBlitz
I recommend used ComponentFactoryResolver to create the components that you need to render.
this.templates = [
{
id: "template-1",
component: Template1,
},
{
id: "template-2",
component: Template2,
},
];
ngOnInit() {
this.templates.forEach((element) => {
this.containerReference.createComponent(
this.factoryResolver.resolveComponentFactory(element.component)
);
});
}
in the .html you should have
<ng-container #containerReference><ng-container>
what about using the same component and styling it different when you select the template?

Vue.js moduler build

We have project of around 100 pages. We are migrating our front end to some emerging technology. We almost have finalized Vue.js(with vue cli). Vue CLI is building project in one build.js. We have a problem with that. We have frequent requirement changes. So after every small change, we will have to upload whole build js and that will need regression testing of the whole project. Is there any way that build will be module wise? So that only changed module need to be uploaded on live after changes.
Using the Vue router:
The following approach will tell the compiler (Webpack) to "return" the component vs "including" it. Resulting in the given component being "chunked" into its own file for lazy loading.
e.g.
export default new Router({
routes: [
// Home component to be included in bundle.js
{
path: '/',
name: 'home',
component: Home
},
// code splitting - generate a separate unique chuck for about component.
{
path: '/about',
name: 'about',
component: () => import(/* webpackMode: "lazy" */ '#/views/About.vue')
}
]
})
Output = bundle.js and about.js or... 100 other files, given you have a component per page.
more on component lazy loading: https://router.vuejs.org/guide/advanced/lazy-loading.html
Using webpack:
You can extend and/or modify the default compiler (webpack) configuration by adding a vue.config.js file to the project root.
e.g.
// vue.config.js
module.exports = {
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
// mutate config for production...
} else {
// mutate for development...
}
}
}
Be sure to read all the documentation at https://cli.vuejs.org/guide/webpack.html as some settings should not be mutated directly.
more on webpack code splitting: https://webpack.js.org/guides/code-splitting

Dynamic Vue Import from a subfolder

I am trying to import all .vue files in a certain subfolder into another component. I know about Global registration of Base components but this does not seem to help me here.
Let's say I have default Vue component (not the main one) with something like this:
export default {
components: {
red: () => import('./panes/red'),
},
data() {
return {
current: false,
};
},
and my file structure is something like:
/src
- main.js
- main.vue
-- components/
-- panes/
--- /red.vue
--- /green.vue
--- /blue.vue
-- SubMain.vue
I am trying to dynamically create the components object for the SubMain.vue folder.
So I try something like this in SubMain.vue:
import myComponents from './src/components/panes';
...
components: Object.keys(myComponents).reduce((obj, name) => {
return Object.assign(obj, { [name]: () => import(myComponents[name]) })
}, {}),
But eslint is giving me errors about Missing file extension and Unable to resolve path to module on the myComponents variable. Yes, I double checked my path and tried other paths. Nothing. I am using the Vue CLI 3 project.
If you're using Webpack, you can use require.context:
import _kebabCase from lodash/kebabCase
const components = require.context('src/panes', true)
components.keys().map(component => {
// turn './ComponentName.vue' into 'component-name'
const componentName = _kebabCase(
component.replace(/^\.\//, '').replace(/\.vue$/, '')
)
// register new component globally
Vue.component(componentName, components(component))
})
I don't think you can import multiple components that way without an index file that aggregates the component import/exports for you. See Import multiple components in vue using ES6 syntax doesn't work

Vue with dynamic routes and components

Trying to build a test app to see if Vue is a suitable replacment for our AngularJS app. Trying to learn Vue at the same time.
After the user logs in we fetch some roles for that user. Base off those roles is how the menu gets built.
User1 { Role1, Role2, Role3}
In theory
User2 {Role1, Role3}
So Role1 would have a path of /start/page1 and page1 (component) and two child components.
Same with Role2 path of /start/page2 and page2 would have components on it.
I don't really want to build the routes until I know which roles the user has.
I'm using quasar-framework.org and using the menu slide out. Trying to create a menu on the fly. Seems like I need the components to already be imported?
I'm able to build the menu by looping through the roles and setting up a list of menus.
Trying to build the routes on the fly using this.$router.addRoutes(newRoute);
To do that I need the component to already be imported.
The Quasar way is load the components on the fly I guess.
In router.js
function loadPage (component) {
return () => import(`../../pages/${component}.vue`)
}
I can't seem to use that function in a method section.
Is this possible in Vue?
Take a look at vue-router lazy loading documentation and Quasar lazy loading documentation
You can't do it in a method, but if the user permission don't match the route permissions the component is never loaded, which is basically what you want.
Example
const routes = [
{
path: '/some-page-protected',
component: () => import('pages/SomePage'),
meta: {role: 'admin'}
}
]
Or
const SomePage = () => ('pages/SomePage')
const routes = [
{
path: '/some-page-protected',
component: SomePage,
meta: {role: 'admin'}
}
]

How can Vue router get current route path of lazy-loaded modules on page load?

I have a vue app with router set up like:
import index from './components/index.vue';
import http404 from './components/http404.vue';
// module lazy-loading
const panda= () => import(/* webpackChunkName: "group-panda" */ "./components/panda/panda.vue");
// ...
export const appRoute = [
{
path: "",
name: "root",
redirect: '/index'
},
{
path: "/index",
name: "index",
component: index
},
{
path: "/panda",
name: "panda",
component: panda
},
//...
{
path: "**",
name: "http404",
component: http404
}
];
So the panda module is lazy-loaded. However, when I navigate to panda page, a console.log() of this.$route.path in App.vue's mounted() lifecycle only outputs
"/"
instead of
"/panda"
But index page works well, it shows exactly
"/index"
as expected.
So how can Vue router get current path correctly of a lazy-loaded page, when page is initially loaded? Did I miss something?
Edit:
It can, however, catch the correct path after Webpack hot-reloads. It catches "/" on first visit of panda, but after I change something in source code, webpack-dev-server hot-reloads, then it gets "/panda".
So I guess it has something to do with Vue life-cycle.
There is a currentRoute property that worked for me:
this.$router.currentRoute
May be you need to use $route not $router
check here : https://jsfiddle.net/nikleshraut/chyLjpv0/19/
You can also do it by $router this way
https://jsfiddle.net/nikleshraut/chyLjpv0/20/
Use this.$route.path.
Simple and effective.
Hide Header in some components using the current route path.
get current route path using this.$route.path
<navigation v-if="showNavigation"></navigation>
data() {
this.$route.path === '/' ? this.showNavigation = false : this.showNavigation = true
}
If You have similar problem the correct answer is to use router.onReady and then calling your logic concerning path. Below the official Vue router docs:
router.onReady
Signature:
router.onReady(callback, [errorCallback])
This method queues a callback to be called when the router has completed the initial navigation, which means it has resolved all async enter hooks and async components that are associated with the initial route.
This is useful in server-side rendering to ensure consistent output on both the server and the client.
The second argument errorCallback is only supported in 2.4+. It will be called when the initial route resolution runs into an error (e.g. failed to resolve an async component).
Source: https://v3.router.vuejs.org/api/#router-onready
For vue 3 (Composition API)
It can be as simple as route.path if you define the variable route as: const route = useRoute()
Usage example
If you try the following, each time your route path changes it will console log the current path:
<script setup>
import {useRoute} from 'vue-router'
const route = useRoute()
watchEffect(() => console.log(route.path))
</script>

Categories

Resources