Notes - JS modularization

historical background

JS itself simple page design: page animation + form submission
There is no modular or namespace concept
With the increasing complexity of front-end pages, the previous methods can not meet the requirements
Therefore, JS has an increasing demand for modularity

development history

Infancy: no modularization

  1. Pages need js with different functions - animation, form, formatting
  2. Various js files are divided into different files
  3. Different files are referenced by the same template
	<script src="jquery.js"></script>
	<script src="main.js"></script>
	<script src="dep1.js"></script>

Advantages: File separation can play the role of module reuse and better maintenance.
Disadvantages: it will pollute the global scope = > it is not conducive to the development of large projects and the cooperation of multi person teams.

Growth: prototype of modularization - IIFE (optimization of syntax side)

It mainly increases the control of the scope
example:

	// Common practice - pollution global scope
	let count = 0;
	const increase = () => ++count;
	const reset = () => { count = 0; }
	
	increase();
	reset();

	// Utilizing IIFE
	const iifeModule = (() => {
		let count = 0;

		return {
			increase: () => ++count,
			reset: () => {
				count = 0
			}
		}
	})();

	iifeModule.increase();
	iifeModule.reset();

	// Optimize IIFE above - inject other dependencies
	const iifeModule = ((dependencyModule1, dependencyModule2) => {
		let count = 0;

		return {
			increase: () => ++count,
			reset: () => {
				count = 0
			}
		}
	})(dependencyModule1, dependencyModule2);

	iifeModule.increase();
	iifeModule.reset();

	// Imitate the dependency processing and module loading of early jquery
	const iifeModule = ((dependencyModule1, dependencyModule2) => {
	  	let count = 0;
	  	const increase = () => ++count;
	  	const reset = () => {
	    	count = 0;
	  	}
	
		// In fact, the disclosure mode is used
	  	return {
	    	increase, reset
	  	}
	})(dependencyModule1, dependencyModule2);
	
	iifeModule.increase();
	iifeModule.reset();

Maturity: CJS - CommonJs

By node JS development

features:

  • Expose the interface through module + exports
  • Call other modules through require

Module organization

	// main.js file
	const dependencyModule1 = require('./dependencyModule1');
	const dependencyModule2 = require('./dependencyModule2');

	// Treatment method
	let count = 0;
	const increase = () => ++count;
	const reset = () => {
		count = 0;
	}
	// Do something related to introducing dependencies

	// Exposed interface
	// 1
	exports.increase = increase;
	exports.reset = reset;
	// 2
	module.exports = {
		increase,reset
	}

Module usage

	const { increase, reset } = require('./main.js');

	increase();
	reset();

Actual execution processing - bottom layer

	(function(thisValue, exports, require, module) {
		const dependencyModule1 = require('./dependencyModule1');
		const dependencyModule2 = require('./dependencyModule2');

		// Business logic
	}).call(thisValue, exports, require, module)
  • Advantages: CommonJs is the first to be implemented on the server side, and solves the problems of dependency and global variable pollution from the framework level
  • Disadvantages: it mainly aims at the solution of the server, and is not so friendly to the processing and integration of asynchronous pull dependency

AMD specification

Allow the formulation of callback functions through asynchronous loading (classic implementation framework: require.js)

New definition method:

	// Define a module through define, and then load it with require
	/*
	* define
	* params: Module name, dependent module, factory method
	**/
	define(id, [depends], callback);
	require([module], callback);

Module definition method:

	define('amdModule', [dependencyModule1, dependencyModule2], (dependencyModule1, 
	dependencyModule2) => {
		// Business logic
		// processing section 
		let count = 0;
		const increase = () => ++count;
		const reset = () => {
			count = 0;
		}

		return {
			increase, reset
		}
	})

Lead in module:

	require(['amdModule'], amdModule => {
		amdModule.increase();
	})

Existing code compatible with CMJ in ADMmodule

	define('amdModule', [], require => {
		// Introduction part
	    const dependencyModule1 = require(./dependencyModule1);
	    const dependencyModule2 = require(./dependencyModule2);
	
	    // processing section 
	    let count = 0;
	    const increase = () => ++count;
	    const reset = () => {
	      count = 0;
	    }
	    // Do something related to introducing dependencies
	
	    return {
	      increase, reset
	    }
	})

