学习一门技术,基础是关键,比如想学react或redux,那就得有node和webpack的基础;学习webpack,知道commonjs规范,理解起来才会更快 。所谓万丈高楼平地起,基础打牢了,理解知识点才会更透彻,后期提升才会更快。
nodejs和commonjs区别?
commonjs是规范,nodejs是这种规范的实现。
commonjs还在不断的发展,还会有新的东西往里添加。
CommonJs有很多实现,其中不乏大名鼎鼎的项目,比如说Apache的CouchDB和node.js
这些项目大部分只实现CommonJs的部分规范。
CommonJs概述
每个文件就一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。
私有作用域不会污染全局作用域。
模块可加载多次,只会在第一次加载时运行一次,然后结果会被缓存起来,以后在使用,就直接读取缓存结果。想要让模块再次运行,必须清除缓存
模块加载顺序是按照其在代码中出现的顺序
基本使用
1、输出:module对象。
使用module.exports输出一个变量或函数,也可以使用exports输出
var x = 5;
var addX = function (value) {
return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
module是可以省略的。
2、module对象
node内部提供一个module构建函数。每个模块内部,都有一个module对象,代表当前模块,他有一些自身属性
module.id 模块的识别符,通常是带有绝对路径的模块文件名。
module.filename 模块的文件名,带有绝对路径。
module.loaded 返回一个布尔值,表示模块是否已经完成加载。
module.parent 返回一个对象,表示调用该模块的模块。
module.children 返回一个数组,表示该模块要用到的其他模块。
module.exports 表示模块对外输出的值。
3、exports变量
node为么每个模块提供一个exports变量,指向module.exports
exports.area = function(r){console.log(r)}
这个是ok的。
exports = function(r){console.log(r)}
这个写法无效,因为改变了改变了exports的指向
如果一个文件中 module.exports 被重新赋值,该文件中其他exports出去的变量(属性)也是无效的。
说说加载
上面说了输出(module.exoprts、exports),接下来说说载入。
node采用CommonJS规范。内置require命令用于加载模块:var foo = require("filename")
加载规则
说说require内部处理流程
1、在加载前,检查是否有缓存(module._cache)
2、如果缓存之中没有,就创建一个新的module实例
3、将他缓存起来
4、使用module.load()加载指定模块文件;读取文件内容之后,使用module.compile()执行文件代码
5、如果加载/解析过程报错,就从缓存删除该模块
6、返回该模块的module.exports
第一步是加载文件的规则
一般后缀默认为.js
路径分三种情况
1、('/home/app.js') -->绝对路径
2、('./home/app.js') -->相对路径 (当前路径下的home文件夹下的appjs文件)
3、('react') --> 没有 以 ‘./ 和 /’开头的路径,则表示加载的一个默认提供的核心模块,会去node_modules文件夹下找或node的系统安装目录中找
如果指定的文件没有发现,node会尝试为文件名添加.js .json .node后缀,再搜索。.js文件会以文本格式的js脚本文件解析,.json文件会以JSON格式的文本文件解析,.node文件会以编译后的二进制文件解析。
模块缓存
第一次加载某个模块,node会缓存该模块,以后再次加载会直接从缓存中取出该模块的module.exports属性
删除执行模块
delete require.cache[moduleName]
AMD规范与CommonJS的区别
CommonJS加载模块是同步的,加载完才执行后续操作;
AMD是异步加载模块,允许指定回调函数。
nodejs用于服务器端,代码文件一般都在本地硬盘,加载比较快,适合使用CommonJS,而浏览器环境,要从服务器加载模块,比较慢,所以浏览器一般采用AMD规范。
为什么会有规范?
回答这个问题之前得知道为什么要有模块?
因为有了模块,我们就可以很方便的使用别人的代码,想要什么功能,就加载什么模块,这样带来了一个问题大家得按照相应的方式编写模块,不然每个人写自己风格的代码,岂不是乱套了。
所有才有了CommonJS、AMD、CMD这些规范!
再说说AMD规范
全称:异步模块定义
诞生背景:基于commonJS规范的nodeJS出来以后,服务端的模块概念已经形成,很自然地,大家就想要客户端模块。而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行。但是,由于一个重大的局限,使得CommonJS规范不适用于浏览器环境。
同步加载对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。
因此,浏览器端的模块,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)。这就是AMD规范诞生的背景。
AMD实现库有 require.js和curl.js。
requirejs主要解决两个问题:
1、实现js文件的异步加载,避免页面因为js文件加载失去响应
2、管理模块之间的依赖性,便于代码的编写和维护
AMD模块的写法
模块必须使用define函数来定义。如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。
例子:
// 没有依赖其他模块
//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(['myLib'], function(myLib){
function foo(){
myLib.doSomething();
}
return {
foo : foo
};
});
// 会先加载myLib模块
还可以加载非规范的模块
使用require.config方法,定义他们的一些特征
例子:
// underscore和backbone这两个库,都没有采用AMD规范编写。如果要加载它们的话,必须先定义它们的特征。
require.config({
shim: {
'underscore':{
exports: '_'
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
另外还有CMD,玉伯写的seajs,就是遵循他提出的CMD规范。
总结
为什么要有规范?
因为要模块化。为什么要模块化?
因为现在项目越来越大,按照功能划分,使用的时候直接加载相应的模块即可,利于维护和开发。比如nodejs都可以做服务器端语言了,将来项目肯定会越来越大,越来越复杂(个人认为:node将来会向java、Python这个方向发展的)。项目划分清晰明了为什么现在有那么多规范?(CommonJS、AMD、CMD)
因为不同规范适用于不同的场景。
CommonJS是同步加载,很快能得到结果的可以使用这个方式。比如:node加载文件时是在本地硬盘加载的,肯定快。
AMD、CMD是异步加载,获取结果比较慢的的情况可以使用这种方式,因为它可以设置回调,什么时候加载完成,什么时候执行对调。比如:浏览器加载服务器的数据,这个受网络环境影响很大,不能一直等服务器返回结果。所以可以给个回调方法,什么时候有结果了,走回调即可。
第一次写这么长的文章,肯定有很多值得商榷的地方,以后好好努力
参考文献:
什么是CommonJS? - 依水间 - 博客园
js模块化编程之彻底弄懂CommonJS和AMD/CMD!
CommonJS规范 -- JavaScript 标准参考教程(alpha)