• javascript模块化


    1、为什么要模块化

      嵌入网页的javascript代码越来越庞大,越来越复杂,需要一个团队分工协作、进度管理、单元测试等,模块化编程,已经成为一个迫切的需求。此外:模块化编程解决的问题有:命名冲突问题,文件依赖问题等等。

    一言以蔽之:模块化就是分解代码。

    2、什么是模块

    模块就是实现特定功能的一组方法。只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块。有了模块,我们可以更方便地使用别人的代码,想要什么功能,就加载什么模块。接着出现了模块规范:就是大家必须以同样的方式编写模块,目前,通行的Javascript模块规范主要有两种:CommonJS和AMD。

     

    3、模块之间的关系如何解决

     通过形成依赖树来保证模块的加载顺序 
    3.1:AMD规范中:如果模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。
     define(['myLib'], function(myLib){
    
        function foo(){
    
          myLib.doSomething();
    
        }
    
        return {
    
          foo : foo
    
        };
    
      });

    3.2:CMD规范中:

    // 所有模块都通过 define 来定义
    define(function(require, exports, module) {
    
      // 通过 require 引入依赖
      var $ = require('jquery');
      var Spinning = require('./spinning');
    
      // 通过 exports 对外提供接口
      exports.doSomething = ...
    
      // 或者通过 module.exports 提供整个接口
      module.exports = ...
    
    });

     

    4、AMD和common js有什么区别 

     

     AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

      AMD也采用require()语句加载模块,但是不同于CommonJS,它要求两个参数:

        require([module], callback);

    第一个参数[module],是一个数组,里面的成员是要加载的模块;

    第二个参数callback,是加载成功之后的回调函数。如果将前面的代码改写成AMD形式,就是下面这样:

    require(['math'], function (math) {
        math.add(2, 3);
    });

     

    CommonJS是服务器端模块的规范,Node.js采用了这个规范。

    根据CommonJS规范,一个单独的文件就是一个模块。加载模块使用require方法,该方法读取一个文件并执行,最后返回文件内部的exports对象。下面就是一个简单的模块文件example.js。

    console.log("evaluating example.js");
    
    var invisible = function () {
      console.log("invisible");
    }
    
    exports.message = "hi";
    
    exports.say = function () {
      console.log(message);
    }

    使用require方法,加载example.js。

    var example = require('./example.js');

    这时,变量example就对应模块中的exports对象,于是就可以通过这个变量,使用模块提供的各个方法。

    {
      message: "hi",
      say: [Function]
    }

    require方法默认读取js文件,所以可以省略js后缀名。

    var example = require('./example');

    js文件名前面需要加上路径,可以是相对路径(相对于使用require方法的文件),也可以是绝对路径。如果省略路径,node.js会认为,你要加载一个核心模块,或者已经安装在本地 node_modules 目录中的模块。如果加载的是一个目录,node.js会首先寻找该目录中的 package.json 文件,加载该文件 main 属性提到的模块,否则就寻找该目录下的 index.js 文件。

    下面的例子是使用一行语句,定义一个最简单的模块。

    // addition.js
    
    exports.do = function(a, b){ return a + b };

    上面的语句定义了一个加法模块,做法就是在exports对象上定义一个do方法,那就是供外部调用的方法。使用的时候,只要用require函数调用即可。

    var add = require('./addition');
    
    add.do(1,2)
    // 3

    再看一个复杂一点的例子。

    // foobar.js
    
    function foobar(){
            this.foo = function(){
                    console.log('Hello foo');
            }
    
            this.bar = function(){
                    console.log('Hello bar');
            }
    }
     
    exports.foobar = foobar;

    调用该模块的方法如下:

    var foobar = require('./foobar').foobar,
        test   = new foobar();
     
    test.bar(); // 'Hello bar'

    有时,不需要exports返回一个对象,只需要它返回一个函数。这时,就要写成module.exports。

    module.exports = function () {
      console.log("hello world")
    }

    总结:

    1.common js是同步加载的 ,amd是异步的 

    2.根本不同:common js用于服务器端同步加载模块;amd是客户端异步加载模块。cmd  =浏览器端实现的蹩脚的common js (为什么这样:因为http是异步的, 在浏览器端使用common js不现实,cmd=amd的思想+common js的写法 )

    3.相同点:两者都有一个全局函数require(),用于加载模块;只是AMD规范中的require函数需要有2个参数。

     

    5、包,以及npm包结构规范

    5.1:概念包就是能完成独立功能的一个特殊模块 ,例如connect,http,compression,cookie-session,body-parser

    都是包,包与模块相比有多个区别:

     

    1、包是由模块组成的
    2、通常情况下由第三方提供的叫包,而自己书写的叫模块
    3、通常引用包用模块的名字,而引用模块用文件路径

     

    4、模块可能不能单独使用,而包是可以单独使用的

    官网解释:package

    This specification describes the CommonJS package format for distributing CommonJS programs and libraries. A CommonJS package is a cohesive wrapping of a collection of modules, code and other assets into a single form. It provides the basis for convenient delivery, installation and management of CommonJS components.

     

    This specifies the CommonJS package descriptor file and package file format. It does not specify a package catalogue file or format; this is an exercise for future specifications. The package descriptor file is a statement of known fact at the time the package is published and may not be modified without publishing a new release. 

    5.2:包结构规范

    CommonJS包规范是理论,NPM是其中的一种实践。NPM之于Node,相当于gem之于Ruby,pear之于PHP。对于Node而言,NPM帮助完成了第三方模块的发布、安装和依赖等。借助NPM,Node与第三方模块之间形成了很好的一个生态系统。 借助NPM,可以帮助用户快速安装和管理依赖包。

    一个符合CommonJS规范的包应该是如下这种结构:

    • 一个package.json文件应该存在于包顶级目录下
    • 二进制文件应该包含在bin目录下。
    • JavaScript代码应该包含在lib目录下。
    • 文档应该在doc目录下。
    • 单元测试应该在test目录下。
    •  由上文的require的查找过程可以知道,Node.js在没有找到目标文件时,会将当前目录当作一个包来尝试加载,所以在package.json文件中最重要的一个字段就是main。而实际上,这一处是Node.js的扩展,标准定义中并不包含此字段,对于require,只需要main属性即可。但是在除此之外包需要接受安装、卸载、依赖管理,版本管理等流程,所以CommonJS为package.json文件定义了如下一些必须的字段:
    • name。包名,需要在NPM上是唯一的。不能带有空格。
    • description。包简介。通常会显示在一些列表中。
    • version。版本号。一个语义化的版本号(http://semver.org/ ),通常为x.y.z。该版本号十分重要,常常用于一些版本控制的场合。
    • keywords。关键字数组。用于NPM中的分类搜索。
    • maintainers。包维护者的数组。数组元素是一个包含name、email、web三个属性的JSON对象。
    • contributors。包贡献者的数组。第一个就是包的作者本人。在开源社区,如果提交的patch被merge进master分支的话,就应当加上这个贡献patch的人。格式包含name和email。如:
    • "contributors": [{ "name": "Jackson Tian", "email": "mail @gmail.com" }, { "name": "fengmk2", "email": "mail2@gmail.com" }],
    • bugs。一个可以提交bug的URL地址。可以是邮件地址(mailto:mailxx@domain),也可以是网页地址(http://url)。
    • licenses。包所使用的许可证。例如:
    • "licenses": [{ "type": "GPLv2", "url": "http://www.example.com/licenses/gpl.html", }]
    • repositories。托管源代码的地址数组。
    • dependencies。当前包需要的依赖

    6、node.js的模块引用机制

     6.1:简单模块定义和使用

    在Node.js中,定义一个模块十分方便。我们以计算圆形的面积和周长两个方法为例,来表现Node.js中模块的定义方式。

    var PI = Math.PI;
    exports.area = function (r) {
        return PI * r * r;
    };
    exports.circumference = function (r) {
        return 2 * PI * r;
    };

    将这个文件存为circle.js,并新建一个app.js文件,并写入以下代码:

    var circle = require('./circle.js');
    console.log( 'The area of a circle of radius 4 is ' + circle.area(4));

    可以看到模块调用也十分方便,只需要require需要调用的文件即可。

    6.2node模块的载入及缓存机制
    1)载入内置模块(A Core Module)
    2)载入文件模块(A File Module)
    3)载入文件目录模块(A Folder Module)
    4)载入node_modules里的模块
    5)自动缓存已载入模块
    一、载入内置模块
    Node的内置模块被编译为二进制形式,引用时直接使用名字而非文件路径。当第三方的模块和内置模块同名时,内置模块将覆盖第三方同名模块。因此命名时需要注意不要和内置模块同名。如获取一个http模块
    var  http = require('http')
    返回的http即是实现了HTTP功能Node的内置模块。
    二、载入文件模块绝对路径的
    var   myMod = require('/home/base/my_mod')
    或相对路径的
    var  myMod = require('./my_mod')
    注意,这里忽略了扩展名“.js”,以下是对等的
    varmyMod = require('./my_mod')
    varmyMod = require('./my_mod.js')
     
    三、载入文件目录模块
    可以直接require一个目录,假设有一个目录名为folder,
    var  myMod = require('./folder')
     
    此时,Node将搜索整个folder目录,Node会假设folder为一个包并试图找到包定义文件package.json。如果folder目录里没有包含package.json文件,Node会假设默认主文件为index.js,即会加载index.js。如果index.js也不存在,那么加载将失败。
    假如目录结构如下
    package.json定义如下
     
    {
    "name": "pack",
    "main": "modA.js"
    }

    四、载入node_modules里的模块
    此时 require('./folder') 将返回模块modA.js。如果package.json不存在,那么将返回模块index.js。如果index.js也不存在,那么将发生载入异常。
    如果模块名不是路径,也不是内置模块,Node将试图去当前目录的node_modules文件夹里搜索。如果当前目录的node_modules里没有找到,Node会从父目录的node_modules里搜索,这样递归下去直到根目录。
    不必担心,npm命令可让我们很方便的去安装,卸载,更新node_modules目录。
     
    五、自动缓存已载入模块
    对于已加载的模块Node会缓存下来,而不必每次都重新搜索。下面是一个示例
    modA.js
    console.log('模块modA开始加载...')
    exports = function() {
    console.log('Hi')
    }
    console.log('模块modA加载完毕')

    init.js
    命令行执行:
    node init.js
    varmod1 = require('./modA')
    varmod2 = require('./modA')
    console.log(mod1 === mod2)
    输入如下
    可以看到虽然require了两次,但modA.js仍然只执行了一次。mod1和mod2是相同的,即两个引用都指向了同一个模块对象。
  • 相关阅读:
    CloudStack 实现VM高可用特性
    cloudstack基础知识
    cloudstack4.5私有云集群规划与安装
    小心了,这个设置会导致你的vm重启时被强制重装系统!
    CloudStack名词解释
    javatoexe之exe4j和innosetup打包jar
    oracle之partition by与group by的区别
    Android中传递对象的三种方法
    设计模式之mvp设计模式
    正则表达式之环视(lookaround)
  • 原文地址:https://www.cnblogs.com/QingFlye/p/4005153.html
Copyright © 2020-2023  润新知