I'm trying to automatically load components from a URL while parsing HTML templates, based on tag names. i.e. If an tag name ends with Component
(like xxxComponent
) this is a component which should be loaded from server, if is not already loaded.
I've wrote a code, but I can't figure out how I can force Vue to load components based on tag name.
Here is a simple VueJS app code which:
- uses CDN version of VueJS.
- has 2 components:
myComponent
andanotherComponent
. - the
loadComponent
function tries to load a component by name and returnstemplate
,script
, andstyle
of the component. - tries to use
config.isUnknownElements
which doesn't work at all and is never called (this link suggests this approach)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue App</title>
<script src="@3/dist/vue.global.js"></script>
<style>
/* Dynamically loaded styles will be added here */
</style>
</head>
<body>
<div id="app">
<h1>START</h1>
<hr>
<myComponent />
<anotherComponent />
<hr>
<h1>END</h1>
</div>
<script>
const componentCache = {};
async function loadComponent(name) {
if (componentCache[name]) {
return componentCache[name]; // Return cached component if already loaded
}
const url = `http://127.0.0.1:8080/src/${name}.vue`;
try {
const response = await fetch(url);
if (!response.ok) throw new Error(`Component ${name} not found`);
const componentCode = await response.text();
const parsedComponent = {
template: componentCode.match(/<template>([\s\S]*?)<\/template>/).trim(),
script: componentCode.match(/<script>([\s\S]*?)<\/script>/).trim(),
style: componentCode.match(/<style>([\s\S]*?)<\/style>/).trim(),
};
componentCache[name] = parsedComponent; // Cache the loaded component
return parsedComponent;
} catch (error) {
console.error(error);
return {
template: `<div>The component <strong>${name}</strong> is not available.</div>`,
script: ``,
style: `div {color: red; border: solid 1px red;}`,
};
}
}
const app = Vue.createApp({
template: document.getElementById('app').innerHTML,
});
app.config.isUnknownElements = function () {
let tagName = arguments[0].toLowerCase();
if (/\S+Component$/i.test(tagName)) {
console.log('loading component: ' + tagName);
if (typeof Vue.optionsponents[tagName] === 'undefined') {
// register a lazy-loader for the component
Vueponent(tagName, function (resolve) {
// ... Load the component 'componentName' in a "lazy" way, see link (1)
});
}
}
return false; // Same behavior of the default Vue configuration, see link (2)
};
console.log(app.config);
app.mount("#app");
</script>
</body>
</html>
I'm trying to automatically load components from a URL while parsing HTML templates, based on tag names. i.e. If an tag name ends with Component
(like xxxComponent
) this is a component which should be loaded from server, if is not already loaded.
I've wrote a code, but I can't figure out how I can force Vue to load components based on tag name.
Here is a simple VueJS app code which:
- uses CDN version of VueJS.
- has 2 components:
myComponent
andanotherComponent
. - the
loadComponent
function tries to load a component by name and returnstemplate
,script
, andstyle
of the component. - tries to use
config.isUnknownElements
which doesn't work at all and is never called (this link suggests this approach)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue App</title>
<script src="https://unpkg/vue@3/dist/vue.global.js"></script>
<style>
/* Dynamically loaded styles will be added here */
</style>
</head>
<body>
<div id="app">
<h1>START</h1>
<hr>
<myComponent />
<anotherComponent />
<hr>
<h1>END</h1>
</div>
<script>
const componentCache = {};
async function loadComponent(name) {
if (componentCache[name]) {
return componentCache[name]; // Return cached component if already loaded
}
const url = `http://127.0.0.1:8080/src/${name}.vue`;
try {
const response = await fetch(url);
if (!response.ok) throw new Error(`Component ${name} not found`);
const componentCode = await response.text();
const parsedComponent = {
template: componentCode.match(/<template>([\s\S]*?)<\/template>/).trim(),
script: componentCode.match(/<script>([\s\S]*?)<\/script>/).trim(),
style: componentCode.match(/<style>([\s\S]*?)<\/style>/).trim(),
};
componentCache[name] = parsedComponent; // Cache the loaded component
return parsedComponent;
} catch (error) {
console.error(error);
return {
template: `<div>The component <strong>${name}</strong> is not available.</div>`,
script: ``,
style: `div {color: red; border: solid 1px red;}`,
};
}
}
const app = Vue.createApp({
template: document.getElementById('app').innerHTML,
});
app.config.isUnknownElements = function () {
let tagName = arguments[0].toLowerCase();
if (/\S+Component$/i.test(tagName)) {
console.log('loading component: ' + tagName);
if (typeof Vue.optionsponents[tagName] === 'undefined') {
// register a lazy-loader for the component
Vueponent(tagName, function (resolve) {
// ... Load the component 'componentName' in a "lazy" way, see link (1)
});
}
}
return false; // Same behavior of the default Vue configuration, see link (2)
};
console.log(app.config);
app.mount("#app");
</script>
</body>
</html>
Share
Improve this question
asked Mar 25 at 18:15
user1243815user1243815
113 bronze badges
8
|
Show 3 more comments
1 Answer
Reset to default 0Maybe you're looking for this https://vuejs./guide/essentials/component-basics#dynamic-components
<component :is="tagName" />
defineAsyncComponent( () => loadModule('./myComponent.vue', options) )
can be used in any place where dynamically loaded component is required. loadableComponent would be a suitable place for this . I'm not sure it's possible to do in Vue 3 what you tried to do for <myComponent /> with isUnknownElements (it's isUnknownElement in the link) because it was a solution for v2 only, I'm unaware of v3 – Estus Flask Commented Mar 25 at 22:17<template><component :is="cache[props.load]"/></template><script setup>let cache=shallowReactive({}); let props=defineProps(['load']); watchEffect(() => { if (props.load && !cache[props.load]) { cache[props.load] = defineAsyncComponent(() => loadModule(`.../src/${name}.vue`)) })
. Cache could be global in case there are multiple occurrences of loadableComponent. I'd expect something like this to work. Let me know if this is what you asked about – Estus Flask Commented Mar 25 at 22:28