Reputation: 3161
Is it possible to use import without a script tag already in place for said module?
My problem is that I want to load modules dynamically based on a config file, for example :
Folder structure :
|-- main.js
|-- config.json.js
|-- modules
|-- module1.js
|-- module2.js
|-- module3.js
Index.html head :
<script src="/config.json.js" type="module"></script>
<script src="/main.js"></script>
config.json.js :
export default {
modules : ['module1', 'module3']
}
main.js :
import config from '/config.json.js'
//Loading modules defined in config
config.modules.forEach(moduleName => {
import(`modules/${moduleName}`)
.then( module => {
console.log(`${module.name} loaded.`);
)}
})
The above won't work as the modules haven't been defined in a script tag.
Is there any way I can achieve this using vanilla JS and keeping it clean?
Upvotes: 2
Views: 4486
Reputation: 13067
Edit! Dynamic import has landed in Firefox 67+.
(async () => {
await import('./synth/BubbleSynth.js')
})()
https://caniuse.com/#feat=es6-module-dynamic-import
Old answer:
To import further modules after DOM load, one not so clean but working way can be to create a new caller script of type module.
/* Example */
/* Loading BubbleSynth.js from «synth» folder*/
let dynamicModules = document.createElement("script")
dynamicModules.type = "module"
dynamicModules.innerText = "import * as bsynth from '../synth/BubbleSynth.js'"
/* One module script already exist. eq: «main.js», append after it */
document.querySelector("script[type='module']").parentElement.appendChild(dynamicModules)
Destroying the module caller script does not harm the past calls:
document.querySelectorAll("script[type='module']")[1].outerHTML = ""
// *BubbleSynth* is still in memory and is running
But appending a new module call to that script does not works. A new caller module script must be created.
As a function:
function dynModule(me){
let dyn = document.createElement("script")
dyn.type = "module"
dyn.innerText = `import * as ${me} from '../synth/${me}.js'`
document.querySelector("script[type='module']").parentElement.appendChild(dyn)
document.querySelectorAll("script[type='module']")[1].outerHTML = ""
}
dynModule("BubbleSynth")
Upvotes: 2
Reputation: 76208
Yes you can, as long as your loader script is marked as module
<script type="module">
const moduleSpecifier = './myModule.mjs';
import(moduleSpecifier)
.then((module) => {
// do something
});
</script>
Although, in your case, a simple forEach
may not be enough. You may need Promise.all
or similar if you want to wait for all the modules to load from your config.
const modules = config.modules.map(moduleName => import(`modules/${moduleName}`))
Promise.all(modules)
.then(modules => {
// modules is an array of all your loaded modules
console.log('All modules loaded.');
)}
Further reading:
Upvotes: 3