目录
1ES6之前的模块化策略
2ES6的模块化
模块化可以减少全局变量的使用,方便代码的维护和书写。
这二者的区别主要在于:CommonJS对模块的加载时采用的是同步(synchronous)的方式,而AMD对模块的加载采用的是异步(Asynchronous)的方式。因为在服务器端,加载的时延主要是对硬盘的读写,而在客户端,加载的时延主要的是网络传输的时延,而网络常常是不稳定的,所以如果在客户端采用同步的方法,会影响浏览器的渲染。
define(function(require) {
//通过相对路径获得依赖模块
const module1 = require("./module1");
//模块暴露
return function() {
....
};
});
当前有两个库实现了AMD规范:require.js和curl.js。
(1)require.js的使用:http://www.ruanyifeng.com/blog/2012/11/require_js.html (转自 阮一峰的网络日志)
require.js解决的问题就是当引入多个script时,多个js文件同步加载影响浏览器渲染以及它们之间依赖关系复杂的问题。
其中解决 影响浏览器渲染 的问题,平常的方法:
方法一:放在body的底部加载,这样页面渲染完毕才会加载js文件
方法二:指定js文件的属性
Node.js 正是使用的是CommonJS规范,典型的CommonJS规范的模块引入与暴露过程:
//模块引入
const bar = require("./bar");
//模块暴露
module.exports = function () {
//....
}
实际上require的模块化加载时运行时加载,即只有运行到这个地方才能确定相应的依赖关系,也即“运行时(动态)加载”。而ES6在编译时便能确定相应的依赖关系,也即“编译时(静态)加载”,效率要高(因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化")。
这其中主要是export和import的使用。
(1)export的使用
//向外声明方法接口
export var meth1 = function(){
console.log("this is a module");
}
//向外声明变量接口
export var variable = "111";
//用类似对象的方法,向外声明一个接口,里边可以有方法也可以有属性
function foo(){
console.log("this is a module, too");
}
var abc = "123";
export {abc, foo};
var a = "name1";
export {a as b};
实际上,在这里就可以看出,export的变量或者方法的名称,必须与import的变量或者方法名称单向对应(import引入的变量或者方法,必须能在export中找到,而export里边的变量不一定都要被import到),否则这里为什么还要as来重命名呢,岂不是多次一举。再者如果不是对应的,如何找到import引入的到底是哪个变量或者方法。
var m = 1;
export {m}; //正确
export m; //错误
export 2; //错误
注意:CommonJS中的模块输出的是一个值得拷贝,而ES6模块输出的是值得引用,因此取值时,只是产生一个只读的引用,只有真正用到才回去模块里取值,不会有缓存值。
(2)import的使用
import {变量名(对应export里边的名称)} from "导入模块的路径名"
注意:路径名可以只是模块名而不带有路径,但是必须有相应的配置文件。
foo(); //可以运行
import {foo} from "./module.js"
if(x === 1){
import {fpp} from "./module";
}
import * as obj form "./module";
console.log(obj.abc);
console.log(obj.xyz);
(3)default的使用
export default 后边可以加匿名函数、非匿名函数,但这在外部看来都是匿名函数。
var m = 1;
export default m;
//export default var m = 1; 是错误的
//export var m = 1; 是正确的
export default function(){
console.log("this is an anonymous function!");
}
function foo(){
console.log("this is not an anonymous function");
}
export default foo;
实际上,export 就是输出了一个叫做default 的变量或者方法,然后系统允许你为它取名字。
所以
export default foo;
//等价于
export {foo as default};
import abc from "./module";
//等价于
import {default as abc} from "./module";
(3)模块的循环加载
(4)如何使用模块
如果要使用的常量非常多或者方法是使用非常频繁,可以建立一个专门的constants目录或者utils目录,将常量写在constants.js或者utils.js的文件中,如果常量或者方法的数量很多,可以再在constants目录或者utils目录下建立子目录,然后将这些文件输出的常量或者方法,合并在一个index.js的文件中,到时外边的文件要加载只加载index.js就可以了。
加载的东西较少时:
加载的东西较多是:
(5)缺陷
固然inport、export编译时加载具有众多优良的特性,但是
if(condition1){
//加载模块1
}else if(condition2){
//加载模块2
}
这种动态加载模块的写法是会报错的,因此有一些真的需要动态加载的场景可能必须要靠require解决
react 推荐通过webpack 或者 browserify 进行应用的构建,搭配对应的loader 或者 plugin 可以实现通过 JavaScript 入口文件统一管理依赖资源。从整体上,这是一个典型的“但JavaScript入口组件”方案。