Home  >  Article  >  Web Front-end  >  A detailed introduction to Javascript modularity

A detailed introduction to Javascript modularity

不言
不言Original
2018-09-05 11:55:341558browse

This article brings you a detailed introduction to Javascript modularization. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

Preface

With the vigorous development of Web technology and the increasingly perfect dependent infrastructure, the front-end field has gradually expanded from the browser to the server (Node.js), desktop (PC, Android , iOS), and even Internet of Things devices (IoT), JavaScript carries the core part of these applications. As their scale and complexity increase exponentially, their software engineering systems are also established (collaborative development, Unit testing, requirements and defect management, etc.), the need for modular programming is increasingly urgent.

JavaScript's support for modular programming has not yet been standardized, and it is difficult to bear this important task; for a time, knights in the world stepped forward and overcame all obstacles, transitioning from slash-and-burn farming to future-oriented modular solutions;

Concept

Modular programming is to realize functions by combining some __relatively independent and reusable modules__. The two core parts are __define module__ and __introduce module__;

  • When defining a module, the execution logic inside each module is not perceived by the outside. Only export (expose) some methods and data;

  • When introducing a module, load the code to be introduced synchronously/asynchronously, execute and obtain its exposed methods and data;

Slash and Burn

Although JavaScript does not provide a modular solution at the language level, it can use its __object-oriented__ language features, plus __design patterns_ _Blessing can realize some simple modular architecture; a classic case is to use the singleton mode to achieve modularization, which can better encapsulate the module and only expose part of the information to the places where the module needs to be used;

// Define a module
var moduleA = (function ($, doc) {
  var methodA = function() {};
  var dataA = {};
  return {
    methodA: methodA,
    dataA: dataA
  };
})(jQuery, document);

// Use a module
var result = moduleA.mehodA();

Intuitively, declaring dependencies and exporting data through immediate execution functions (IIFE) is not hugely different from the current modular solution, but it is essentially different and has some important features that cannot be met. ;

  • When defining a module, the declared dependencies are not forced to be automatically introduced, that is, the dependent module code must be manually introduced before defining the module;

  • When a module is defined, its code has already completed the execution process and cannot be loaded on demand;

  • When using a module across files, the module needs to be mounted to a global variable (window) Previous;

AMD & CMD Divide the World

Digression: Due to the passage of time, these two modular solutions have gradually faded out of the stage of history, and the specific characteristics will not be discussed in detail;

In order to solve the remaining needs of the "slash-and-burn" era, the AMD and CMD modular specifications came out to solve the need for asynchronous modular programming on the browser side. The core principle is to asynchronously load scripts dynamically and event listening. Loading modules; __

The two most representative works of AMD and CMD correspond to require.js and sea.js respectively; the main difference lies in the timing of dependency declaration and dependency loading, among which require.js defaults to the moment of declaration. For execution, sea.js advocates lazy loading and on-demand use; it is also worth mentioning that the writing method of the CMD specification is very similar to CommonJS, and can be used in CommonJS with only slight modifications. It is more helpful to refer to the following Case for understanding;

// AMD
define(['./a','./b'], function (moduleA, moduleB) {
  // 依赖前置
  moduleA.mehodA();
  console.log(moduleB.dataB);
  // 导出数据
  return {};
});
 
// CMD
define(function (requie, exports, module) {
  // 依赖就近
  var moduleA = require('./a');
  moduleA.mehodA();     

  // 按需加载
  if (needModuleB) {
    var moduleB = requie('./b');
    moduleB.methodB();
  }
  // 导出数据
  exports = {};
});

CommonJS

In 2009, Ty released the first version of Node.js. As one of the core features, CommonJS is suitable for services. End-to-end scenarios; after years of inspection and baptism of time, as well as full support for front-end engineering, CommonJS is widely used in Node.js and browsers;

// Core Module
const cp = require('child_process');
// Npm Module
const axios = require('axios');
// Custom Module
const foo = require('./foo');

module.exports = { axios };
exports.foo = foo;

Specification

  • module (Object): the module itself

  • exports (*): the exported part of the module, that is, the exposed content

  • require (Function): Function to load the module and obtain the export value of the target module (the basic type is copy, the reference type is shallow copy), you can load built-in modules, npm modules and custom modules

Implementation

1. Module definition

By default, any .node .js .json file is a module that conforms to the specification;

2. Import module

First read the module from the cache (require.cache) first. If the cache is not hit, perform path analysis and then process it according to different types of modules:

  • Built-in modules, directly Load from memory;

  • For external modules, first perform file addressing and positioning, then compile and execute, and finally obtain the corresponding export value;

