Leecason

vuePress-theme-reco Leecason    2018 - 2020
Leecason Leecason

Choose mode

  • dark
  • auto
  • light
主页
分类
  • CSS
  • FrontEnd
  • GraphQL
  • JavaScript
  • TypeScript
  • Vue
  • Webpack
  • 其它
  • 数据结构与算法
  • 浏览器相关
标签
时间线
GitHub
author-avatar

Leecason

80

Article

61

Tag

主页
分类
  • CSS
  • FrontEnd
  • GraphQL
  • JavaScript
  • TypeScript
  • Vue
  • Webpack
  • 其它
  • 数据结构与算法
  • 浏览器相关
标签
时间线
GitHub

前端模块化

vuePress-theme-reco Leecason    2018 - 2020

前端模块化

Leecason 2020-03-22 模块化IIFEAMDCMDCommonJSES6
  1. 什么是模块
  2. 模块化的好处
  3. IIFE、AMD、CMD、CommonJS、ES Module
  4. CommonJS 与 ES Module 区别
  5. 总结

# 什么是模块

  • 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件),并进行组合在一起
  • 块的内部数据与实现是私有的,只是向外部暴露一些接口(方法)与外部其它模块通信

# 模块化的好处

模块化可以带来以下好处:

1.解决命名冲突,减少命名空间污染 2.更好的分离,按需加载 3.更高复用性 4.提高代码可维护性

# 立即执行函数(IIFE)

通过函数作用域解决了命名冲突,污染全局作用域的问题。

(function (globalVars) {
  globalVars.test = function () {
    // 声明各种变量,都不会污染全局作用域
  }
})(globalVars);
1
2
3
4
5

# AMD

异步加载模块,允许指定回调函数。

浏览器环境,要从服务端加载模块,就必须采用非同步模式,因此浏览器端一般采用 AMD 规范

AMD 规范比 CommonJS 规范在浏览器端的实现要早

// AMD
define(['./a', './b'], function (a, b) {
  // 加载模块完毕可使用
  a.do();
  b.do();
})
1
2
3
4
5
6

# CMD

专门用于浏览器端,异步加载模块,模块使用时才会加载执行。

整合了 AMD 和 CommonJS 的特点。

// CMD
define(function (require, exports, module) {
  // 加载模块
  // 可以把 require 写在函数体任何地方实现延迟加载
  var a = require('./a');
  a.do();
});
1
2
3
4
5
6
7

# CommonJS

最初被应用于 Nodejs,成为 Nodejs 的模块规范。目前也在广泛使用,目前在 Node 中的模块管理已经和 CommonJS 有一些区别了。

// a.js
module.exports = {
  a: 1,
};

// 或者
exports.a = 1;

// b.js
var module = require('./a.js');
console.log(module.a) // 1
1
2
3
4
5
6
7
8
9
10
11
// 其实是包装了一层 IIFE
// module 是 Node 独有的一个变量
module.exports = {
  a: 1,
};
1
2
3
4
5
// module 的基本实现
var module = {
  id: 'xxx', // 去寻找模块
  exports: {}, // exports 就是个空对象
};

var exports = module.exports; // exports 和 module.exports 用法相似的原因

var load = function (module) {
  // 导出的东西
  var a = 1;
  module.exports = a;
  return module.exports;
};

// 然后 require 时找 id,然后将使用的东西用 IIFE 包装下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

虽然 exports 和 module.exports 用法相同,但是不能对 exports 直接赋值,虽然 exports 和 module.exports 是同一个内存地址,但是对 exports 直接赋值就会导致两者不再指向同一个地址。

# ES Module

原生实现的模块化方案,设计思想是尽量静态化,使得编译时就能确定依赖关系,以及输入和输出变量。AMD 和 CommonJS 都只能在运行时确定这些对象, CommonJS 模块就是对象,输入时必须查找对象属性。

但目前浏览器对 ES6 Module 兼容还不太好,我们平时在 Webpack 中使用的 export 和 import,会经过 Babel 转换为 CommonJS 规范。

# CommonJS 与 ES Module 区别

  1. CommonJS 是动态语法支持动态导入,require(${path}/xx.js),可以写在判断里。ES Module 静态语法只能写在顶层,但目前已有提案。
  2. CommonJS 在导出的是值的拷贝,ES Module 导出的是值的引用,导入值会跟随导出值变化。
  3. CommonJS 是同步导入,因为用于服务端,文件都在本地,同步导入卡住主线程影响也不大。ES Module 是异步导入,因为用于浏览器,需要下载文件,采用同步会对渲染有大影响。
  4. CommonJs 的 this 是当前模块,ES Module的 this 是 undefined,因为 ES Module 自动采用严格模式,不管你是否声明 "use strict";
  5. CommonJS 模块是运行时加载,输出的是对象,输入时去对象上查找属性。ES6 模块是编译时加载,使静态分析成为可能。

# 总结

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

# 参考

  • [字节跳动] common.js 和 es6 中模块引入的区别? (霍小叶)
  • ECMAScript 6 入门 by 阮一峰 - Module 的语法
  • 前端模块化详解(完整版)