模块化的意义

将代码拆分成独立的块,而后再把这些块应用模块模式连接起来实现不同的性能。

就像小时候玩的拼图一样,不同的拼图组合在一起就能够拼成任意的形态。

这种模式的背地思维也很简略:把逻辑分块、各自封装,互相独立,同时自行决定引入执行那些内部模块以及裸露本身的那些模块。

这个根本的思维是所有的 JavaScript 模块零碎的根底。

文中代码案例地址:https://github.com/AnsonZnl/JS-Modules-Sample

模块化的益处

  • 防止命名抵触(缩小命名空间净化)
  • 更好的拆散, 按需加载
  • 更高复用性
  • 高可维护性

JS 中常见的模块

IIFE 模式:匿名函数自调用(闭包)

次要利用在浏览器端。

利用闭包的原理发明一个独有的函数作用域来保留公有变量,达到模块化的成果。

应用

HTML

<script type="text/javascript" src="module.js"></script><script type="text/javascript">  console.log(myModule.get()); // output-data(获取外部数据)  myModule.set("new data"); // 设置外部数据  console.log(myModule.data); //output-undefined (不能拜访模块外部数据)  myModule.data = "xxxx"; //不是批改的模块外部的data  console.log(myModule.get()); //output-new data 批改后的值</script>

JS

// module.js文件(function(window) {  let data = "data";  //获取数据  function get() {    return data;  }  // 批改数据  function set(val) {    data = val;  }  //裸露行为  window.myModule = {    get,    set,  };})(window);

CommonJS

次要利用在服务端,如果在浏览器端运行须要借助其余工具(Browserify)。

裸露模块: module.exports = value或者exports.xx = value(exports 是一个导出的对象)

引入模块: require(xx),如果是第三方模块,xxx 为模块名,如果为自定义模块,xxx 为模块的文件门路。

特点

  • 所有代码都运行在模块作用域,不会净化全局作用域。
  • 模块能够屡次加载,然而只会在第一次加载时运行一次,而后运行后果就被缓存了,当前再加载,就间接读取缓存后果。要想让模块再次运行,必须革除缓存。
  • 模块加载的程序,依照其在代码中呈现的程序。

应用
在 Node 中 装置 uniq 函数。

npm initnpm install uniq --save
// module.jslet arr = [1, 2, 2, 3, 3];module.exports = {  arr,};
// app.jslet module1 = require("./module.js");let uniq = require("uniq");console.log(uniq(module1.arr)); // [1,2,3]

AMD

全称是 Asynchronous Module Definition - 异步模块定义

和 CommonJS 不同的是 AMD 采纳非同步的形式来加载模块。

根本语法

定义裸露模块

// 定义没有依赖的模块define(function() {  return 模块;});// 定义有依赖的模块define(["module1", "module2"], function(m1, m2) {  return 模块;});

引入应用模块

require(["module1", "module2"], function(m1, m2) {  应用m1 和 m2;});

应用案例

<!-- index.html --><body>  <!-- 引入require.js并指定js主文件的入口 -->  <script    data-main="main"    src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.js"  ></script></body>
// main.js(function() {  require(["module.js"], function(module) {    let currentUrl = module.getUrl();    alert("以后页面的URl:" + currentUrl);  });})();
// module.js// 定义模块define(function() {  let url = window.location.href;  function getUrl() {    return url.toUpperCase();  }  // 裸露模块  return {    getUrl,  };});

更多的应用办法请参考:https://requirejs.org/

CMD

CMD---是 SeaJS 在推广过程中对模块定义的规范化产出,是一个同步模块定义,是 SeaJS 的一个规范,SeaJS 是 CMD 概念的一个实现,SeaJS 是淘宝团队提供的一个模块开发的 JS 框架。

什么时候用到什么时候引入,即用即返回,这是一个同步概念。

特点: CMD 是 AMD 在根底上改良的一种标准,和 AMD 不同在于依赖模块的执行机制不同,CMD 是就近依赖,而 AMD 是前置依赖。

环境: 浏览器环境

语法:

  • 导入:define(function(require, exports, module){})
  • 导出:define(function(){return '值'})

应用

// main.jsdefine(function(require, exports, module) {  var moduleA = require("./module.js");  alert(moduleA.a); // 打印出:hello world});// module.jsdefine(function(require, exports, module) {  exports.a = "hello world";});
<body>  <script    data-main="main"    src="https://cdn.bootcdn.net/ajax/libs/require.js/2.3.6/require.js"  ></script></body>

Sea.js 用法请参考:https://seajs.github.io/seajs...

UMD

全称 Universal Module Definition 看名字就晓得,特点是兼容 AMD 和 CommonJS 标准,而且兼容全局引入。

环境: 服务器环境和浏览器端

UMD 实现原理很简略:

  • 先判断是否反对 AMD(define 是否存在),存在则应用 AMD 形式加载模块;
  • 再判断是否反对 Node.js 模块格局(exports 是否存在),存在则应用 Node.js 模块格局;
  • 前两个都不存在,则将模块公开到全局(window 或 global)

应用

(function(root, factory) {  if (typeof define === "function" && define.amd) {    //AMD    define(["jquery"], factory);  } else if (typeof exports === "object") {    //Node, CommonJS之类的    module.exports = factory(require("jquery"));  } else {    //浏览器全局变量(root 即 window)    root.returnExports = factory(root.jQuery);  }})(this, function($) {  //办法  function myFuncA() {} // 公有办法,因为没有返回  function myFuncB() {} // 公共办法,因为返回了  //裸露公共办法  return {    myFuncB,  };});

大家平时引入的 jQuery 的 CND 就是 UMD 的,源码能够查看:https://cdn.bootcdn.net/ajax/...

ES6 Module

在 ES6 之前,模块化次要是社区在推动进行的,从而呈现了 CommonJS 和 AMD 两个,前者用于服务器后者用于浏览器,ES6 模块的呈现将齐全代替 CommonJS 和 AMD 标准,成为浏览器和服务器通用的解决方案。

ES6 模块的设计思维是尽量的动态化,使得编译时就能确定模块的依赖关系,以及输出和输入的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些货色。比方,CommonJS 模块就是对象,输出时必须查找对象属性。

特点

  • 按需加载(编译时加载)
  • import 和 export 命令只能在模块的顶层,不能在代码块之中(如:if 语句中),import()语句能够在代码块中实现异步动静按需动静加载

环境: 服务器环境和浏览器端

语法:

  • 导入:import {modules1,modules1,} from '模块门路'
  • 导出:export或者export default
  • 动静导入:import('模块门路').then(..)
    <!-- 揺树(tree-shaking) -->

应用

Node 中 先装置 Babel:

npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/nodenpm install --save @babel/polyfill# 而后运行npx babel-node main.js
// modules/double.jslet mes = "Hello Modules for double";function sum(value) {  return `${mes} - ${value * 2}`;}export default {  mes,  sum,};// main.jsimport module from "./modules/double";console.log(module.sum(10)); // Hello Modules for double - 20

浏览器中

区别

  • 和 CommonJS 的区别:

    • CommonJS 模块输入的是一个值得拷贝,ES6 模块输入的是值的援用
    • CommonJS 模块是运行时加载,ES6 模块是编译时输入接口
    • CommonJS 模块的 require()是同步加载模块,ES6 模块的 import 命令是异步加载,有一个独立的模块依赖的解析阶段。

毛病
浏览器和服务器目前的反对不是很好,现阶段应用须要借助一些工具(Babel)。

  • 浏览器反对:在新版本的浏览器(如 Chrome)中能够应用<script type="module" src="./foo.js"></script>写法
  • 服务器反对(Node)有两种模式,别离是 ES6 模块和 CommonJS。

    • 从 Node.js v13.2 开始,默认反对 ES6 模块,然而须要采纳.mjs为后缀名、或者在package.json中批改type字段为module(举荐)
    • 应用 CommonJS 的话须要以.cjs为后缀,也能够设置package.json中批改type字段为commonjs(举荐)。

最好不要两者混用。更多的应用办法能够参考:https://es6.ruanyifeng.com/#d...

总结

  • CommonJS 标准次要用于服务端编程,加载模块是同步的,这并不适宜在浏览器环境,因为同步意味着阻塞加载,浏览器资源是异步加载的,因而有了 AMD CMD 解决方案。
  • AMD 标准在浏览器环境中异步加载模块,而且能够并行加载多个模块。不过,AMD 标准开发成本高,代码的浏览和书写比拟艰难,模块定义形式的语义不顺畅。
  • CMD 标准与 AMD 标准很类似,都用于浏览器编程,依赖就近,提早执行,能够很容易在 Node.js 中运行。不过,依赖 SPM 打包,模块的加载逻辑并重
  • ES6 在语言规范的层面上,实现了模块性能,而且实现得相当简略,齐全能够取代 CommonJS 和 AMD 标准,成为浏览器和服务器通用的模块解决方案。

参考

  • 前端模块化详解
  • JS 模块
  • javascript 中 UMD 标准介绍
  • ES6 Modules
  • 一篇了解前端模块化:AMD、CMD、CommonJS、ES6