JS笔记 - 模块化

JavaScript 模块指的是一段可复用的,独立的代码。他们通常都有特定的功能,能在整个代码系统里被引进或删除。

模块通常是独立的——与其他代码解耦,因此方便修改。这也提高了代码的可读性和可维护性。模块化在使部分代码保持私有,仅暴露公共部分的的同时,还解决了命名空间模糊性的问题。

CommonJS

CommonJS 规范需要使用两个关键字requireexports

require:用来声明导入某个模块,可以传入具体的模块路径也可以传入模块名。当 require 某个模块名时,Nodejs 就会在 node_modules 文件夹中查找对应的模块;

exports:用来声明当前 js 文件要导出的内容。

//------ store/customer.js 文件------
exports = function(){
    return customers.get('store');
}
                         
//------ payments.js 文件------
var customerStore = require('store/customer');

Nodejs 遵守了 CommonJS 的规范,但是它使用的是module.exports

//store/customer.js 文件
function customerStore(){
    return customers.get('store');
}
modules.exports = customerStore;

缺点:

  1. 只支持同步加载,不支持异步,必须等待加载完所有文件后才能运行。
  2. 只有对象能被导出(函数也是特殊的对象),即无法导出变量/常量。
  3. CommonJS 规范不能直接在浏览器的js环境下使用 ( 必须使用 Webpack 等工具转译处理)

注意事项:

  1. require第一次加载某个模块时,Node会缓存该模块。以后再加载该模块,就直接从缓存取出该模块。

    //a.js
    module.exports = {
      name: '123',
      age: 27
    }
    
    //b.js
    let obj1 = require('./a.js')
    
    console.log(obj1) // {name:'123',age:27}
    obj1.name = 'abc'
    
    let obj2 = require('./a.js')
    console.log(obj2) // {name:'abc',age:27}
    console.log(obj1) // {name:'abc',age:27}
    
  2. CommonJS模块的加载机制是,一旦输出一个值,模块内部的变化就影响不到这个值,改造了一下上面的例子,可以看到调用模块内部的 change 方法时并不会改变输出的值。

    //a.js
    var name = "123";
    function change() {
      name = "456";
    }
    
    module.exports = {
      name: name,
      age: 27,
      change: change
    };
    
    //b.js
    let obj1 = require("./a.js");
    
    console.log(obj1); // {name:'123',age:27}
    obj1.change();
    
    let obj2 = require("./a.js");
    console.log(obj2); // {name:'123',age:27}
    console.log(obj1); // {name:'123',age:27}
    

AMD

AMD 解决了 CommonJS 无法直接在浏览器上使用的问题,并且支持了异步加载。

AMD 的设计初衷就是给浏览器环境使用的,可以加快页面启动时间,而且这些模块导出内容可以是对象、函数、构造器、字符串、JSON等等。支持多模块多文件。

requirejs 实现了 AMD 的规范,它使用requiredefined两个 API

// index.js 
// 调用模块
require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
  // some code here
  // 可以在这里编写模块加载后的代码
});
// require()函数接受两个参数:
// 第一个参数是一个数组,表示所依赖的模块['moduleA', 'moduleB', 'moduleC']
// 第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用
// 注意['moduleA', 'moduleB', 'moduleC']这里面的三个模块与index.js在同一个目录

// moduleA.js
// 创建模块
define(function (){
  var add = function (x,y){
    return x+y;
  };
  return {
    add: add
  };
});
// index.js
require(['moduleA'], function (moduleA){
  console.log(moduleA)
  //moduleA就是moduleA.js模块传入的函数执行后返回的对象{add:function}
});

ES6

ES6 模块分为两部分:export 和 import

export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值

此外还提供了按需加载的功能,能够动态引入模块

button.addEventListener('click', event => {
  import('./dialogBox.js')
  .then(dialogBox => {
    dialogBox.open();
  })
  .catch(error => {
    /* Error handling */
  })
});

ES6 和 CommonJS 之间的区别:

  1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用
  2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
  3. CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段
作者

BiteByte

发布于

2023-01-12

更新于

2024-11-15

许可协议