commonjs简介

学习一门技术,基础是关键,比如想学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)

你可能感兴趣的:(commonjs简介)