Using revealing in AMD

	define('amdModule', [], (require, export, module) => {
    // Introduction part
    const dependencyModule1 = require(./dependencyModule1);
    const dependencyModule2 = require(./dependencyModule2);

    // processing section 
    let count = 0;
    const increase = () => ++count;
    const reset = () => {
      count = 0;
    }
    // Do something related to introducing dependencies

    export.increase = increase();
    export.reset = reset();
  })

  define('amdModule', [], require => {
    const otherModule = require('amdModule');
    otherModule.increase();
    otherModule.reset();
  })

Compatible with AMD & CMJ / how to judge CJS and AMD
Emergence of UMD

	(define('amdModule', [], (require, export, module) => {
	    // Introduction part
	    const dependencyModule1 = require(./dependencyModule1);
	    const dependencyModule2 = require(./dependencyModule2);
	
	    // processing section 
	    let count = 0;
	    const increase = () => ++count;
	    const reset = () => {
	      count = 0;
	    }
	    // Do something related to introducing dependencies
	
	    export.increase = increase();
	    export.reset = reset();
  	}))(
	    // The goal is to differentiate CommonJSorAMD at one time
	    typeof module === "object"
	    && module.exports
	    && typeof define !== "function"
	      ? // It's CJS
	        factory => module.exports = factory(require, exports, module)
	      : // It's AMD
	        define
	  )
  • Advantages: it is suitable for loading asynchronous modules in the browser, and multiple modules can be loaded in parallel
  • Disadvantages: there will be an introduction cost, and it cannot be loaded on demand

CMD specification

Load the main application framework sea js

	define('module', (require, exports, module) => {
	    let $ = require('jquery');
	    // jquery related logic
	
	    let dependencyModule1 = require('./dependecyModule1');
	    // dependencyModule1 related logic
	  })
  • Advantages: load on demand and rely on nearby
  • Disadvantages: depending on packaging, the loading logic exists in each module and expands the volume of the module

ES6 modularization

New definition:
import Keyword - import
Export Keyword - export

	// Introduction area
	import dependencyModule1 from './dependencyModule1.js'
	import dependencyModule2 from './dependencyModule2.js'

	// Implement code logic
	let count = 0;
	export const increase = () => ++count;
	export const reset = () => {
	    count = 0;
	}

	// Export area
	export default {
		increase,reset
	}

	// Module introduction
	<script type="module" src="esModule.js"></script>

Dynamic module - Inspection: export promise

ES11 native solution:

  import('./esModule.js').then(dynamicEsModule => {
    dynamicEsModule.increase();
  })
  • Advantages (importance): it integrates js modules in the most unified form
  • Disadvantages (limitations): it is essentially runtime dependency analysis

(important) a new idea to solve modularization - front end Engineering

background

Fundamental problem - the modular approach of the front end depends on runtime analysis
Solution - offline execution: gulp webpack

	<!doctype html>
	    <script src="main.js"></script>
	    <script>
	      // Give the build tool an identification bit
	      require.config(__FRAME_CONFIG__);
	    </script>
	    <script>
	      require(['a', 'e'], () => {
	        // Business processing
	      })
	    </script>
	  </html>
  define('a', () => {
    let b = require('b');
    let c = require('c');

    export.run = () {
      // run
    }
  })

Engineering realization

step1: scan dependency table

  {
    a: ['b', 'c'],
    b: ['d'],
    e: []
  }

Step 2: regenerate the dependent data template

  <!doctype html>
    <script src="main.js"></script>
    <script>
      // Build tool generates data
      require.config({
        "deps": {
          a: ['b', 'c'],
          b: ['d'],
          e: []
        }
      })
    </script>
    <script>
      require(['a', 'e'], () => {
        // Business processing
      })
    </script>
  </html>

step3: an execution tool, which adopts a modular solution to solve the modular processing dependency

  define('a', ['b', 'c'], () => {
    // Execute code
    export.run = () => {}
  })
  • advantage:
  1. The configuration is generated at build time and executed at run time
  2. Finally, it is transformed into execution processing dependency
  3. Can expand

Full body webpack centered engineering + mvvm framework componentization + design pattern

Keywords: Javascript Front-end ECMAScript

Added by nsr500rossi on Sat, 15 Jan 2022 22:27:35 +0200