I think I now mostly understand (in theory, at least) how ES2015 Modules work.
What I don't understand (and this is far from insignificant) is how to enable module scripts to inform my main script.
Usually, at the bottom of my HTML document, I have something like the following:
<script src="/global-script-is-a-classic-script.js"></script>
That's it. No defer, no async and no type="module".
I may wish to write some optional modules which declare functions, calculate variables and objects and then make all the declared and calculated values available to global-scripts.js:
<script src="/module-script-1.mjs" type="module"></script>
<script src="/module-script-2.mjs" type="module"></script>
<script src="/module-script-3.mjs" type="module"></script>
Naturally each of these module scripts will contain an exports statement.
But... how do I then import what these module scripts calculate into my global-script.js?
I can't use import - because that only works in a Module Script - and (unless I'm mistaken) it seems like it would be a bad idea to have global-scripts.js as anything other than a Classic Script.
So, do I now need to have two global scripts?
<script src="/global-script-is-a-classic-script.js"></script>
<script src="/module-mothership-is-a-module-script.js" type="module"></script>
Or is there an alternative standard architecture which we're all supposed to be using?
It seems you aren't really understanding the change in design philosophy with a module architecture. You don't make things global. A module imports the other modules that it needs and gets its capabilities from the import, not from some global set of functions. In a modular architecture, there would be NO global script. That's why your thinking isn't quite fitting into the modular architecture.
exports is a means of making certain entry points or data publicly accessible to anyone who imports the module while keeping everything else local to the module. One must import the module to get access to these entry points.
So, is exports intended for communication between sub-modules and modules then, rather than for between modules and a global script? That is to say modules never export, only sub-modules do?
Mostly. But, even a top level module could have exports. They wouldn't be used for anything in the project in which it was a top level module, but it could perhaps be used in a different project where it wasn't a top level module and was imported by some other module. The module architecture gets you to think about code reuse and code sharing without having to do a lot of extra work. The normal way you write your project should automatically create a bunch of reusable code.
That might have been the final piece of the puzzle I needed. So exports is just for sub-modules (and sibling-modules), then? Standalone, self-contained top-level modules don't need exports because any functionality happens right there in the module - the data never needs to go anywhere else. (Is that right?)
Yes. Unless you might want to be able to reuse that module in another project where you did import it and did want to import some entry points or data.
I may wish to write some optional modules which declare functions, calculate variables and objects and, after calculation, make all the declared and calculated values available to global-scripts.js. But... how do I then import what these module scripts calculate into my global-script.js?
You will get rid of your global script entirely in a module design. You will have a top level module that imports other modules.
So, do I now need to have two global scripts?
Nope, you get rid of the global script and you don't make things global any more.
Or is there an alternative standard architecture which we're all supposed to be using?
The module architecture:
Top Level Module
import module1
import moduleA
import moduleZ
import moduleB
import module2
import moduleB
import moduleC
import module3
import moduleA
import moduleD
This can go to any arbitrary depth as needed. Modules are cached and only initialized once so importing the same module from more than one place is perfectly fine and perfectly efficient.
Related
why not simply use folders instead of modules if the only purpose is to make the structure more organized?
It is weird because it does not seem necessary since every file is a separate module in node js and if i need anything i have to import it using the import statement anyways so what is the point of specifying the imports and exports in a module? does it affect the dependency injection if i just import what i need without using modules(#Module decorator) or something similar?
NestJs has opted to design their framework with dependency injection as one of its core principals. Ie, you write your code just using the name/type of the services you want to use, and then a different piece of code is responsible for finding that service, knowing how to construct it, and then passing it in to you.
Native import/export doesn't have a system for dependency injection, so the main thing that the #Module decorator does is organize the metadata needed for the injector system.
I’m trying to import whole modules in a javascript file.
This file pertains to the Home Assistant environment where the frontend is written in javascript, usually using LitElements and modules (cf. documentation).
For instance, the doc uses a fancy wired-card by writing:
import "https://unpkg.com/wired-card#0.8.1/wired-card.js?module";.
I've read a lot about the import call but resources are usually about local elements and it seems that I need them to be distant.
In fact, I know the plain old JS quite well but I am a bit clueless regarding importing modules (and LitElements for that matter).
For instance, I'm looking for an accordion (expansion panel), like the one of JQueryUI. I found several resources (e.g. here, here, or here) but I couldn't find how to import them easily.
What makes a module importable? Are those not or am I doing it wrong?
In standard ECMAscript, a JS file is importable if it defines a module in the new system. Kinda circular.
Basically, it should export some resources from the module file. For example, if I have a test-module.js, I can export some class using the export keyword:
class Fubar {}
export { Fubar }
// or, more concisely
export class Fubar {}
The export keyword tells the module system that the resource defined should be made available to importers.
On the flip side, if you want to import a module, you must also do so from a module! This is because module imports are async and processed before the execution (excluding the dynamic import() function).
So, if I want to import my Fubar class from another module, I can do this:
import { Fubar } from './test-module.js`
However, if I load this script as a non-module, I will get an error. Instead, I must tell the browser that the script is a module:
<script type="module" src="test-module.js"></script>
So, in short, something is "importable" if it is itself a module.
More reading:
Mozilla Dev Network article on the modules system: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
MDN article on the import keyword: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
EDIT: something I missed - to make web resources a little nicer, the import URLs can be any URL, not just a relative path. This allows importing 3rd party scripts as modules. But, those 3rd party scripts need to be modules themselves.
I currently have my js files layed out as such
file.js
anotherFile.js
anotherFile2.js
db.js
I want to be able to include the db.js in file.js,anotherFile.js and anotherFile2.js. Do I need to import db.js into every single file or is there a way that I can import it once to be used throughout the project.
file.js imports anotherFile.js and anotherFile2.js but all use db.js
Thanks in advance.
Do I need to import db.js into every single file...
That's best practice, yes. More specifically: You import the parts you need from it. (Don't worry, it's only loaded once.) Otherwise, you're back in the land of globals everywhere, which is what modules are intended to help you avoid, because the global namespace is crowded and conflicts are easy to run into.
You could create globals in db.js, load it from file.js, and have anotherFile.js and anotherFile2.js rely on those globals, but it's strongly discouraged. Explicit relationships between modules don't only help the browser, IDE, and bundler; they help the programmer, too.
Hello guys i have a little question about importing files into a single .js file.
Which way is better (best practice), what's the scenario that is used for:
import './file;'
import { something } from './file'
import * as evertything from './file'
Because i see that 2 and 3 are the same thing but different syntax(maybe Syntactic Sugar).
All three do different things.
import './file;'
That loads the file, and does not import anything. This is useful if you want to initialize that module (or add some external dependency, e.g. a css file if you use Webpack).
import { something } from './file'
That just imports something from the file, therefore a bundler could optimize all other dependencies away. I'd always try to go with that instead of
import * as evertything from './file'
That imports everything from that module under a namespace, and therefore makes treeshaking more difficult (the bundler cannot optimize it well). I'd only use that if you need everything from that dependency, or if that dependency is loaded externally nevertheless (e.g. import * as React from "react").
I guess the following MDN documentation will make you clear about those things:
import - JavaScript|MDN
As far as I know, 1st method is used when you have only one default export. 2nd is used when you have multiple default exports but you don't want all of them to load and want only few of them. 3rd is the case when you want everything under a single object (which can be used similar to namespace in other programming languages).
This is a problem I faced more than one. Here is an example file structure:
app.js
folder
-- index.js
-- module1.js
-- module2.js
From app.js, the entry point of my application, I require folder/index.js. This file itself is just a loader who requires all the other files in the directory. module1.js and module2.js both define some methods I want to use eventually. They are never directly required by app.js since index.js takes care of that and adds common helper utilities and applies some transformations to these modules.
All works well if I then use some methods defined in those files from app.js after I required them. But the problem comes when a method defined in module2.js wants to use a method defined in method1.js (or vice-versa). Sometimes it will work, sometimes not (in folders with multiple files I didn't get to find out when precisely it works and when it doesn't yet).
If, from module2.js I require ./index.js and then use methods in it defined by module1.js, I sometimes get Cannot read property 'myMethod' of undefined. I assume it has to do with the order the modules are required by index.js, but I can't seem to find a solution to this common problem (other than duplicating code or not using methods of these other modules).
Is there a common solution to this problem? I tried doing something like this :
var modules = require(./index.js);
exports.myMethod = function() {
if(!modules.module1 || !modules.module1.myOtherMethod) {
modules = require('./index.js');
}
modules.module1.myOtherMethod();
};
But it doesn't to do anything, modules.module1 is still undefined.
It just sounds like module should require module2. That's the solution to module1 needing to call methods in module2.
If you're worried about many calls to require, don't. That's standard practice in every programming language I've used (in particular look at the import statements in any Java program. Java is verbose, I know, but this is correct practice.)
It's definitely bad practice to look at code in one module and have no idea where anything comes from, because the require statements are missing.
You have a circular dependency problem. Try moving some of the common functions to a third file and have module1 and module2 require it, or make sure that one of them requires the other in one way only.
Never ever require a file that requires the current file back.