Modular development

Time:2021-12-28

Modular development is one of the most important front-end development paradigms

Modular evolution process

  • Stage1 file division method
    The specific method is to put each function and its related status data into different files separately. It is agreed that each file is an independent module. Using a module is to introduce the module into the page, and then directly call the members (variables / functions) in the module
    The disadvantages are obvious:

    • Global scope contaminated
    • Naming conflict problem
    • Unable to manage module dependencies

      Modular development

      Modular development

  • Stage2 namespace mode
    Each module only exposes one global object, and all module members are mounted to this object. The specific method is to wrap each module into a global object on the basis of the first stage, which is a bit similar to adding a namespace to the members in the module
    The concept of [namespace] reduces the possibility of naming conflicts, but similarly, there is no private space, all module members can be accessed or modified outside the module, and there is no way to manage the dependencies between modules

    Modular development

Modular development

  • Stage3 Iife executes the function expression immediately
    Each module member is placed in the private scope provided by a function. The members that need to be exposed to the outside are implemented by hanging on the global object. With the concept of private member, private members can only be accessed in the form of closures within module members

    Modular development

    If you need to expose to external members, you can use this to mount to the global scope
  • Stage4 modular evolution
    Use Iife parameter action dependency declaration. The specific method is to transfer module dependencies by using the parameters of immediate execution function on the basis of the third stage, so as to make the relationship between each module more obvious
    Modular development

    For example, if you use jQuery, you use the immediate call function to accept the parameters of jQuery

    The above is the modular landing method in the early stage without tools and specifications

The emergence of modular specifications

The required contents are:
Modular Standard + module loader

Commonjs specification (specification in node.js)

  • A file is a module
  • Each module has a separate scope
  • Through module Exports export members
  • Load the module through the require function

Commonjs loads modules in synchronous mode
It will inevitably lead to inefficiency in browsers

AMD(Asynchronous Module Definition)

Asynchronous module definition specification

require.js

require. JS implements the AMD specification and is also a powerful module loader

Modular development

require. JS defines a module. The first parameter is the name of the module. The second parameter is an array, which declares the dependencies of the module. The third parameter is a function. The parameters in the function correspond to the dependencies one by one. Each item is the member exported by the dependency. The function is to provide a private space for the current module. If it is necessary to export some members to the outside, You can do this through return

Modular development

Automatically load a module, which is only used to load the module. Other parameters are similar to define

At present, most third-party libraries support amd specification

  • AMD is relatively complex to use
  • Module JS file requests are frequent and inefficient

Sea.js+CMD

Modular development

These previous knowledge is also a very important link at present

Modular standard specifications (modular best practices)

  • In the node environment, the commonjs specification will be adopted
  • In the browser environment, a specification called es modules is adopted
    Modular development

    Nowadays, most browsers already support es modules, so the learning of ES modules has become the top priority

ES Modules

  • By adding the attribute of type = module in script, you can execute the JS code in the ES module standard brick
    <script type="module">
        console.log("this is ES modules")
    </script>
  • ESM will automatically adopt strict mode, ignoring use strict
    (in non strict mode, this refers to the window object)
    <script type="module">
        console.log(this)
    </script>
  • Each es module runs in a separate private scope (the second printed foo will report an error undefined)

    <script type="module">
        var foo = 100
        console.log(foo)
    </script>

    <script type="module">
        console.log(foo)
    </script>
  • In ESM, external JS modules are requested through CORS
  • The script tag of ESM delays the execution of the script
    (delay loading the script, render the elements on the page first, and the general script tag will not render the elements until the script is loaded)
    This small feature is the same as the defer attribute of the script tag
<script type= "module" src="demo.js"></script>
    <p>What to display</p>

Es modules import and export

  • You can export variables, functions, classes, etc
export var name = 'foo module'

export function hello(){
    console.log("foo hello")
}

export class Person{

}
  • It can also be exported uniformly, such as:
export { name , hello , Person}
  • In another module, the JS file is to be imported
import { hello, name } from './module.js'
console.log(name)
hello()

rename

 var name = 'foo module'

 function hello(){
    console.log("foo hello")
}

 class Person{

}

export { 
    name as fooName,
    hello as fooHello,
    Person as fooPerson
}

After renaming, you should also pay attention to the name change when importing

import { fooHello, fooName } from './module.js'
console.log(fooName)
fooHello()

Rename special case

Set the export member name to default, and this member will be set as the default export member of the current module. It must be renamed during import

export { 
    name as default,
    hello as fooHello,
    Person as fooPerson
}

Rename default to call

import { fooHello, default as fooName } from './module.js'

ESM special handling for default

Set the name variable as the default export

export default name;

When importing, you can accept the default exported members by directly importing + variable name, and the variable name is optional

//Fooname can be named at will here
import fooName from './module.js'

Considerations for ESM import and export

  • The parentheses followed by export are not literal quantities, but fixed syntax
  • When importing, those members are shared memory space and have exactly the same reference relationship
  • Imported members are read-only

