前端模块化规范

110 阅读2分钟

早期模块化落地实现:

  1. 文件划分方式
  2. 命名空间方式
  3. 立即执行函数 IIFE 方式
  4. 利用自执行函数参数作为依赖声明使用
(function($) {
  const name = 'module-a';
  function foo() {
    $('body').animate(....);
  };
})(JQuery)

模块化规范实现:

  • AMD: (实现:require.js),主要用于浏览器端,异步加载模块
//定义没有依赖的模块
define(function(){
   return 模块
})

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

// 使用模块
require(['module1', 'module2'], function(m1, m2){
   使用m1/m2
})
  • commonjs: (实现:node)主要用于服务器端, 同步加载机制, 值缓存(不适用浏览器端,页面效率低下) 一个文件就是一个模块 每个模块都有单独的作用域 通过 module.exports 导出成员 通过 require 函数载入模块 多次加载取第一次加载运行的结果
// moduleA.js
const arr = [1,2,3]
module.exports = {
  msg: "moduleA",
  arr,
  foo() {
    arr.push(4);
  },
};

// moduleB.js
let moduleA = require("./moduleA");  // 保留moduleA的拷贝
moduleA.foo()
console.log(moduleA.arr)  // [1,2,3,4]
  • CMD: 实现(sea.js), 整合了commonjs、amd规范的特点, 主要用于浏览器端, 异步加载
define('moduleA', ['jquery'], function(require, exports, module) {
  // 异步加载
  require.async('./module3', function (m3) {
	m3.doSomething()
  })
  //暴露模块
  exports.xxx = xxxxx
});
  • UMD: 通用模块定义, 兼容AMD和commonJS的规范;根据所处环境使用不同的规范,node环境使用common.js规范,浏览器环境使用AMD规范

  • ESModule: (es6实现), 静态化,编译时确定模块依赖关系, 浏览器、服务器通用

// 指定默认输出, 一个模块仅包含一个默认输出
export default xxx
import foo from xxxx
------------------------
// 输出多个变量
export const a = 1;
export const b = { a: 1 };
export const c = [1, 23];
// 等价于
export { a, b, c }   // 动态绑定,可获取实时值
import { a, b, c } from xxxxx   // 静态解析阶段,生成只读引用
// 等价于
import * as foo from xxxxx

特别地:import命令编译时静态解析,动态加载使用import()函数实现(es2020提案)

es6 module加载实现

---- 浏览器加载
<script type="module"></script>

---- node.js加载
// ./node_modules/es-module-package/package.json
{
	type: 'module',  // 以es6模块加载, 否则以commonjs模块加载
	main: 'xxxx',  // 入口文件
	"exports": {   // 子目录别名
      "./submodule": "./src/submodule.js"
    }
}

// 加载模块
import { something } from 'es-module-package';
import submodule from 'es-module-package/submodule';

ES6 模块与 CommonJS 模块的差异

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口

CommonJS 加载的是一个对象, ES6 模块不是对象, 通过export 向外暴露接口, 它的对外接口只是一种静态定义, 在代码静态解析阶段就会生成; ES 模块允许进行静态分析,从而有助于tree-shaking和作用域提升之类的优化工作,同时提供了一些其他的高级特性)

参考链接: 前端模块化四大规范