- Dynamic loading of Vue components
Remember to install suggested extensions in StackBlitz, after the installation of the project.
If this approach doesn't work for you, you can always just clone the project and run it locally.
Sample project for dynamic loading of Vue components using:
typescript
- yes, ambitious goal but finally types and intellisense are working!generic components
- more on this heredefineAsyncComponent
- for dynamic loading of componentsvite-plugin-chunk-split
- for chunk splitting
Note
This project is based on my vue template. Feel free to use it as a base for your project.
Vue 3 offers a lot of very useful features like defineAsyncComponent
and generic components.
What it doesn't offer is a way to use them together in a fully typed way out of the box.
So I set myself a goal to make it work!
You could ask:
But why?
A normal way of dealing with this is to usev-if
orv-show
directives!.
Sure, but what if most of your app is almost split in half, but still contains many common parts?
There are such cases in which your app has to be very flexible in showing many different components for different:
- domains/areas
- client types
- user roles
- user permissions
- depending on the data fetched from the server
- and so on...
Normally you would have to duplicate a lot of code or use a ton of v-if
directives.
So why not make it more dynamic and organized at the same time, by setting all in one place and changing it globally?
The main idea was to create a generic wrapper component that will load a one of the components fro a wished group
depending on the domain
(user role, client type, etc.).
The domain
can be set in the localStorage
, read by pinia
store and used in the wrapper component.
Setting it can accompany a login action, result of a server response or any other interaction with the app.
Ideally such wrapper component would have just one additional property which would define the group from which
one of the components would be loaded and the rest of the properties would be expected by the wrapper based
on an amalgamation of the types of all the components that could be loaded.
Due to limitations of Vue 3 generic components
, it not an option, but it's still possible to make it with two properties:
group-name
- for the groupinternal-props
- for a reactive object with the rest of the properties
If you would like to move the whole plugin to your project, it should be possible by simply copying the dynamicLoad
folder from the plugins
directory and modifying files mentioned below.
Note
Plugin uses pinia
and @vueuse/core
- remember to install it in your project.
Probably you would also need to copy or exchange the useLogger composable I used for a better logging.
This sample can be found in the DomainTestParent.vue component
<template>
<DynamicLoad
group-name="DomainTestGroup_1"
:internal-props="{
countDomainA: countDomainA,
countDomainB: countDomainB,
countGeneral: countGeneral,
onEmitDomainA: (e) => logToConsole(e.message + ' - onEmitDomainA'),
onEmitDomainB: (e) => logToConsole(e.message + ' - onEmitDomainB'),
onEmitGeneral: (e) => logToConsole(e.message + ' - onEmitGeneral'),
}"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const countGeneral = ref(999)
const countDomainA = ref(100)
const countDomainB = ref(200)
const logToConsole = (value: any) => {
console.log(value)
}
</script>
The DynamicLoadListShape
is a type that ensures that the dynamicLoadList
object has the correct structure,
and relies on the DOMAIN
const by allowing only the keys defined as the possible domains.
The const is defined in the localStorage.ts file.
Note
Modify it to match your wished domains/areas/groups/roles etc.
export const DOMAIN = {
DOMAIN_A: 'domain_A',
DOMAIN_B: 'domain_B',
} as const
Note
There is one more thing to change in the dynamicLoadFilters.ts file.
At the very end of the file you have to add your domains like so to get the full support of the types and intellisense.
type YourDomainAComponentProps<G extends DynamicLoadGroups>
= FilterComponentsProps<G> extends { YOUR_DOMAIN_A: unknown }
? FilterComponentsProps<G>['YOUR_DOMAIN_A'] : object
//...
export type CombinedComponentsProps<G extends DynamicLoadGroups> = Prettify<
YourDomainAComponentProps<G>
& YourDomainBComponentProps<G>
>
The mentioned previously component list is located in the dynamicLoadList.ts file.
export const dynamicLoadList = {
DomainTestGroup_1: {
DOMAIN_A: defineAsyncComponent(DomainTestGroup_1_domain_A),
DOMAIN_B: defineAsyncComponent(DomainTestGroup_1_domain_B)
},
DomainTestGroup_2: {
DOMAIN_B: defineAsyncComponent(DomainTestGroup_2_domain_B)
},
} satisfies DynamicLoadListShape
Note
Modify it to match your wished groups and components.
Probably it could be done simpler if there would be more native support for generics in Vue 3,
but it works, is fully typed and offers a lot of flexibility and potential for extending.
Feel free to use it in your project, improve it and share your thoughts via discussions in this repo.
Assuming that you have already installed Node.js
,
go to the root directory and run the following commands.
# Install globally Yarn package manager
npm install -g yarn
# Install dependencies
yarn run init
Start the app in development mode with hot-reloads
yarn run serve
# Build for production
yarn run build:prod
# Build for staging
yarn run build:staging
yarn run preview
# Only lint
yarn run lint
# Lint and fix
yarn run lint:fix
# Install dependencies for ci
## Helpful when you want to install all dependencies for a CI pipeline,
## reassuring that they are installed exactly as in the `yarn.lock` file.
yarn run ci
# Clean dependencies
## Helpful when you want to reinstall all dependencies.
yarn run clean
All commands, tips and tricks and documentation about used tools and libraries are in the
DEVELOPMENT.md file.
yarn run build:prod
# or
yarn run build:staging
The builds are saved in the /dist
directory.
The NODE_ENV=production is not supported in the .env file in Vite
Only NODE_ENV=development is supported to create a development build of the project
Vite has also so called Mode, depending on the .env file used for build or a serve command.
Respectively it uses
.env
or.env.development
file for development mode (serve command).env.production
file for production mode (build command).env.[mode]
file for custom mode (build command with --mode [mode] argument)
If you need some local environment variables, use .env.development.local
file.
More about modes here
To make use of it in your code, you can use import.meta.env.MODE
variable.
if (import.meta.env.MODE !== 'production') {
console.log('Not the production mode');
}
Use VITE_APP_
prefix to expose environment variables to your app
VITE_APP_API_URL=https://api.example.com