js模块化入门与commonjs解析与应用

Posted zheoneandonly

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js模块化入门与commonjs解析与应用相关的知识,希望对你有一定的参考价值。

  • JS模块化的基本原理
  • commonjs规范
  • commonjs在前端模块化中的基本使用
  • commonjs的实现原理
  • AMD与CMD规范剖析博客链接

 一、JS模块化基本原理

在JS没有提出来模块化的时候,开发JS项目比较简单,同时也比较杂乱,第一个问题就是全局变量的管理,为了解决这个问题基本上都使用了命名空间和闭包两个主流的解决方式,但是随着硬件基础和网络环境的改善,前端开发也越来越复杂,需要协同合作,代码复用的需求越来越多,复杂性带来的第一个问题就是命名管理问题再次遇到挑战,然后就是协同开发也被正式提上了议题,模块化正式走入台前。

 1 var a = 1;
 2 //函数
 3 function bindeEvent()
 4 function addClass()
 5 //命名空间
 6 var obj1 = 
 7     a:2,
 8     foo:function()
 9 
10 var obj = 
11     a:2,
12     foo:function()
13 
14 //闭包
15 (function($)
16     var a = 123;
17     function foo()
18     return foo:foo
19 )(jQuery);

模块化的在很大程度上与闭包的实现模式非常类似,传入闭包的参数可以看作是模块的引用,返回值可以看作是模块定义的接口,整个闭包就可以理解为一个模块,有差别的地方就是模块化是一个模块单独在一个js文件中实现、模块中使用的模块自身以外的参数和属性都必须依赖引入。

有了单独js文件和各种规范的标准接口,就能很好的实现了协同开发这问题,解决了前端开发越来越麻烦的复杂性,同时命名冲突问题也在模块的私有变量特性中迎刃而解。

 二、commonjs规范

在commonjs官网的最上方有一句这样的描述:javascript: not just for browsers any more!翻译:javascript:不仅仅是针对浏览器了!最近两年在前端很火的nodejs就是commonjs规范的实现,而webpack是基于nodejs实现的,自然而然就是commonjs规范的实现。虽然我们后期在js的开发中不一定使用commonjs规范,但是我们需要使用webpack作为前端工程化的实现工具,还有基于nodejs的模块、插件、库,所以对于我们理解和应用其他技术很有帮助,所以对于commonjs的理解是有必要的。

可能说到这里,我们还不清除commonjs是个什么东西,这么来说,javascript官方定义的api只能构建基础的浏览器应用,也就是说javascript官方只实现了在浏览器环境中的api,如果来那javascript来做服务端应用程序,又或者是图形界面应用程序,还有命令行工具等就有些力不从心,2009年程序员Ryan Dahl创造了node,js项目,将javascript用于服务器端编程,这标志这javascript模块化开发的诞生,而commonjs就是这个模块的规范。

commonjs都有哪些规范呢?

1.commonjs的出发点:js没有模块系统、标准库较少、缺乏包管理工具;为了让javascript可以在任何地方运行,以达到JAVA、C#、php这些后台语言的开发能力。

 

2.commonjs规范的具体内容:

 一个文件就是一个模块;

 普通方式定义的变量、函数、对象都属于该模块内;

 通过require来加载模块;

 通过exports和modul.exports来暴露模块中的内容(接口);

 

3.所有代码都运行在模块作用域,不会污染全局作用域;模块可以多次加载,但只会在第一次加载的时候运行,然后运行结果就被缓存了,以后再加载就读取缓存里面的结果;模块的加载顺序按照代码的出现顺序同步加载。

 

4._dirname代表当前模块文件所在的文件夹路径,_filename代表当前模块文件所在文件夹路径+文件名。

 

5.require(同步加载基本功能):读取并执行一个js文件,然后返回该模块的exports对象,如果没有发现指定模块会报错。

 

6.模块内的exports:nodejs为每个模块提供了一个exposrts变量,其指向module.exports,相当于再模块头部加了这句代码:var exports = module.exports;在对外输出时,可以给exports对象添加方法,但不能直接赋值,因为这样会切断exports与module.exports的联系。

 

7.npm root -g 查看全局包安装位置,建议nvm目录下npm\\node_modules目录,然后设置npm全局包安装位置npm config set prefix "",然后将该路径添加到环境变量中;

 

8.npm init -y 初始化package.json文件,加上-y就会默认生成该文件;npm docs 包名:查看包的文档;npm install:安装package.json中dependencies属性中所以来的包。

 

9.由于npm服务器是国外的,下载慢或者不能下载成功经常出现,建议使用淘宝NPM镜像http://npm.taobao.org/,与官方NPM同步频率为10分钟一次,安装命令:npm install -g cnpm --registry=https://registry.npm.taobao.org,安装包:cnpm install 包名(其它命令基本一致);

 

10.如果你不想下载cnpm,npm还提供了一个镜像源管理工具:npm install -g nrm,通过:nrm ls,查看镜像源列表 ,通过:npm use 镜像源,来切换;

 

11、NPM的模块加载机制:

      如果require的是绝对路径文件,查找不会去遍历每个node_modules目录,其速度最快

  1).从module.paths数组中(由当前执行文件目录到磁盘根目录)取出第一个目录作为查找基准

  2).直接从目录中查找该文件,如果存在则结束查找,如果不存在则进行下一条查找

  3).尝试添加.js、.json、.node后缀之后查找,如果存在文件则结束查找,如果不存在则进行下一条查找

  4).尝试将require的参数作为一个包来进行查找,读取目录下的package.json文件,取得Main参数指定的文件

  5).尝试查找该文件,如果存在则结束查找,如果不存在则进行第3条查找

  6).如果继续失败,则取出module.paths数组中的下一目录作为基准查找,循环第1-5个步骤

  7).如果继续失败,循环第1-6个步骤,直到module.paths中的最后一个值

  8).如果继续失败,则抛出异常

 三、commonjs在前端模块化中的基本使用

