什么是代码模块化?
将一个复杂的应用程序依据一定的规则(规范)封装成几个块(文件),并组合在一起;块的内部数据/实现是私有的,只是向外部暴露一些接口与其他模块进行通信。
为什么要有js模块化?
由于最早js应用功能简单,代码都是写在一个文件或者一个全局作用域内;但随着js应用愈来愈复杂,代码量越来越大,这时代码就变得难以维护,耦合度高,功能区划不清,易命名冲突而污染全局环境。所以就要有模块化。
js模块化前期发展的几个阶段:
第一阶段:全局function模式
此时函数和变量直接定义在全局作用域内,易产生全局污染。
第二阶段:namespace模式(命名空间)
此时函数和变量挂载在全局对象的属性上,不会污染全局环境,但也能通过修改对象的属性而被篡改。
第三阶段:IIFE模式(立即执行函数,闭包)
此时变量声明在函数的局部作用域内,是函数私有的,且不易被篡改。
第四阶段:IIFE模式增强:引入依赖
如下,引入并使用jQuery
第五阶段:load script
多个script标签,会导致发送多次请求,依赖关系模糊,难以维护。
js模块化现在常用的几个规范
一. commonJs
Commonjs规范主要实现在node应用中,模块是同步加载的。
基本语法
暴露模块:1. module.exports = value; 2. exports.xxx = value;
引入模块: var some_name = require(xxx); 其中,如是第三方模块’xxx’为模块名,如是自定义模块’xxx’为文件路径。
暴露模块的理解:导出的实际上是exports对象,module.exports是赋值给exports自身,exports.xxx是赋值给exports的一个属性
二.AMD(异步模块定义 Asynchronous Module Definition)
amd规范的一个实现是require.js库,专门用于浏览器端,模块加载是异步的。
基本语法
定义暴露模块:
- 没有依赖的模块
define(function(){
return 模块
})
2. 有依赖的模块
define(['module1', 'module2'], function(m1, m2){
return 模块
})
引入使用模块
requirejs([“module1”, “module2”], function(m1, m2){
使用m1/m2
})
requirejs的使用还需定义一个主文件,也是入口文件,主要是配置模块名与模块路径的映射:
只需要引入一个js文件
目录结构
三.CMD(通用模块定义 Common Module Definition)
cmd规范的一个实现是sea.js库,专门用于浏览器端,模块加载是异步的。
基本语法(结合了commonjs和amd)
定义没有依赖的模块:
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){
})
})
引入使用模块:
define(function(require){
var m1 = require(“./module1”);
m1.show();
})
2个script标签
amd和cmd的区别:cmd是就近依赖,模块使用时才会加载执行;amd是前置依赖,模块提前加载执行。
四.ES6规范
es6模块导出和导入分别是export和import关键字。
(一). 模块导出
模块导出分为2种:命名导出和默认导出。
- 命名导出(理解:导出的模块实际上是一个容器对象,实际导出的值都存储在这个容器对象的属性上,导入时需要从这个容器对象取得对应的属性)
命名导出可用2种方式:分别暴露和统一暴露,主要区别如下:
- 默认导出
使用default关键字,并且一个文件只能有一个默认导出。
(二). 模块导入
语法: import xxx from '路径'
当模块为命名导出时,xxx需为对象解构赋值的形式;当模块为默认导出时,xxx为任意变量名的形式。当为第三方模块时,‘路径’为模块名。
ts模块
在ts中,模块的语法还可以有如下形式:
导出模块:export = 模块名;
导入模块: import module = require(路径);