好程序员web前端教程分享js中的模块化一

  好程序员web 前端教程分享 js 中的模块化一:我们知道最常见的模块化方案有 CommonJS AMD CMD ES6 AMD 规范一般用于浏览器,异步的,因为模块加载是异步的, js 解释是同步的,所以有时候导致依赖还没加载完毕,同步的代码运行结束; CommonJS 规范一般用于服务端,同步的,因为在服务器端所有文件都存储在本地的硬盘上,传输速率快而且稳定。

 

1.script 标签引入

 

最开始的时候,多个script 标签引入 js 文件。但是,这种弊端也很明显,很多个 js 文件合并起来,也是相当于一个 script ,造成变量污染。项目大了,不想变量污染也是很难或者不容易做到,开发和维护成本高。 而且对于标签的顺序,也是需要考虑一阵,还有加载的时候同步,更加是一种灾难,幸好后来有了渲染完执行的 defer 和下载完执行的 async ,进入新的时代了。

 

接着,就有各种各样的动态创建script 标签的方法,最终发展到了上面的几种方案。

 

2.AMD CMD

 

2.1AMD

 

异步模块定义,提供定义模块及异步加载该模块依赖的机制。AMD 遵循依赖前置,代码在一旦运行到需要依赖的地方,就马上知道依赖是什么。而无需遍历整个函数体找到它的依赖,因此性能有所提升。但是开发者必须先前知道依赖具体有什么,并且显式指明依赖,使得开发工作量变大。而且,不能保证模块加载的时候的顺序。 典型代表 requirejs require.js 在声明依赖的模块时会立刻加载并执行模块内的代码。 require 函数让你能够随时去依赖一个模块,即取得模块的引用,从而即使模块没有作为参数定义,也能够被使用。他的风格是依赖注入,比如:

 

/api.js

 

define(‘myMoudle’,[‘foo’,’bar’],function(foo,bar){

 

    // 引入了 foo bar ,利用 foo bar 来做一些事情

 

    return {

 

        baz:function(){return ‘api’}

 

               }

 

});

 

require([‘api’],function(api) {

 

    console.log(api.baz())

 

})

 

复制代码

 

然后你可以在中间随时引用模块,但是模块第一次初始化的时间比较长。这就像开始的时候很拼搏很辛苦,到最后是美滋滋。

 

2.2CMD

 

通用模块定义,提供模块定义及按需执行模块。遵循依赖就近,代码在运行时,最开始的时候是不知道依赖的,需要遍历所有的require 关键字,找出后面的依赖。一个常见的做法是将 function toString 后,用正则匹配出 require 关键字后面的依赖。 CMD  里,每个  API  都简单纯粹。可以让浏览器的模块代码像 node 一样,因为同步所以引入的顺序是能控制的。 对于典型代表 seajs ,一般是这样子:

 

define(function(require,exports,module){

 

    //… 很多代码略过

 

     var a = require(‘./a’);

 

    // 要用到 a ,于是引入了 a

 

    // 做一些和模块 a 有关的事情

 

 

 

});

 

复制代码

 

对于b.js 依赖 a.js

 

//a.js

 

define(function(require, exports) {

 

    exports.a = function(){// 也可以把他暴露出去

 

    //  很多代码    

 

    };

 

});

 

 

 

//b.js

 

define(function(require,exports){

 

      // 前面干了很多事情,突然想要引用 a

 

        var fun = require(‘./a’);

 

     console.log(fun.a()); //  就可以调用到及执行 a 函数了。

 

   })

 

 

 

// 或者可以 use

 

seajs.use([‘a.js’], function(a){

 

    // 做一些事情

 

});

 

复制代码

 

AMD CMD 对比:  AMD  推崇依赖前置、提前执行, CMD 推崇依赖就近、延迟执行。

 

AMD 需要先列出清单,后面使用的时候随便使用(依赖前置),异步,特别适合浏览器环境下使用(底层其实就是动态创建 script 标签)。而且 API  默认是一个当多个用。

 

CMD 不需要知道依赖是什么,到了改需要的时候才引入,而且是同步的,就像临时抱佛脚一样。

 

对于客户端的浏览器,一说到下载、加载,肯定就是和异步脱不了关系了,注定浏览器一般用AMD 更好了。但是, CMD api 都是有区分的,局部的 require 和全局的 require 不一样。

 

3.CommonJS ES6

 

3.1 ES6

 

ES6 模块的 script 标签有点不同,需要加上 type=’module’

 

<script src=’./a.js’ type=’module’>…</script>

 

复制代码

 

对于这种标签都是异步加载,而且是相当于带上defer 属性的 script 标签,不会阻塞页面,渲染完执行。但是你也可以手动加上 defer 或者 async ,实现期望的效果。  ES6 模块的文件后缀是 mjs ,通过 import 引入和 export 导出。我们一般是这样子:

 

//a.mjs

 

import b from ‘b.js’

 

//b.mjs

 

export default b

 

复制代码

 

ES6 毕竟是 ES6 ,模块内自带严格模式,而且只在自身作用域内运行。在 ES6 模块内引入其他模块就要用 import 引入,暴露也要用 export 暴露。另外,一个模块只会被执行一次。  import ES6 新语法,可静态分析,提前编译。他最终会被 js 引擎编译,也就是可以实现编译后就引入了模块,所以 ES6 模块加载是静态化的,可以在编译的时候确定模块的依赖关系以及输入输出的变量。 ES6 可以做到编译前分析,而 CMD AMD 都只能在运行时确定具体依赖是什么。

 

3.2CommonJS

 

一般服务端的文件都在本地的硬盘上面。对于客户,他们用的浏览器是要从这里下载文件的,在服务端一般读取文件非常快,所以同步是不会有太大的问题。require 的时候,马上将 require 的文件代码运行

 

代表就是nodejs 了。用得最多的,大概就是:

 

//app.js

 

var route = require(‘./route.js’)// 读取控制路由的 js 文件

 

 

 

//route.js

 

var route  = {……}

 

module.exports = route

 

复制代码

 

require  第一次加载脚本就会马上执行脚本,生成一个对象

 

区别: CommonJS 运行时加载,输出的是值的拷贝,是一个对象(都是由 module.export 暴露出去的),可以直接拿去用了,不用再回头找。所以,当 module.export 的源文件里面一些原始类型值发生变化, require 这边不会随着这个变化而变化的,因为被缓存了。但是有一种常规的操作,写一个返回那个值的函数。就像 angular 里面 $watch 数组里面的每一个对象,旧值是直接写死,新值是写一个返回新值的函数,这样子就不会写死。 module.export 输出一个取值的函数,调用的时候就可以拿到变化的值。

 

ES6 是编译时输出接口,输出的是值的引用,对外的接口只是一种静态的概念,在静态解释后已经形成。当脚本运行时,根据这个引用去原本的模块内取值。所以不存在缓存的情况, import 的文件变了,谁发出 import 的也是拿到这个变的值。模块里面的变量绑定着他所在的模块。另外,通过 import 引入的这个变量是只读的,试图进行对他赋值将会报错。

 

    原文作者:好程序员IT
    原文地址: http://blog.itpub.net/69913892/viewspace-2661415/
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