During the compilation process, Node wraps the content of the obtained JavaScript file head and tail. The results are as follows:

(function (exports, require, module, __filename, __dirname) {
    var circle = require('./circle.js');
    console.log('The area of a circle of radius 4 is ' + circle.area(4));
});

Feature summary

  • Synchronous execution module declaration and Introducing logic, you need to pay attention when analyzing some complex dependency references (such as circular dependencies);

  • The caching mechanism has better performance and limits memory usage;

  • Module module is highly flexible for transformation and can meet some customization requirements (such as hot update, module support for any file type);

ES Module(推荐使用)

ES Module 是语言层面的模块化方案,由 ES 2015 提出,其规范与 CommonJS 比之 ,导出的值都可以看成是一个具备多个属性或者方法的对象,可以实现互相兼容;但写法上 ES Module 更简洁,与 Python 接近;

import fs from 'fs';
import color from 'color';
import service, { getArticles } from '../service'; 

export default service;
export const getArticles = getArticles;

主要差异在于:

  • ES Module 会对静态代码分析,即在代码编译时进行模块的加载,在运行时之前就已经确定了依赖关系(可解决循环引用的问题);

  • ES Module 关键字:import export 以及独有的 default  关键字,确定默认的导出值;

  • ES Module 中导入模块的属性或者方法是强绑定的,包括基础类型;

UMD

通过一层自执行函数来兼容各种模块化规范的写法,兼容 AMD / CMD / CommonJS 等模块化规范,贴上代码胜过千言万语,需要特别注意的是 ES Module 由于会对静态代码进行分析,故这种运行时的方案无法使用,此时通过 CommonJS 进行兼容;

(function (global, factory) {
  if (typeof exports === 'object') {   
    module.exports = factory();
  } else if (typeof define === 'function' && define.amd) {
    define(factory);
  } else {
    this.eventUtil = factory();
  }
})(this, function (exports) {
  // Define Module
  Object.defineProperty(exports, "__esModule", {
    value: true
  });
  exports.default = 42;
});

构建工具中的实现

为了在浏览器环境中运行模块化的代码,需要借助一些模块化打包的工具进行打包( 以 webpack 为例),定义了项目入口之后,会先快速地进行依赖的分析,然后将所有依赖的模块转换成浏览器兼容的对应模块化规范的实现;

模块化的基础

从上面的介绍中,我们已经对其规范和实现有了一定的了解;在浏览器中,要实现 CommonJS 规范,只需要实现 module / exports / require / global 这几个属性,由于浏览器中是无法访问文件系统的,因此 require 过程中的文件定位需要改造为加载对应的 JS 片段(webpack 采用的方式为通过函数传参实现依赖的引入)。具体实现可以参考:tiny-browser-require。

webpack 打包出来的代码快照如下,注意看注释中的时序;

(function (modules) {
  // The module cache
  var installedModules = {};
  // The require function
  function __webpack_require__(moduleId) {}
  return __webpack_require__(0); // ---> 0
})
({
  0: function (module, exports, __webpack_require__) {
    // Define module A
    var moduleB = __webpack_require__(1); // ---> 1
  },
  1: function (module, exports, __webpack_require__) {
    // Define module B
    exports = {}; // ---> 2
  }
});

实际上,ES Module 的处理同 CommonJS 相差无几,只是在定义模块和引入模块时会去处理 __esModule 标识,从而兼容其在语法上的差异。

异步和扩展

1、浏览器环境下,网络资源受到较大的限制,因此打包出来的文件如果体积巨大,对页面性能的损耗极大,因此需要对构建的目标文件进行拆分,同时模块也需要支持动态加载;

webpack 提供了两个方法 require.ensure() 和 import() (推荐使用)进行模块的动态加载,至于其中的原理,跟上面提及的 AMD & CMD 所见略同,import() 执行后返回一个 Promise 对象,其中所做的工作无非也是动态新增 script 标签,然后通过 onload / onerror 事件进一步处理。

2、由于 require 函数是完全自定义的,我们可以在模块化中实现更多的特性,比如通过修改 require.resolve 或 Module._extensions 扩展支持的文件类型,使得 css / .jsx / .vue / 图片等文件也能为模块化所使用;

附录:特性一览表

模块化规范 加载方式 加载时机 运行环境 备注
AMD 异步 运行时 浏览器
CMD 异步 运行时 浏览器
CommonJS 同步/异步 运行时 浏览器 / Node
ES Module 同步/异步 编译阶段 浏览器 / Node 通过 import() 实现异步加载

相关推荐:

javascript模块化编程(转载),javascript模块化

JavaScript模块化思想

The above is the detailed content of A detailed introduction to Javascript modularity. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn