一览js模块化:从CommonJS到ES6

  一览js模块化:从CommonJS到ES6
  
  模块化是指把一个复杂的系统分解到一个一个的模块。
  
  模块化开发的优点:
  
  (1)代码复用,让我们更方便地进行代码管理、同时也便于后面代码的修改和维护。
  
  (2)一个单独的文件就是一个模块,是一个单独的作用域,只向外暴露特定的变量和函数。这样可以避免污染全局变量,减少变量命名冲突。
  
  js模块化规范有:CommonJS、AMD、CMD、ES6的模块系统。本文将依次介绍下每个规范。
  
  0.早期:用script来引入js模块
  
  
  
  
  
  
  
  
  
  缺点:
  
  (1)加载的时候会停止渲染网页,引入的js文件越多,网页失去响应的时间越长;
  
  (2)会污染全局变量;
  
  (3)js文件之间存在依赖关系,加载是有顺序的,依赖性最大的要放到最后去加载;当项目规模较大时,依赖关系变得错综复杂。
  
  (4)要引入的js文件太多,不美观,代码难以管理。
  
  1.CommonJS 规范
  
  是服务器端模块的规范,由nodejs推广使用。该规范的核心思想是:允许模块通过require方法来同步加载所要依赖的其他模块,然后通过 exports 或module.exports 来导出需要暴露的接口。
  
  CommonJS 还可以细分为 CommonJS1 和 CommonJS2,区别在于 CommonJS1 只能通过 exports.xx = xx 的方式导出,CommonJS2 在 CommonJS1 的基础上加入了module.exports = xx 的导出方式。 CommonJS 通常指 CommonJS2。
  
  采用CommonJS 规范导入导出:
  
  // 导出
  
  module.exports = moduleA.someFunc;
  
  // 导入
  
  const moduleA = require('./moduleA');
  
  实例:
  
  //math.js
  
  var num = 0;
  
  function add(a, b) {
  
  return a + b;
  
  }
  
  module.exports = {
  
  //需要向外暴露的变量、函数
  
  num: num,
  
  add: add
  
  }
  
  可以这样加载:
  
  //引入自定义的模块时,参数包含路径,可省略.js
  
  //引入核心模块时,不需要带路径,如var http = require("http");
  
  var math = require('./math');
  
  math.add(1, 2)//3
  
  实际上,从上面的例子就可以看出,math.add(1,2)必须要等待math.js加载完成,即require是同步的。
  
  在服务器端,模块文件保存在本地磁盘,等待时间就是磁盘的读取时间。但对于浏览器而言,由于模块都放在服务器端,等待时间取决于网上的快慢。因此更合理的方案是异步加载模块。
  
  缺点:
  
  (1)不能并行加载模块,会阻塞浏览器加载;
  
  (2)代码无法直接运行在浏览器环境下,必须通过工具转换成标准的 ES5;
  
  2.AMD和require.js
  
  AMD:异步模块定义。上面已经介绍过,CommonJS是服务器端模块的规范,主要是为了JS在后端的表现制定的,不太适合前端。而AMD就是要为前端JS的表现制定规范。由于不是JavaScript原生支持,使用AMD规范进行页面开发需要用到对应的库函数,也就是require.js(还有个js库:curl.js)。实际上AMD 是 require.js在推广过程中对模块定义的规范化的产出。
  
  AMD采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
  
  require.js也采用require()语句加载模块,但是不同于CommonJS:
  
  // 定义一个模块
  
  define('module', ['dep'], function (dep) {
  
  return exports;
  
  });
  
  // 导入和使用
  
  require(['module'], function (module) {
  
  });
  
  上面示例中的代码改写成AMD形式:
  
  math.js定义一个模块:
  
  define('math', ['jquery'], function (jquery) {//引入jQuery模块
  
  return {
  
  add: function (x, y) {
  
  return x + y;
  
  }
  
  };
  
  });
  
  导入和使用:
  
  require(['math'], function (math) {
  
  math.add(1, 2)
  
  })
  
  math.add()与加载math模块不是同步的,不会阻塞浏览器的加载。
  
  3.CMD和sea.js
  
  CMD:通用模块定义。
  
  国内的玉伯大佬写了sea.js,实际上CMD就是 sea.js在推广过程中对模块定义的规范化的产出。
  
  define(function (require, exports, module) {
  
  // 模块代码
  
  });
  
  说明:
  
  require:可以把其他模块导入进来的一个参数;
  
  exports:可以把模块内的一些属性和方法导出的;
  
  module: 是一个对象,上面存储了与当前模块相关联的一些属性和方法。
  
  上面示例中的代码改写成AMD形式:
  
  define(function (require, exports, module) {
  
  var add = function (a, b) {
  
  return a + b;
  
  }
  
  exports.add = add;
  
  })
  
  //导入和使用
  
  seajs.use(['math.js'], function (math) {
  
  var sum = math.add(1, 2);
  
  });
  
  CMD与AMD的不同的在于:
  
  (1)AMD推崇依赖前置;CMD推崇依赖就近,只有在用到某个模块的时候再去require:
  
  //AMD推崇的依赖关系前置:在定义模块时就要声明要依赖的模块
  
  define(['a', 'b', 'c', 'd'], function (a, b, c, d) { // 依赖必须一开始就写好
  
  a.doSomething()
  
  // 此处省略100行
  
  ...
  
  b.doSomething()
  
  ...
  
  })
  
  //CMD推崇依赖就近,按需加载,只有在用到某个模块时再去require
  
  define(function (require, exports, modules) {
  
  var a = require('a');
  
  a.doSomething();
  
  // 此处省略100行
  
  ...
  
  var b = require("b");//按需加载
  
  b.doSomething();
  
  ...
  
  })
  
  (2)AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。
  
  对于依赖的模块,AMD是提前执行,CMD是延迟执行。
  
  具体细节可点击参考
  
  4.ES6模块化
  
  ES6在语言的层面上实现了模块化。浏览器厂商和 Node.js 都宣布要原生支持该规范。它将逐渐取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
  
  在 ES6 中,使用export关键字来导出模块,使用import关键字引用模块。但是浏览器还没有完全兼容,需要使用babel转换成ES5。
  
  // 导出
  
  export function hello() { };
  
  package com.serial.tool;
  
  import java.io.ByteArrayInputStream;
  
  import java.io.ByteArrayOutputStream;
  
  import java.io.IOException;
  
  import java.io.ObjectInputStream;
  
  import java.io.ObjectOutputStream;
  
  public class SerializeObjectTool {
  
  //序列化
  
  public static byte[] serialize(Object obj) {
  
  ObjectOutputStream obi = null;
  
  ByteArrayOutputStream www.ysyl157.com  bai = null;
  
  try {
  
  bai = new ByteArrayOutputStream();
  
  obi = new ObjectOutputStream(bai);
  
  obi.writeObject(obj);
  
  byte[] byt = bai.toByteArray();
  
  return byt;
  
  } catch (IOException e) {
  
  e.printStackTrace();
  
  }
  
  return null;
  
  }
  
  // 反序列化
  
  public static Object unserizlize(byte[] byt) {
  
  ObjectInputStream oii = null;
  
  ByteArrayInputStream bis www.taohuaqing178.com = null;
  
  bis = new ByteArrayInputStream(byt);
  
  try {
  
  oii = new ObjectInputStream(www.gouyiflb.cn bis);
  
  Object obj = oii.readObject();
  
  return obj;
  
  } catch (Exception e) {
  
  e.printStackTrace();
  
  }
  
  return null;
  
  }
  
  }
  
  测试代码
  
  package com.redis.client;
  
  import com.obj.bean.Person;
  
  import com.serial.www.dfgjpt.com/ tool.SerializeObjectTool;
  
  import redis.clients.jedis.Jedis;
  
  public class RedisObjectClient www.gouyiflb.cn{
  
  public static void main(String[] args) {
  
  // TODO Auto-generated method stub
  
  Jedis redis = new Jedis(www.thd178.com"10.190.130.143", 6379);
  
  export default {
  
  // ...
  
  };
  
  // 导入
  
  import { readFile } from 'fs';
  
  import React from 'react';
  
  使用import导入模块时,需要知道要加载的变量名或函数名。
  
  在ES6中还提供了export default,为模块指定默认输出.对应导入模块import时,不需要使用大括号。
  
  上面示例中的代码改写成ES6形式:
  
  //math.js
  
  var num = 0;
  
  var add = function (a, b) {
  
  return a + b;
  
  };
  
  export { num, add };
  
  //导入
  
  import { num, add } from './math';
  
  function test(ele) {
  
  ele.textContent = add(1 + num);
  
  }
  
  缺点
  
  浏览器还没有完全兼容,必须通过工具转换成标准的 ES5 后才能正常运行。
  
  5.小结
  
  本文从script引入js文件讲起,到服务器端模块的规范CommonJs,再到推崇依赖前置的浏览器端模块的规范AMD、推崇依赖就近的浏览器端模块的规范CMD,最后介绍了ES6的模块化。每个介绍中都有各规范基本的用法和一个示例。如有问题,欢迎指正。

你可能感兴趣的:(一览js模块化:从CommonJS到ES6)