You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The curly braces after `import` indicate the bindings to import from a given module. The keyword `from` indicates the module from which to import the given binding. The module is specified by a string representing the path to the module (called the *module specifier*). Browsers use the same path format you might pass to the `<script>` element, which means you must include a file extension. Node.js, on the other hand, follows its traditional convention of differentiating between local files and packages based on a filesystem prefix. For example, `example` would be a package and `./example.js` would be a local file. I'll discuss specific details on how browsers and Node.js load modules in the "Loading Modules" section, after finishing the basics of importing and exporting.
67
+
The curly braces after `import` indicate the bindings to import from a given module. The keyword `from` indicates the module from which to import the given binding. The module is specified by a string representing the path to the module (called the *module specifier*). Browsers use the same path format you might pass to the `<script>` element, which means you must include a file extension. Node.js, on the other hand, follows its traditional convention of differentiating between local files and packages based on a filesystem prefix. For example, `example` would be a package and `./example.js` would be a local file.
68
68
69
69
I> The list of bindings to import looks similar to a destructured object, but it isn't one.
70
70
@@ -507,181 +507,6 @@ import { second } from "example/index.js";
507
507
508
508
Each of these module specifiers cannot be loaded by the browser. The two module specifiers are in an invalid format (missing the correct beginning characters) even though both will work when used as the value of `src` in a `<script>` tag. This is an intentional difference in behavior between `<script>` and `import`.
509
509
510
-
Of course, these are just the rules for importing modules in browsers. Every JavaScript engine defines their own rules for how modules are loaded and imported. Next, I'll explain how Node.js handles modules and there are some significant differences from the way that browsers handle them.
511
-
512
-
### Using Modules in Node.js
513
-
514
-
While adding support for modules in web browsers was fairly straightforward, adding modules to Node.js was a bit more involved. From the beginning, Node.js has supported the CommonJS module system and would need to continue supporting that format for the foreseeable future. Node.js needed to support JavaScript modules importing CommonJS modules and CommonJS modules importing JavaScript modules. The end result was to create a new file extension, `.mjs`, to use instead of `.js` whenever a file contains an JavaScript module.
515
-
516
-
W> Node.js support for JavaScript modules hasn't been implemented at the time of my writing, so it's possible the details could change during implementation. Make sure to read the most current Node.js documentation about JavaScript module support.
517
-
518
-
#### Node.js Module Specifier Resolution
519
-
520
-
The `.mjs` file extension signifies to Node.js that the file should be loaded as a JavaScript module. Node.js traditionally supported the `.js` and `.json` file extensions to load JavaScript and JSON files, respectively. The `.mjs` file extension has the highest precedence amongst the three file extensions, so Node.js will always look for `.mjs` files first whenever an extension is not present in the `import` statement (you must include the `.json` file extension explicitly to load a JSON file). Consider the following example:
521
-
522
-
```js
523
-
import { sum } from"./example";
524
-
```
525
-
526
-
When Node.js sees `"./example"`, a path without an extension, it searches the following locations in order, moving on to the next location if the previous one isn't found:
527
-
528
-
1.`./example.mjs`
529
-
1.`./example.js`
530
-
1.`main` entry in `./example/package.json`
531
-
1.`./example/index.mjs`
532
-
1.`./example/index.js`
533
-
534
-
This lookup sequence allows you to place JavaScript modules and CommonJS modules in the same directory, ensuring (but not requiring) backwards compatibility for other consumers. Those using an older version of Node.js would always load the `.js` file whereas a newer version would load the`.mjs` file. The lookup sequence is particular important in the case of packages installed in the `node_modules` directory, such as:
535
-
536
-
```js
537
-
import { sum } from"example";
538
-
```
539
-
540
-
In this case, `"example"` is a package name rather than a filename. As such, the lookup sequence is slightly different:
541
-
542
-
1.`./node_modules/example.mjs`
543
-
1.`./node_modules/example.js`
544
-
1.`main` in `./node_modules/example/package.json`
545
-
1.`./node_modules/example/index.mjs`
546
-
1.`./node_modules/example/index.js`
547
-
1. Repeat this process in the parent directory
548
-
549
-
As with single files, packages can contain both `.mjs` and `.js` files to ensure compatibility with older versions of Node.js.
550
-
551
-
A> #### Module Resolution Differences Between the Browser and Node.js
552
-
A>
553
-
A> The ECMAScript 6 standard does not specify the format of module filenames in `import` statements, leaving those details up to the implementations. As such, there's a subtle but important difference in the way browsers and Node.js resolve modules. Consider the following:
554
-
A>
555
-
A> ```js
556
-
A> import { sum } from "example/sum.js";
557
-
A> ```
558
-
A>
559
-
A> While this code may look simple to understand, browsers and Node.js interpret `"example/sum.js"` differently. In a browser, `"example/"` results in an error being returned because anything other than an absolute URL must begin with `/`, `./`, or `../`. Node.js, on the other hand, loads the file as CommonJS successfully. These differences pose some interoperability concerns for those who want to write JavaScript modules that work in both the browser and Node.js. The upcoming module loader specification is intended to help solve some of these module resolution interoperability concerns.
560
-
561
-
#### Loading CommonJS Modules from JavaScript Modules
562
-
563
-
Since there are millions of existing Node.js modules written in CommonJS format, the ability for JavaScript modules to load CommonJS modules is important. As previously mentioned, an `import` statement is capable of loading from a CommonJS module, although the semantics are a bit different due to how CommonJS defines modules. Suppose that the following CommonJS module is in the file `example.js`:
564
-
565
-
```js
566
-
module.exports.sum=function(num1, num2) {
567
-
return num1 + num2;
568
-
};
569
-
```
570
-
571
-
This code exports a single function, `sum()` from a CommonJS module. To import just the `sum()` function from `example.js` in a JavaScript module, you can use the following:
572
-
573
-
```js
574
-
import { sum } from"./example";
575
-
576
-
let result =sum(1, 2);
577
-
```
578
-
579
-
The `sum()` function is imported directly from `example.js` as if the imported module were a JavaScript module. Similarly, you can import everything defined on `module.exports` by importing `*`, such as:
580
-
581
-
```js
582
-
import*asexamplefrom"./example";
583
-
584
-
let result =example.sum(1, 2);
585
-
```
586
-
587
-
Here, the local binding `example` is an object that has references to every own property on the `module.exports` object from `example.js` (`example` is not the same object as `module.exports`). That means you can access the `sum()` function as a method on `example`.
588
-
589
-
##### Importing module.exports Directly
590
-
591
-
You can access `module.exports` directly in two ways. First, you can use the single name `import` statement:
592
-
593
-
```js
594
-
importexamplefrom"./example";
595
-
596
-
let result =example.sum(1, 2);
597
-
```
598
-
599
-
In this code, `example` is the `module.exports` object as it is defined in `example.js`. You can also access `module.exports` directly by importing `default` and renaming it, such as:
600
-
601
-
```js
602
-
import { defaultasexample } from"./example";
603
-
604
-
let result =example.sum(1, 2);
605
-
```
606
-
607
-
This code is functionally equivalent to the preceding example and imports the default value from `example.js` and assigns it to the local binding `example`. Once again, `example` is equal to `module.exports` from `example.js`.
608
-
609
-
The ability to import `module.exports` directly is important in the case where `module.exports` is a function or primitive value. For example, if `example.js` is defined like this instead:
610
-
611
-
```js
612
-
module.exports=functionsum(num1, num2) {
613
-
return num1 + num2;
614
-
};
615
-
```
616
-
617
-
Since `module.exports` is a function, the result of importing using a namespace versus the default is significant:
618
-
619
-
```js
620
-
importexample1from"./example";
621
-
import*asexample2from"./example";
622
-
623
-
let result1 =example1(1, 2);
624
-
console.log(result1); // 3
625
-
626
-
// works
627
-
let result2 =example2.default(1, 2);
628
-
console.log(result2);
629
-
630
-
// throws error - example2 is not a function
631
-
let result3 =example2(1, 2);
632
-
```
633
-
634
-
The `example1` binding is directly equivalent to `module.exports` in `example.js`, so you can execute it as a function directly. The `example2` binding is a namespace object and the value of `module.exports` is always represented as the `default` property, so you can call `example2.default(1, 2)` and get a response. An error is thrown when `example2(1, 2)` is executed because `example2` is not a function.
635
-
636
-
A> ##### Limitations on import
637
-
A>
638
-
A> There is a very important distinction between the `import` statement and the `require()` function that isn't obvious from looking at code. That difference is in the search locations for resolving imported packages. The `require()` function searches for packages (such as `require("example")`) not just in `node_modules`, but also in several other nonlocal directories:
639
-
A>
640
-
A> 1. `$NODE_PATH`
641
-
A> 1. `$HOME/.node_modules`
642
-
A> 1. `$HOME/.node_libraries`
643
-
A> 1. `$PREFIX/lib/node`
644
-
A>
645
-
A> When using the `import` statement, these four locations will not be searched if a package cannot be found in `node_modules`. These special directories are still supported by `require()` for backwards compatibility.
646
-
647
-
#### Loading JavaScript Modules from CommonJS Modules
648
-
649
-
It's also possible to import JavaScript modules into CommonJS modules using `require()`. Because JavaScript modules do not specify an object to be exported, the exports must be wrapped in a namespace object. For example, suppose this is your `example.mjs` file:
650
-
651
-
```js
652
-
exportfunctionsum(num1, num2) {
653
-
return num1 + num2;
654
-
}
655
-
```
656
-
657
-
You can import the function `sum()` using `require()` to create a namespace object:
658
-
659
-
```js
660
-
let example =require("./example");
661
-
662
-
let result =example.sum(1, 2);
663
-
```
664
-
665
-
The `example` object in this code doesn't exist in `example.mjs` in any form. It's created by the `require()` call in order to represent the exported parts of the JavaScript module.
666
-
667
-
If a JavaScript module has a default export, then that value is assigned to the `default` property on the namespace object. For example, if you have this code in your `example.mjs` file:
668
-
669
-
```js
670
-
exportdefaultfunctionsum(num1, num2) {
671
-
return num1 + num2;
672
-
}
673
-
```
674
-
675
-
Then you can import this function using `require()` and accessing the `default` property on the namespace object, such as:
676
-
677
-
```js
678
-
let example =require("./example");
679
-
680
-
let result =example.default(1, 2);
681
-
```
682
-
683
-
The call to `example.default(1, 2)` calls the `sum()` function defined in `example.mjs`. The `default` property is `undefined` when a JavaScript module doesn't export a default value.
684
-
685
510
686
511
## Summary
687
512
@@ -692,5 +517,3 @@ You must export any functionality you'd like to make available to consumers of a
692
517
Modules need not export anything if they are manipulating something in the global scope. You can actually import from such a module without introducing any bindings into the module scope.
693
518
694
519
Because modules must run in a different mode, browsers introduced `<script type="module">` to signal that the source file or inline code should be executed as a module. Module files loaded with `<script type="module">` are loaded as if the `defer` attribute is applied to them. Modules are also executed in the order in which they appear in the containing document once the document is fully parsed.
695
-
696
-
Node.js takes a different approach to loading JavaScript modules, requiring you to use a filename with a `.mjs` extension instead of the traditional `.js` extension. The file extension is the signal that the file needs to be parsed differently. It's possible to use CommonJS and JavaScript modules together in a project, with each capable of loading the other format.
0 commit comments