This is the 15th day of my participation in the August More Text Challenge. For details, see:August is more challenging

Modularity is the separation of a complex application into several separate files according to the specification. These files accomplish common or similar logic, and integrate with the outside by exposing some data or calling methods

This makes it easier for us developers to develop and maintain code, especially as the projects get bigger and the complexity of the code increases, increasing the need for modularity

The main characteristics of modularization are: reusability, composability, independence and centralization

So what problems can using modularity help us solve?

  • Resolved name conflicts: because each module is independent, there is no conflict with the same variable or function name
  • Improved maintainability: Code maintenance is facilitated because each file has a single responsibility
  • Performance tuning: Asynchronously loading modules can be very good for page performance
  • Module versioning: Modules can be versioned through aliases and other configuration, combined with build tools
  • Sharing modules across environments: Modules can be shared across servers and browsers with the NodeJS version of sea-.js

At present, the mainstream of the front-end modular standards are:

  • CommonJS
  • AMD
  • CMD
  • UMD
  • ES6

The following is a detailed summary

CommonJS

Node uses the CommonJS modularity specification

The specification is such that each file is a module, with its own scope, variables, methods, etc., and is invisible to other files, which is also an indication of independence

Inside each module, there is a Module object, representing the current module, which is used to export the API in the current module. Modules have several properties:

  • exports: is an external interface. When a module is loaded, the module is loadedmodule.exportsattribute
  • loaded: returns a Boolean value indicating whether the module has been loaded
  • parent: Returns an object representing the module that called it
  • children: returns an array indicating that this module is used by a collection of other modules
  • filename: The file name of the module with an absolute path
  • id: The module identifier, which is usually the module file name with an absolute path

CommonJS specification features:

  • Each file is a separate module with a separate scope and does not pollute the global space
  • Files can be repeatedly referenced and loaded. The first load is cached, and subsequent references are read directly from the cache
  • When a module is loaded, module.exports exports a copy of the value. Once the value is exported, any changes in the module will not affect the value already exported

Here’s how it works:

export

module.exports.foo = function(){... }// Output only oneOr you can output more than oneexports.a = 1
exports.foo = function(){... }Copy the code

Exports can be interpreted as a reference to module.exports, so the result is the same. Ruan Yifeng once said that if it is difficult to distinguish between exports, it is good to give up exports and only use Module. exports

The import

const foo = require("./xxx") // If the file name suffix is not written, the file will be automatically searched in the order of.js,.json, and.node
Copy the code

The loading process is as follows:

  • Look it up in the cache, and then load it
  • If the cache does not have a global module, it checks whether it is a global module, and if it is, it loads it directly
  • Not just check whether the module path has the file, if there is, parse the path and locate the file, and then perform the load
  • If none of the above is true, you recursively look up the current path until you reach the root directory node_modules

AMD

The CommonJS specification loads modules synchronously, and only when the load is complete can the subsequent operations be performed. AMD loads modules asynchronously, and you can specify callback functions

Since Node.js runs on a server, all files are stored on a local hard disk and there is no need to request asynchronous loading. However, in the browser environment, you need to request the module file from the server, so synchronous loading is not appropriate, so there is a fully browser-compliant ADM specification, which is implemented in require.js

It is used by defining code as a module with a global function define and loading the module with the require method

Define takes three parameters

  • The first is the module name, or you can leave it blank. The default is the file name
  • The second argument must be an array that defines the list of modules that the module depends on
  • The third argument is the function or object that the module initializes to execute. If it is a function, it is executed only once. If it is an object, it should be the output value of the module

Watch a chestnut

define("myModule"["require"."exports"."beta"].function(require.exports, beta){
    exports.foo = function(){
        return beat.foo()
    }
})
Copy the code

Create a module called myModule that relies on the require, exports, and beta modules and exports the foo function

export

module.exports = { ... }
Copy the code

The import

const foo = require("./xxx")
Copy the code

CMD

The CMD specification integrates the features of the CommonJS and AMD specifications mentioned above. The implementation of the CMD specification is sea-.js

The CMD specification is characterized by lazy loading. Instead of declaring dependencies when a module is defined, dependencies can be loaded dynamically when the module is executed, and modules can be loaded synchronously and asynchronously

The main differences between CMD and AMD are:

  • AMD needs to load modules asynchronously, while CMD can load them synchronously (require), or asynchronously (require.sync)
  • CMD follows the dependency proximity principle, AMD follows the dependency preposition principle. In AMD, you need to declare all dependencies in the dependency array, whereas in CMD, you only need to require modules in the specific code logic

