前端模块化方式总结

本文旨在对前端模块化方式 CommonJS, AMD, CMD, UMD, ES6 Module 的大致使用方式、解决的问题以及特性进行归纳总结,方便以后查阅。

VanillaJs

IIFE & 闭包

// people.js
var people = (function() {
  let name = "Sophia";
  let age = 12;

  return {
    getName: () => name,
    getAge: () => age
  };
})();

// invoke.js
console.log(people.getName());
console.log(people.getAge());

有了 namespace 的概念,外部无法覆盖 nameage,但是 people 依然是全局变量。

改良

// people.js
(function(window) {
  let name = "Sophia";
  let age = 12;

  return {
    getName: () => name,
    getAge: () => age,
    say: () => say(name)
  };
})(window, say);

// say.js
(function(window) {
  window.say = (name) => {
    console.log(`Hello ${name}.`)
  }
})(window);
<script src="./dance.js"></script>
<script src="./people.js"></script>
<script>
  people.say();
</script>

有了依赖的概念,但是依然没有解决全局变量的问题,而且模块引入顺序必须要靠人工维护。

CommonJS

主要用于 NodeJs,浏览器原生不支持,可通过 browserifywebpack 打包后在浏览器上跑。

// people.js
const { say } = require("./say.js");

let name = "Sophia";
let age = 12;

module.exports = {
  getName: () => name,
  getAge: () => age,
  say: () => say(name)
};

// say.js
module.exports = {
  say: (name) => {
    console.log(`Hello ${name}.`);
  }
}

// invoke.js
const people = require("./people.js");
people.say();

特性

  • 以文件为单元模块
  • 同步加载模块
  • 模块可以被加载多次,但只会在第一次执行,而后读取的是之前运行的缓存。
  • 模块加载顺序和代码中的顺序一致
  • 导出的值是拷贝

AMD / RequireJs

AMD (Asynchronous Module Definition) 顾名思义,主要解决模块的异步加载的问题。

// people.js
define(['./say.js'], function({ say }) {
  let name = "Sophia";
  let age = 12;

  return {
    getName: () => name,
    getAge: () => age,
    say: () => say(name)
  };
});

特性

  • 异步加载
  • 依赖前置

CMD / SeaJs

CMD (Common Module Definition) 通用模块定义,最早由阿里的大神玉伯提出。

// people.js
define(function (requie, exports, module) {
  const { say } = require('./say.js');

  let name = "Sophia";
  let age = 12;

  module.exports = {
    getName: () => name,
    getAge: () => age,
    say: () => say(name)
  };
});

特性

  • 相对自然的依赖声明风格
  • 依赖就近 (RequireJs 2.0+ 也加入此特性)

UMD

UMD (Universal module definition) 是 AMDCommonJS 的综合的产物。AMD 适用于浏览器,CommonJS 用于 NodeJs 服务端,为了解决模块跨平台问题,衍生出 UMD 规范。其本质就是通过 if/else 判断全局变量,然后决定如何加载。

(function (window, factory) {
    if (typeof exports === 'object') {
        module.exports = factory();
    } elseif (typeof define === 'function' && define.amd) {
        define(factory);
    } else {
        window.eventUtil = factory();
    }
})(this, function () {
    //module ...
});

ES6 Module

// people.js
import { say } from "./say.js"

let name = "Sophia";
let age = 12;

export default {
  getName: () => name,
  getAge: () => age,
  say: () => say(name)
};

特性

  • 模块输出是值的引用,而 CommonJS 是值的拷贝
  • 静态语法,只能卸载顶层,而 CommonJS 是动态语法,可以写在判断里
  • CommonJS 一样,模块只会被加载一次,不同的是,import 指向模块的引用,而 CommonJS 加载的时候就会执行该脚本,然后内存中生成一个对象,而后只会返回会缓存的结果
  • thisundefined,而 CommonJS 是指向当前模块
  • 无论模块内是否声明 use strict,都采用严格模式