ESM import usage

  • When importing, the from keyword is followed by a string. The internal content path must have a complete file name, and the JS suffix cannot be omitted, which is completely opposite to commonjs
  • You can also use the complete URL to load the module, that is, you can use the module above the CDN to load the module completely
  • If you only execute the functions of a module and do not extract the members in the module, you can keep the flower brackets empty or directly import the string. This feature is very useful when we import some sub function modules that do not need external control
import {} from './module.js'
import './module.js'
  • There are many members that need to be exported. They will be used when importing. You can extract them all with * and store them all in the object with the as keyword
import * as mod from './module.js'
console.log(mod)
Modular development

  • Dynamic import
import('./module.js').then(function (module) {
  console.log(module)
})
  • Default and explicit members are exported at the same time
var name = 'jack'
var age = 18

export { name, age }

console.log('module action')

export default 'default export'

import abc, { name, age } from './module.js'
console.log(name, age, abc)

ESM direct export import members

  • The specific method is to change the import keyword to export. All imported members will be exported members of the current module. These members can no longer be accessed under the current scope.
    It is generally used for index files to organize scattered modules together and export them for external use

avatar.js:

export var Avatar = 'Avatar Component'

button.js:

var Button = 'Button Component'
export default Button

index.js:

export { default as Button } from './button.js'
export { Avatar } from './avatar.js'

app. JS (import):

import { Button, Avatar } from './components/index.js'

console.log(Button)
console.log(Avatar)

Avatar and button are exposed components, index JS imports and exports these two components as a bridge

ESM in browser (ployfill compatibility scheme)

  • Let the browser support the vast features of ESM
  • The module name is browser ESM loader

https://github.com/ModuleLoader/browser-es-module-loader

For modules under NPM, you can get all JS files through upkg the CDN service of this website

https://unpkg.com/+Module name under NPM

Modular development

such as

https://unpkg.com/[email protected]/dist/browser-es-module-loader.js

/Dist / indicates the file in the directory

Modular development

Copy the corresponding path and import the browser address with script tag
  • Introduce promise and ployfill required by ie
 <script nomodule src="https://unpkg.com/[email protected]/dist/polyfill.min.js"></script>
  <script nomodule src="https://unpkg.com/[email protected]/dist/babel-browser-build.js"></script>
  <script nomodule src="https://unpkg.com/[email protected]/dist/browser-es-module-loader.js"></script>
  • Nomodule attribute
    It solves the problem that browsers supporting Polyfill do not load the resources in the tag

ESM in Node.js

  • To directly use ESM in a node, you need to do the following:
    • First, change the file extension from JS changed to mjs;

      Modular development

    • Second, it needs to be added at startup--experimental-modulesParameters;

      Modular development

  • You can also load native modules with ESM
//// at this time, we can also load the built-in module through ESM
import fs from 'fs'
fs.writeFileSync('./foo.txt', 'es module working')
  • You can also directly extract the members in the module. The built-in module is compatible with the method of extracting members in ESM
import { writeFileSync } from 'fs'
writeFileSync('./bar.txt', 'es module working')
  • Third party NPM modules can also be loaded through ESM
    (for example, the third-party module lodash)
import _ from 'lodash'
_.camelCase('ES Module')
  • However, you cannot load members of third-party modules in the form of parentheses in ESM
//// not supported, because all third-party modules are exported as default members
import { camelCase } from 'lodash'
console.log(camelCase('ES Module'))

ESM in Node. Interaction between JS and commonjs module

  • The commonjs module always exports only one default member
  • Common JS module can be imported in ESM
  • Members cannot be extracted directly. Import is not a deconstructed export object
  • It is also not possible to load ESM through require in commonjs

    Modular development

ESM in Node. Differences between JS and commonjs

Before that, it is recommended to use the nodemon tool, which can monitor the changes of MJS files and give error messages
First use NPM for global installation, and then use

Modular development

  • There is no module global member in ESM
  • Require, module and exports can be replaced by import and export
  • __ Filename and__ Dirname is obtained through the meta attribute of the import object
const currentUrl = import.meta.url
console.log(currentUrl)
  • Convert to a path through the fileurltopath method of the URL module
import { fileURLToPath } from 'url'
import { dirname } from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
console.log(__filename)
console.log(__dirname)

The new version of node supports ESM even more

  • Package. In the new version JSON adds the type attribute to indicate module, and all JS files can be supported by ESM by default
  • If you need to continue using commonjs with type = module, you need to modify the file extension to cjs

Babel compatibility scheme

  • In earlier node versions, you can use Babel for ESM compatibility
  • The mainstream JavaScript compiler can compile the code of new features into the code supported by the current environment
    A series of Babel dependencies need to be installed
yarn add @babel/node @babel/core @babel/core @babel/preset-env --dev
  • Detect Babel command:

    Modular development

  • Install plug-ins
yarn add @babel/plugin-transform-commonjs --dev
  • Build a Babelrc file
{
  "plugins": [
    "@babel/plugin-transform-modules-commonjs"
  ]
}

  • Run file
 yarn babel-node .\index.js