It uses a global function define, similar to require.js, which takes only one argument, either a function or an object. If it is an object, the module exports an object. If it is a function, the function is passed three arguments

define( function(require.exports.module){... })Copy the code

The three parameters are:

  • require: can refer to other modules or call other modules asynchronously with require.async
  • expxort: is an object. When defining a module, you need to add attributes to export the API using the export parameter
  • module: is an object that has three upper properties
    • Uri: The complete URI path of the module
    • Dependencies: Indicates the module dependency
    • Exports: Apis that modules need to be exported

Watch a chestnut

define( function(require.export.module){
    const add = require("math").add
    exports.increment = function(val){
        return add(val, 1)}module.id = "increment"
})
Copy the code

You define a module called Increment, refer to the Add method in the Math module, and export the increment function after processing

UMD

UMD has no specific specification, but rather a combination of the three specifications mentioned above, allowing us to choose the right module specification in the right context

For example, in node.js environment, the CommonJS module specification is used. If AMD is supported in the browser, the AMD module specification is used. If not, it is exported as a global function

Look at the implementation code

(function(root, factory){
    if(typeof define === "function" && define.amd){
        define(["xxx"], factory)
    }else if(typeof exports= = ="object") {module.exports = factory( require("xxx"))}else{
        root.returnExports = factory( root.xxx )
    }
}(this.($) = > {
    return{... }}))Copy the code

Here’s how it works

  • Determine whether the module is supported by AMD. If it is, load the module in AMD mode
  • If yes, use the node.js module format. If yes, use the node.js module format
  • If neither of the first two exists, expose the module to global, window or global

ES6 modular

CommonJS and AMD both determine dependencies at run time, i.e. load at run time. CommonJS loads copies, while ES6 Module determines dependencies at compile time, and all loads are references. The benefit of this is that static analysis and type checking can be performed

ES6 Module CommonJS

  • ES6 ModuleIs a reference to a Module, that is, ES6 Module is a dynamic reference, the output is a reference to the value, change the value in the original Module will also change the value of the reference;CommonJSIs a copy of a module. Changing the value of the original module does not affect the referenced value
  • ES6 Module“This” points to undefined;CommonJS“This” refers to the module itself
  • ES6 ModuleDependencies are determined at compile time, interfaces are generated and output. CommonJS loads modules at run time
  • ES6 ModuleYou can load a method separately;CommonJSIs to load the entire module
  • ES6 ModuleCannot be reassigned, an error will be reported.CommonJSYou can reassign (change this to)

ES6 Module usage, take a look at the code

export

// Mode 1 can output multiple outputs
export const a = 1
export function foo(){}

// Only one output can be produced in mode 2
const a = 1
function foo(){}
export default {
    a, 
    foo 
}

/ / note
export { a as b }  // as means to rename a variable. The renamed variable can be exposed multiple times
Copy the code

In addition, export Default will export the default output. Those who use VUE should be familiar with it, but they do not need to know the name of the output in the module, so they can customize the name when importing

/ / export
export default function(){... }/ / introduction
import "./xxx"So it's just loading, no output, and it can't be called// The following can be used
import funName1 from "./xxx"
import { funName1 as foo } from "./xxx"
import { funName1, funName2 } from "./xxx"

// Loading the entire module ignores the default output
import * as myModule from "./xxx"
/ / use
myModule.a
myModule.foo()

// Module inheritance
import * from "./xxx" // By introducing another module in the current module, we inherit all the exported modules from XXX
Copy the code

Use ES modularity in the browser

Simply add the type=”module” attribute to the script tag, which is supported in newer versions of browsers. If not, add the Nomodule attribute to implement other solutions

<script type="module">
    import module1 from "./xxx"
</script>
<script nomodule>
    alert("Your browser does not support ES module, please upgrade the browser version first")
</script>
Copy the code

Use ES modularity in Node.js

Node.js supports ES modules since version 9.0

You can use –experimental-modules when you need to start the script, and the file must have a.mjs suffix

node --experimental-modules module1.mjs

/ / use
import module1 from "./xxx"
Copy the code

Alternatively, you can install babel-cli and babel-preset-env, configure the. Babelrc file, and run the command

./node_modules/.bin/babel-node
Copy the code

or

npx babel-node
Copy the code

other

Webpack itself maintains a module system that is compatible with almost every module specification in the history of the front end, including all of the modularity mentioned above

conclusion

Praise support, hand stay fragrance, and have glory yan

Thank you for seeing this!