I have an A-Frame project with a primary component street
that also depends on other components and helper functions from the same repo.
Currently using the street
component in an A-Frame scene requires importing 8 separate js files from the same repo each with their own <script>
tag. (code / show page)
Instead, I would prefer a simpler structure to import just one file, but I'd rather not use a bundler such as webpack. I think there is an ES6 approach but I'm confused about how to go about leveraging ES6 imports while still allowing the components to access functions from other files. In other words, how to get the imported files into the same namespace.
This question helps but the comments clarify the imported functions are not added to the global namespace automatically: ES6 import equivalent of require() without exports
Maybe the structure would be something like this?
// index.html
<script src="src/street.js" ></script>
<a-scene>
<a-entity street="dosomething"></a-entity>
</a-scene>
// helperFunctions1.js
function coolHelperFunction1a (variable) {
// useful code
}
// street.js
import 'helperFunctions1.js'
import 'helperFunctions2.js'
AFRAME.registerComponent('street', {
coolHelperFunction1a (variable);
})
That probably won't work, what's the right way to do it?
A-Frame and ES Modules don't play well together, and I don't think there's a solution without using a bundler like Webpack.
In short, if you want to have a single <script>
element for your entire app while still organizing your code well, I'd recommend Webpack, with the generated bundle being loaded synchronously in the <head>
of your HTML document. Webpack can consume ES Modules, so you're still free to use those with the build process.
More detail on why native module loading won't work:
Your code would need a few changes for native ES Module usage, but the first one presents a problem: the <script>
elements loading your modules need the type="module"
attribute. This has a side effect: the browser treats modules the same way it treats traditional script tags with the defer attribute, meaning the script is executed after the DOM has been parsed. This is by design, as it leads to better initial page performance. Unfortunately A-Frame expects to run before the DOM has been parsed, and further it expects that you've initialized all of your components, systems, etc. before it runs. That is why they recommend putting the a-frame script in the <head>
, and you'll notice there is no defer
attribute.
A-Frame is kind of going against modern web performance advice, but it has a good reason: it doesn't want the browser to render the HTML before it runs, since its using the HTML for its own purposes.
This all means using native ES modules directly in the browser isn't going to work for an A-Frame app.