common js
npm 的模块都是 JavaScript 语言写的,但浏览器用不了,因为不支持 CommonJS 格式。要想让浏览器用上这些模块,必须转换格式。
原理
var module = {
exports: {}
};
(function(module, exports) {
exports.multiply = function (n) { return n * 1000 };
}(module, module.exports))
var f = module.exports.multiply;
f(5) // 5000
module对象
module.exports属性
module.exports属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取module.exports变量
exports变量
node为每一个模块提供了一个exports变量(可以说是一个对象),指向 module.exports。这相当于每个模块中都有一句这样的命令
var exports = module.exports;
这样,在对外输出时,可以在这个变量上添加方法。例如
exports.add = function (r){return Math.PI * r *r};
注意:不能把exports直接指向一个值,这样就相当于切断了 exports 和module.exports 的关系。例如 exports=function(x){console.log(x)};
一个模块的对外接口,就是一个单一的值,不能使用exports输出,必须使用 module.exports输出。module.exports=function(x){console.log(x);};
require命令
equire命令用于加载模块文件,相当于读入并执行一个js文件,然后返回该模块的exports对象,没有发现指定模块,则就会报错。例如 example.js
exports.name = 'tom';
exports.age = 50;
在 同目录下的 demo.js 文件中
var example = require('./example.js');
console.log(example.name); // tom
console.log(example.age); // 50
或者 example.js
function fn(){console.log(1)};
var name = 'tom'
module.exports = {fn:fn,name:name}
这里可以简写一下,es6的对象简写,key 和 value 一致,可以只写一个。
module.exports = {fn,name};
在 同目录下的 demo.js 文件中 var example = require('./example.js');
example.fn(); // 1
console.log(example.name); // tom
模块的加载机制
commonsJS的加载机制,输入的是被输出值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值了。
Subtopic 2
ES6模块化
在ES6中每一个模块即是一个文件,在文件中定义的变量,函数,对象在外部是无法获取的。如果你希望外部可以读取模块当中的内容,就必须使用export来对其进行暴露(输出)。
test.js
let myName="laowang";
let myAge=90;
let myfn=function(){
return "我是"+myName+"!今年"+myAge+"岁了"
}
export {
myName,
myAge,
myfn
}
index.js
import {myfn,myAge,myName} from "./test.js";
console.log(myfn());//我是laowang!今年90岁了
console.log(myAge);//90
console.log(myName);//laowang
如果你不想暴露模块当中的变量名字,可以通过as来进行操作
test.js
let myName="laowang";
let myAge=90;
let myfn=function(){
return "我是"+myName+"!今年"+myAge+"岁了"
}
export {
myName as name,
myAge as age,
myfn as fn
}
/******************************接收的代码调整为**********************/
import {fn,age,name} from "./test.js";
console.log(fn());//我是laowang!今年90岁了
console.log(age);//90
console.log(name);//laowang
默认导出:一个模块只能有一个默认导出,对于默认导出,导入的名称可以和导出的名称不一致。
/******************************导出**********************/
export default function(){
return "默认导出一个方法"
}
/******************************引入**********************/
import myFn from "./test.js";//注意这里默认导出不需要用{}。
console.log(myFn());//默认导出一个方法
可以将所有需要导出的变量放入一个对象中,然后通过default export进行导出
/******************************导出**********************/
export default {
myFn(){
return "默认导出一个方法"
},
myName:"laowang"
}
/******************************引入**********************/
import myObj from "./test.js";
console.log(myObj.myFn(),myObj.myName);//默认导出一个方法 laowang
重命名export和import:如果导入的多个文件中,变量名字相同,即会产生命名冲突的问题,为了解决该问题,ES6为提供了重命名的方法。
/******************************test1.js**********************/
export let myName="我来自test1.js";
/******************************test2.js**********************/
export let myName="我来自test2.js";
/******************************index.js**********************/
import {myName as name1} from "./test1.js";
import {myName as name2} from "./test2.js";
console.log(name1);//我来自test1.js
console.log(name2);//我来自test1.js
AMD模块化
AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"
它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
AMD也采用require()语句加载模块,但是不同于CommonJS,它要求两个参数:
require([module], callback);
第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,是加载成功之后的回调函数。
require(['math'], function (math) {
math.add(2, 3);
});
RequireJS
实现了AMD规范
假定我们自己的代码文件是main.js,也放在js目录下面。
<script src="js/require.js" data-main="js/main"></script>
模块的加载 main.js 开始部分
require.config({
baseUrl: "js/lib",
paths: {
"jquery": "jquery.min",
// 或者"jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"
"underscore": "underscore.min",
"backbone": "backbone.min"
}
});
require.js要求,每个模块是一个单独的js文件。这样的话,如果加载多个模块,就会发出多次HTTP请求,会影响网页的加载速度。因此,require.js提供了一个优化工具,当模块部署完毕以后,可以用这个工具将多个模块合并在一个文件中,减少HTTP请求数。
AMD模块的写法
假定现在有一个math.js文件,它定义了一个math模块。那么,math.js就要这样写:
// math.js
define(function () {
var add = function (x, y) {
return x + y;
};
return {
add: add
};
});
加载方法如下:
require(['math'], function (math){
alert(math.add(1,1));
});
如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。
define(['myLib'], function(myLib){
function foo(){
myLib.doSomething();
}
return {
foo : foo
};
});
加载非规范的模块
require.config()接受一个配置对象,这个对象除了有前面说过的paths属性之外,还有一个shim属性,专门用来配置不兼容的模块。具体来说,每个模块要定义(1)exports值(输出的变量名),表明这个模块外部调用时的名称;(2)deps数组,表明该模块的依赖性。
require.config({
shim: {
'underscore':{
exports: '_'
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
shim: {
'jquery.scroll': {
deps: ['jquery'],
exports: 'jQuery.fn.scroll'
}
}