JS 模块化知识

前言
最近遇到很多朋友问 Javascript 模块化使用的问题,然后发现对这这个也是一知半解,偶尔还会搞混,在项目中使用的时候会出各种小错误,所以就写一下,复习一下 Javascript 模块化的知识和使用方式。

模块通常是指编程语言所提供的代码组织机制,利用此机制可将程序拆解为独立且通用的代码单元。所谓模块化主要是解决代码分割、作用域隔离、模块之间的依赖管理以及发布到生产环境时的自动化打包与处理等多个方面。

1 模块化的优点

  1. 可维护性。因为模块是独立的,一个设计良好的模块会让外面的代码对自己的依赖越少越好,这样自己就可以独立去更新和改进。
  2. 命名空间。在 JavaScript 里面,如果一个变量在最顶级的函数之外声明,它就直接变成全局可用。因此,常常不小心出现命名冲突的情况。使用模块化开发来封装变量,可以避免污染全局环境。
  3. 重用代码。我们有时候会喜欢从之前写过的项目中拷贝代码到新的项目,这没有问题,但是更好的方法是,通过模块引用的方式,来避免重复的代码库。

2 模块化规范

2.1 CommonJS 规范

CommonJS 是 Mozilla 的工程师于 2009 年开始的一个项目,他的目的是让浏览器之外的 Javascript (比如服务器端或者桌面端)能够通过模块化的方式来开发和协作。

在 CommonJS 规范中,每个 JS 文件就是一个独立的模块上下文(module context),在这个上下文中创建的属性都是私有的。也就是说,在一个文件定义的变量(包括函数和类),都是私有的,对其他文件是不可见的。

2.1.1 实例

这里分两种情况讲明吧,一种是一个文件中导出一个模块,这种我们引入模块的时候可以定义随便一个变量名去接入使用模块。另外则是一个文件中导出多个模块。这种情况的话,模块文件导出的是一个包含多个模块的对象,我们导入引用的时候只能是用一个对象 {} 引用,然后这边使用的模块名必须要和源文件中导出的模块名一致。(这边有点绕,具体看代码就明白了)

  • 一个文件导出一个模块
1
2
3
4
5
6
7
8
9
// sayHello.js
function sayHello(){
console.log('hello')
}
module.exports = sayHello

//main.js 中引入 sayHello.js
// 引入单个模块,这里引入的时候可以 say,sayHello 等等变量名去定义导入的模块
const say = require('./sayHello.js');
  • 一个文件导出多个模块
1
2
3
4
5
6
7
8
9
10
11
12
13
// say.js
function sayHi(){
console.log('hello');
}
function sayBye(){
console.log('bye');
}
module.exports = {sayBye,sayHi}

/* main.js
* 这个时候引用的话,用{}去引用导出的模块,怎么导出我们就怎么引用,然后里面的模块名就只能是引用文件 say.js 里面定义的 sayBye,sayHi 变量名
*/
const {sayBye, sayHello} = require('./say.js');

2.1.2 注意事项

因为 CommonJS 规范主要应用的场景是服务器端,所以采用同步加载模块的策略。如果我们依赖 3 个模块,代码会一个一个一次加载,这种的话就不适合浏览器端来使用了。

2.2 AMD 规范

AMD 是 Asynchronous Module Definition 的简称,即『异步模块定义 』,见名知义,AMD 优先照顾浏览器模块的加载场景,使用异步加载和回调的方式。注意使用模式需要 define 方法的支持。一般是引用 requireJS

2.2.1 实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// file lib/sayModule.js 

define(function (){
return {
sayHello: function () {
console.log('hello');
}
};
});

//file main.js

define(['./lib/sayModule'], function (say){
say.sayHello(); //hello
})

这种形式感觉有点烦,个人基本都没怎么用。

2.3 CMD 规范

CMD 是 Common Module Definition,公共模块定义,

2.4 ES6 规范

ES6 规范用的就比较多了,他的话比较简单清晰,就分为导出(export)与导入(import)两个模块 然后他的使用方式也是有很多种情况。

  1. 直接用 export {模块,模块} 导出。引用的话就直接用 import from 导出就可以了。
1
2
3
4
5
6
7
8
9
// module.js
let fn = function(){
console.log('test');
}
let bar = 'bar';
export {fn, bar}

// main.js
import {bar, fn} from './module';
  1. 重命名导出与导入。有时候发现我引用了两个文件的模块的名字是一样的,然后就会出现命名冲突了,这个时候就需要重命名导出导入来解决这种情况了,具体就看代码了。
1
2
3
4
5
6
7
8
// 导出重命名
let name = 'name';
// 这个时候其他文件导入的时候就用 newName 导入了
export { name as newName}

// 导入重命名
// 这样我们就用 newName 这个名字重命名原来的模块了。
import { moduleName as newName} from 'test'
  1. export default 匿名导出,如果只想导出一个 js 模块或者功能,可以直接用 export default 导出
1
2
3
4
5
6
// module.js 字符串,方法,对象,想导出啥就导出啥
export default 'string';

// main.js XX YY,想用啥命名就用啥命名,毕竟导出的就只有一个对象
import XXX from './lib'
console.log(XXX) // 'string'

总结

一般就用 ES6 的规范和 CommonJS 的规范,AMD 和CMD 实在不熟,就没用过了。



----------- 本文结束 -----------




小红帽 wechat
想看更多文章,那就订阅我的微信公众号吧
如果觉得我的文章对你有很大帮助的话,请我喝杯奶茶吧~(@^_^@)~