前端模块化机制有哪些

相关问题

  • JavaScript 主要有哪几种模块化规范
  • AMD / CMD 有什么异同
  • ES6
  • 模块化解决了什么问题/痛点在这里插入图片描述

一、模块化是什么?

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

二、为什么需要模块化和模块化规范​(模块化解决了什么问题/痛点)

  • 模块化可以解决代码之间的变量、函数、对象等命名的冲突/污染问题,良好的模块化设计可以降低代码之间的耦合关系,提高代码的可维护性、可扩展性以及复用性
  • 模块化规范的作用是为了规范 JavaScript模块的定义和加载机制,以统一的方式导出和加载模块,降低学习使用成本,提高开发效率

三、前端模块化机制

1.CommonJS

1.1简述

CommonJS主要是Node.js使用,通过 require 同步加载模块,exports 导出内容。在 CommonJS 规范下,每一个 JS 文件都是独立的模块,每个模块都有独立的作用域,模块里的本地变量都是私有的。

1.2特点

  • 所有代码都运行在模块作用域,不会污染全局作用域
  • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存
  • 模块加载的顺序,按照其在代码中出现的顺序

1.3基本语法

  • 暴露模块:module.exports = valueexports.xxx = value
  • 引入模块:require(xxx),如果是第三方模块,xxx为模块名;如果是自定义模块,xxx为模块文件路径
    此处我们有个疑问:CommonJS暴露的模块到底是什么?CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即
    module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。
// example.js
var x = 5;
var addX = function (value) {
  return value + x;
};
module.exports.x = x;
module.exports.addX = addX;

上面代码通过module.exports输出变量x和函数addX

var example = require('./example.js');//如果参数字符串以“./”开头,则表示加载的是一个位于相对路径
console.log(example.x); // 5
console.log(example.addX(1)); // 6

require命令用于加载模块文件。require命令的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象。如果没有发现指定模块,会报错。

1.4模块的加载机制

CommonJS模块的加载机制是,输入的是被输出的值的拷贝也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。这点与ES6模块化有重大差异(下文会介绍),请看下面这个例子:

// lib.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
};
// main.js
var counter = require('./lib').counter;
var incCounter = require('./lib').incCounter;

console.log(counter);  // 3
incCounter();
console.log(counter); // 3

上面代码说明,counter输出以后,lib.js模块内部的变化就影响不到counter了。这是因为counter是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部变动后的值。

1.5优缺点

  • 优点
    简单易用
    可以在任意位置 require 模块
    支持循环依赖
  • 缺点
    同步的加载方式不适用于浏览器端
    浏览器端使用需要打包
    难以支持模块静态分析

2.ES6模块化

2.1简介

ES6模块的设计思想是尽量的静态化使得编译时就能确定模块的依赖关系,以及输入和输出的变量CommonJSAMD 模块,都只能在运行时确定这些东西。比如,CommonJS模块就是对象,输入时必须查找对象属性。

2.2ES6模块化语法

export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

/** 定义模块 math.js **/
var basicNum = 0;
var add = function (a, b) {
    return a + b;
};
export { basicNum, add };

/** 引用模块 **/
import { basicNum, add } from './math';
function test(ele) {
    ele.textContent = add(99 + basicNum);
}

使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。

export default function () {
  console.log('foo');
}
import customName from './export-default';
customName(); // 'foo'

模块默认输出, 其他模块加载该模块时,import命令可以为该匿名函数指定任意名字

2.3ES6 模块与 CommonJS 模块的差异

差异:
1.CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
2. CommonJS模块是运行时加载,ES6模块是编译时输出接口。

第二个差异是因为 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。

下面重点解释第一个差异,我们还是举上面那个CommonJS模块的加载机制例子:

// lib.js
export let counter = 3;
export function incCounter() {
  counter++;
}
// main.js
import { counter, incCounter } from './lib';
console.log(counter); // 3
incCounter();
console.log(counter); // 4

ES6 模块的运行机制与 CommonJS 不一样。ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

2.4 优缺点

  • 优点
    支持同步/异步加载
    语法简单
    支持模块静态分析
    支持循环引用
  • 缺点
    兼容性不佳

3, AMD

3.1 简介

CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作
AMD规范则是非同步(异步)加载模块,允许指定回调函数。由于Node.js主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS规范比较适用。但是,如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用AMD规范。此外AMD规范比CommonJS规范在浏览器端实现要来着早。

3.2语法

定义暴露模块:

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

引入使用模块:

require(['module1', 'module2'], function(m1, m2){
   使用m1/m2
})

3.3 实现

requirejs

3.4 优缺点

  • 优点
    依赖异步加载,更快的启动速度
    支持循环依赖
    支持插件
  • 缺点
    语法相对复杂
    依赖加载器
    难以支持模块静态分析

4.CMD

4.1 简介

CMD规范专门用于浏览器端,模块的加载是异步的,模块使用时才会加载执行。CMD规范整合了CommonJS和AMD规范的特点。在 Sea.js 中,所有JavaScript模块都遵循CMD模块定义规范。

4.2 语法

定义暴露模块:

//定义没有依赖的模块
define(function(require, exports, module){
  exports.xxx = value
  module.exports = value
})
//定义有依赖的模块
define(function(require, exports, module){
  //引入依赖模块(同步)
  var module2 = require('./module2')
  //引入依赖模块(异步)
    require.async('./module3', function (m3) {
    })
  //暴露模块
  exports.xxx = value
})

引入使用模块:

define(function (require) {
  var m1 = require('./module1')
  var m4 = require('./module4')
  m1.show()
  m4.show()
})

4.3 具体实现

seajs

4,4 优缺点

  • 优点
    依赖异步加载,更快的启动速度
    支持循环依赖
    依赖就近
    与 CommonJS 保持很大的兼容性
  • 缺点
    语法相对复杂
    依赖加载器
    难以支持模块静态分析
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值