Commonjs规范最开始就是为了将javascript运行在服务器环境下,所以采用了同步加载不同模块文件,适用于服务端。因为模块文件都存放在服务器的各个硬盘上,读取速度块,适合服务器,不适应浏览器。

而且浏览器不兼容Commonjs,原因是浏览器缺少module、exports、require、global是个环境变量。如果要使用的化需要工具转换,有时候还会用到global这个nodejs中的全局变量,相当于dom中的window变量。

 

在前端开发中使用Commonjs模块化规范:

1.定义文件结构:

//工作区间
    -->modules//依赖模块
        -->m1.js
        -->m2.js
        -->m3.js
    -->demo.html//结构文本
    -->index.js//主入口文件

依赖模块代码(m1.js):

1 module.exports = 
2     msg:‘m1‘,
3     foo:function()
4         return this.msg;
5     
6 

依赖模块代码(m2.js):

1 module.exports = function()
2     return ‘m2‘;
3 

依赖模块代码(m3.js):

1 // var exports = module.exports;在第博客二部分:commonjs规范中第六小点由说明
2 exports.foo = function()
3     return ‘ms‘;
4 

主入口文件(index.js):

1 var m1 = require(‘./modules/m1‘);
2 var m2 = require(‘./modules/m2‘);
3 var m3 = require(‘./modules/m3‘);
4 
5 console.log(m1.foo());
6 console.log(m2());
7 console.log(m3.foo());

前面规范中说过在浏览器并不支持commonjs规范,主入口文件index.js肯定不能直接被demo.html结构文本使用,直接使用的话会报错;所以在要前端开发中使用commonjs规范的话就必须使用插件将commonjs模块化规范转换成浏览器识别的代码结构,在这之前系统上必须安装nodejs环境,这时候我们可以先在控制台中测试看看index.js的依赖是否成功:

1 node -v //测试node环境是否安装成功,如果成功的话会在控制台打印出node的版本号
2 node index.js

打印结果:

技术图片

这是在node环境下能够编译执行index.js,前面说过要想将commonjs模块化结构代码在浏览器中编译执行,得需要使用工具转换成浏览器能够编译执行的代码结构,这个工具就是Browserify,工具的官网:http://browserify.org/

技术图片

下载安装成功是这样的:

技术图片

在Browserify官网首页的使用介绍中有这样一句命令说明:

技术图片

意思是将commonjs规范的main.js文件通过browserify转换成浏览器能够执行的bundle.js文件;那么我们就可以使用这个命令来将示例中的index.js转换成一个可以在浏览器使用的js文件(下面这句命令是要在命令窗口执行):

 browserify index.js -o bundle.js

这行命令执行完成后会在我们的工作区间生成一个bendle.js文件,这个文件就是通过browserify工具将基于commonjs规范的index.js入口文件及其依赖的m1.js、m2.js、m3.js转换生成的浏览器可以执行的js代码。

这时候我们可以将bundle.js引入到demo.html结构文件中进行执行:

<script src="bundle.js"></script>

打开页面-查看浏览器控制台:

技术图片

在前端使用commonjs规范然后使用browserify转换成浏览器能执行的文件,全部过程演示完成;值得我们关注的是生成的bundle.js是什么?

技术图片

从生成的代码来看,本质上就是将依赖的文件合并到了一个文件中,然后同样是采用闭包加对象属性实现的命名空间的代码管理方式,只不过这个过程我们交给了工具来实现,在我们的日常开发中,我们只需要关注我们自己的模块开发,不再需要去考虑全局命名冲突,代码管理问题。而且通过模块化开发带来的最大的好处就是代码的复用性特别方便,采用依赖模式然后使用工具转换,也解决了原来在结构文件中引入大量插件和库的问题,给代码瘦身的同时也降低了网络请求压力。特别是插件、库、组件之间的相互依赖关系带来的加载阻塞问题得到了很好的解决。

然后,在Browserify的官方首页使用介绍中有一个基于全局模块uniq的示例:

下载安装uniq模块:

npm install uniq

下载安装成功以后会在当前工作区间生成一个node_modules文件夹和一个packge-lock.json文件,各相关的原理在webpack博客中详细解析。然后,官网给出的代码放到我们示例代码中的index.js中:

1 var unique = require(‘uniq‘);
2 var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];
3 console.log(unique(data));

然后,在控制台中再次编译一次index.js到bundle.js:

 browserify index.js -o bundle.js

然后刷新demo.html,查看浏览器控制台:

技术图片

 

 

关于commonjs的实现原理等过段时间再来补充;

AMD、COM规范解析

以上是关于js模块化入门与commonjs解析与应用的主要内容,如果未能解决你的问题,请参考以下文章

ES6 模块与 CommonJS 模块的差异

Node入门教程第五章:node 模块化(下) npm与yarn详解

commonjs 与 ES6 模块化

模块化 angularjs 应用程序 commonJS 与 AMD 模块语法

ES6入门十二:Module(模块化)

JS - CommonJSES2015AMDCMD模块规范对比与介绍(附样例)