Skip to main content

Modules and Components

28th October, 2022

Updated: 28th October, 2022

    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

    require vs import

    O7XuF

    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…

    img
    img

    ## UMD (Universal Module Definition)


    cdfb489e-41b4-419c-987b-7d6a2cfb762b

    Created on: 28th October, 2022

    Last updated: 28th October, 2022

    Tagged With: