Modules and Components
ES Modules
As of ES6 (ES2015), JavaScript supports a native module format called ES Modules, or ECMAScript Modules. This is modern way to do modules in JavaScript.
This approach uses the export
and import
keywords, instead of the older CommonJS syntax of module.exports
and require
.
To enable ES modules set the script tag type to module
.
Inline approach
<!-- index.html -->
<script type="module">
import { bar } from 'https://some-library'
import { foo } from './foo'
</script>
External approach
<!-- index.html -->
<script type="module" src="main.js"></script>
// main.js
import { bar } from 'https://dev.jspm.io/some-library'
import { foo } from './foo'
Set package type
Configure NPM to see your project as ES Modules.
package.json
{ "name": "my-app", "type": "module" }
:::danger
Warning - if you have a config or ESLint or similar which uses the old module.exports
syntax, you’ll get an error and so you’ll have to rewrite that config. VS Code provides a neat feature to do that for you.
:::
Set file extension
Use .mjs
extension in place of .js
. This is not so common, but it allows you do mix both types in a project which does not have the NPM config set to use the module type.
External
When loading a JS library from a CDN, make sure you pick a URL which is compatible with ES Modules approach. This might mean a param like ?module
or loook for .mjs
extension.
Import sources
Import local JS module
Import from a local module. Note dot slash.
import { foo, bar } from './foo'
import { buzz } from '../bazz/bizz'
Often the extension is omitted - like .js
, .ts
or .jsx
. You might have to include if .vue
.
Import an asset
Styling
Load a CSS file so that it gets added to the page. Note you don’t have to specify an object to import or how it will be used on the page.
import './index.css'
Images
Load a image as an object so you can use it as a URL in your content.
Based on React Quickstart.
import logo from './logo.svg'
export default function App() {
return (
<div className="App">
<img src={logo} />
</div>
)
}
Import a 3rd-party package
From Node modules
Import from a package installed with NPM.
import * as vscode from 'vscode'
import * as assert from 'assert'
import React from 'react'
Local modules
In an NPM project, you can configure your project to use ES Modules as follows.
{
"type": "module"
}
In testing, I found that my ES Module imports gave an error without this config value setup.
Named exports
Some ways to export with a name.
function foo() {
console.log('Hello')
}
export { foo }
alternative.
export function foo() {
console.log('Hello')
}
Usage:
import { foo } from './foo'
// Multiple.
import { foo, fuzz } from './foo'
// Rename.
import { foo as fizz } from './foo'
Or, exporting a module (without importing it first).
export { foo } from './foo'
Import multiple objects and put them under a namespace.
import * as foo from './foo'
foo.bar()
foo.buzz()
Default exports
function foo() {
console.log('Hello')
}
export default foo
Usage:
import foo from './foo'
Combine multiple defaults
Normally you could export another module as default, but this will be invalid for two modules.
export React from 'https://dev.jspm.io/react'
export ReactDOMServer from 'https://dev.jspm.io/react-dom/server'
// Equivalent to:
import React from 'https://dev.jspm.io/react'
import ReactDOMServer from 'https://dev.jspm.io/react-dom/server'
export default React
export default ReactDOMServer
Here is an alternative. So instead of assigning a name directly, import as unpacking the default
object and then assign it a name.
export { default as React } from "https://dev.jspm.io/react";
export { default as ReactDOMServer } from "https://dev.jspm.io/react-dom/server";
// Equivalent to:
import React from "https://dev.jspm.io/react";
import ReactDOMServer from "https://dev.jspm.io/react-dom/server";
export {
React
ReactDOMServer
}
Now you can import one or both modules from your module.
import { React, ReactDOMServer } from './deps.ts'
Mixing named and default imports
Intended:
// Default.
import React from './deps.ts'
// Named.
import { Application } from './deps.ts'
Combined:
import React, { Application } from './deps.ts'
Nomodule
In case a browser does not support ES Modules, you can prompt the user to upgrade.
Example - based on esm.sh homepage source.
<script module>
// ...
</script>
<script nomodule>
const mainEl = document.querySelector('main')
mainEl.innerHTML =
'<p><em style="color: #999;">nomodule, please upgrade your browser...</em></p>'
</script>
Unlike function scopes, module scopes have a way of making their variables available to other modules as well. They can say explicitly which of the variables, classes, or functions in the module should be available.
When something is made available to other modules, it’s called an export. Once you have an export, other modules can explicitly say that they depend on that variable, class or function.
Because this is an explicit relationship, you can tell which modules will break if you remove another one.
Once you have the ability to export and import variables between modules, it makes it a lot easier to break up your code into small chunks that can work independently of each other. Then you can combine and recombine these chunks, kind of like Lego blocks, to create all different kinds of applications from the same set of modules.
Since modules are so useful, there have been multiple attempts to add module functionality to JavaScript. Today there are two module systems that are actively being used.
- CommonJS (CJS) is what Node.js has used historically.
- ESM (EcmaScript modules) is a newer system which has been added to the JavaScript specification. Browsers already support ES modules, and Node is adding support.
How ES modules work
When you’re developing with modules, you build up a graph of dependencies. The connections between different dependencies come from any import statements that you use.
These import statements are how the browser or Node knows exactly what code it needs to load. You give it a file to use as an entry point to the graph. From there it just follows any of the import statements to find the rest of the code.
But files themselves aren’t something that the browser can use. It needs to parse all of these files to turn them into data structures called Module Records. That way, it actually knows what’s going on in the file.
After that, the module record needs to be turned into a module instance. An instance combines two things: the code and state.
- State is the actual values of the variables at any point in time.
Links
- ES modules: A cartoon deep-dive - Mozilla Hacks - the Web developer blog
- ES Modules | Dev Cheatsheets
require vs import
Apart from that, You can't selectively load only the pieces you need with require but with import, you can selectively load only the pieces you need, which can save memory. Loading is synchronous(step by step) for require on the other hand import can be asynchronous(without waiting for previous import) so it /can perform a little better than/ require.
Biggest difference that affects code is that exports in CommonJS modules are “computed”, whereas exports in an ESM module are static (pre-defined). JS can determine the exports in an ESM module after only parsing the code (not yet running it). In a commonJS module, the exports are only known when the module actually runs and you see what is assigned to module.exports when the module initialization code finishes running. This difference alone creates compatibility headaches in trying to make a single module work for both ESM and CommonJS.
The major difference between require and import, is that require will automatically scan node_modules to find modules, but import, which comes from ES6, won’t. Most people use babel to compile import and export, which makes import act the same as require.
An import statement can reference an ES module or a CommonJS module. import statements are permitted only in ES modules, but dynamic import() expressions are supported in CommonJS for loading ES modules.
When importing CommonJS modules, the module.exports object is provided as the default export. Named exports may be available, provided by static analysis as a convenience for better ecosystem compatibility.
The major difference in commonjs and ES module is of synchronous and asynchronous nature>> -commonjs modules are synchronous, this is'nt an issue in case of small module execution but in case of large modules it can be delay to process. while, -loading and parsing of ES modules is asynchronous, this is the difference where may be the performance gets vary..
How can I use CommonJS?
CommonJS wraps each module in a function called ‘require’, and includes an object called ‘module.exports’, which exports code for availability to be required by other modules. All you have to do is add whatever you want accessible to other files onto the ‘exports’ object and require the module in the dependent file. the syntax for the require function is *“var VariableName = require(‘moduleId_or_pathToModule’);”*. Here’s an example…
## UMD (Universal Module Definition)
Created on: 28th October, 2022
Last updated: 28th October, 2022
Tagged With: