一、使用script导入文件的问题
请求过多 依赖模糊 难以维护
二、常用的模块化规范
1. CommonJS
- 应用实现 nodejs
运行时加载
社区方案,提供了服务器/浏览器的模块加载方案。非语言层面的标准。只能在运行时确定模块的依赖关系及输入/输出的变量,无法进行静态优化。
-
说明: 每个文件都可当做一个模块 在服务器端:模块加载是运行时同步加载 在浏览器端 模块需要提前编译打包处理
-
基本语法
暴露模块
module.exports= value
exports.xxx = value
模块暴露的本质 export对象
引入模块
require(xxx)
第三方模块 xxx为包名
自定义模块 xxx为模块路径
vue 项目中常用来导入图片等资源
cmmonJS模块的加载原理
CommonJS的一个模块,就是一个脚本文件。require
命令第一次加载该脚本,就会执行整个脚本,然后在内存生成一个对象。
{
id: '...',
exports: { ... },
loaded: true,
...
}
上面代码中,该对象的id
属性是模块名,exports
属性是模块输出的各个接口,loaded
属性是一个布尔值,表示该模块的脚本是否执行完毕。其他还有很多属性,这里都省略了。(详细介绍参见《require() 源码解读》)
以后需要用到这个模块的时候,就会到exports
属性上面取值。即使再次执行require
命令,也不会再次执行该模块,而是到缓存之中取值。
CommonJS模块的循环加载
CommonJS模块的重要特性是加载时执行,即脚本代码在require
的时候,就会全部执行。CommonJS的做法是,一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出
详情请参考阮志峰日志
2. ES6 (重要)ESMAScript6+
- 实现 浏览器端
编译时加载
出 语言规格层面支持模块功能。支持编译时静态分析,便于JS引入宏和类型检验。动态绑定。
- 说明 依赖模块需要编译打包处理 ,可以将es6语法打包成es5语法 处理兼容性。 使用babel将es6 编译为es5代码, 使用Browserify 编译打包js
- 语法 导出
// 多次导出
export const xxx = ...
// 单次导出
export default {}
导入
import xxx form '路径'
import '路径'
// 一般导入动态路由
import()
ES6模块的循环加载
ES6模块的运行机制与CommonJS不一样,它遇到模块加载命令import
时,不会去执行模块,而是只生成一个引用
。等到真的需要用到时,再到模块里面去取值。
因此,ES6模块是动态引用
,不存在缓存值的问题,而且模块里面的变量,绑定其所在的模块。请看下面的例子。
// m1.js
export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);
// m2.js
import {foo} from './m1.js';
console.log(foo);
setTimeout(() => console.log(foo), 500);
上面代码中,m1.js
的变量foo
,在刚加载时等于bar
,过了500毫秒,又变为等于baz
。
让我们看看,m2.js
能否正确读取这个变化。
$ babel-node m2.js
bar
baz
上面代码表明,ES6模块不会缓存运行结果,而是动态地去被加载的模块取值,以及变量总是绑定其所在的模块。
这导致ES6处理"循环加载"与CommonJS有本质的不同。 ES6根本不会关心是否发生了"循环加载",只是生成一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。
3. AMD 规范 (Asynchronous Module Definition)
- 说明 异步模块定义 专门用于浏览器端 模块的加载是异步的
- 基本语法 定义暴露模块 1)定义没有依赖的模块
define(function(){
return 模块
})
2)定义有依赖的模块
define(['module1','module2'],function(m1,m2){
return 模块
})
引入使用模块
require(['module1','module2'],function(m1,m2){
使用 m1、m2
})
4. CMD (Common Module Definition)
阿里已出售 了解即可