AMD/RequireJS 使用入门

参考资料

RequireJS 中文网
Javascript模块化编程(三):require.js的用法——阮一峰

前言

本人菜鸟,入IT只为当鼓励师。本编文章意在总结 RequireJS 的目的和用法。

一、require.js 的 目的

1.1 管理模块间的依赖性,便于代码的编写和维护

RequireJS 鼓励代码的模块化,主要目的是为了代码的模块化。

  • 如果一个文件需要依赖另外一些文件中定义的东西时,这个文件依赖的所有文件都要在它之前导入。过于复杂的系统,依赖关系可能出现相互交叉的情况,依赖关系的管理就更加难了。
    例如:51行到60行,分别是编写的10个模块。第61行引入的 main.js 是主模块,编写的是程序运行的过程。main.js 这个文件用到了从51行到60行的模块,而它并没有被其他模块使用,故可以且必须放在最末尾导入。而上面的10个模块,要确保模块的依赖在该模块导入之前就要导入,为了解除导入顺序的限制,只能让各模块间解耦。


    AMD/RequireJS 使用入门_第1张图片
  • 而 RequireJS 使用了不同于传统

    加载这个文件,因为浏览器是同步加载的,也可能会造成网页失去响应。
    那么,你可以把它放在网页底部加载:


    或者,你可以将它写成:

    
    

    async属性表示该文件需异步加载,避免网页失去响应。但IE浏览器不支持async,只支持defer,所以把defer也写上。

    3.2 引入网页程序的主模块

    data-main 入口点

    页面顶层 // main.js require.config({ paths: { foo: 'libs/foo-1.1.3' } }); // other.js /** * 由于 main.js 中的 foo 模块是异步加载的, * other.js 可能在 foo 模块加载完之前就已经加载执行, * 此时 other.js 中的 foo 模块 指的是 scripts/foo.js 而非 libs/foo-1.1.3。 */ require( ['foo'], function( foo ) { });

    四、主模块的写法

    main.js "主模块",是整个网页的入口代码。它有点像 C语言 的 main() 函数,所有代码都从这里开始运行。

    • main.js 若不依赖其他模块,则可以直接写js代码:
      // main.js
      console.log("加载成功!");
      这就相当于 C语言 中,直接把代码写在 main() 函数中:
      void main(int arg[], char* agvs[]) {
      printf("加载成功!");
      }
    • 但正常情况下,主模块会依赖于其他模块,这时就要使用AMD规范定义的 require() 函数。
      // main.js
      require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
      // 运行代码
      });
      /**
      * dependency_array:依赖数组
      * callback_func:回调函数
      * require(dependency_array, callback_func);
      /
      require() 函数接收两个参数:
      第一个参数是一个数组,表示所依赖的模块有哪些;
      第二个参数是一个回调函数*,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块。
      require() 异步加载 moduleA、moduleB 和 moduleC,浏览器不会失去响应;它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。

    五、模块的加载

    使用 require.config() 方法,我们可以对模块的加载行为进行自定义。require.config() 方法接收一个参数,该参数为包含一些指定属性的原生对象,我们可通过设置对应属性的值来修改加载行为。这些属性有:baseUrlpathshimmapconfig 等等。

    5.1 设置 path(module ID)

    RequireJS 鼓励在使用脚本时 以 module ID 替代 URL 地址,默认假定所有的依赖资源都是 js 脚本,因此无需在 module ID上再加 ".js" 后缀。
    我们可以设置 require.config() 方法 中 传入对象 的 path 属性,来设置各模块的 module ID。

    requirejs.config({
        paths: {
            Bird: 'scripts/views/Bird',
            Block: 'scripts/views/Block',
            Counter: 'scripts/views/Counter',
            GameBg: 'scripts/views/GameBg',
            GameOver: 'scripts/views/GameOver',
            GrassLand: 'scripts/views/GrassLand',
            StartBtn: 'scripts/views/StartBtn',
            StartInfo: 'scripts/views/StartInfo',
            RandomCreator: 'scripts/utils/RandomCreator'
        }
    });
    
    // 加载 scripts/views/Bird.js 和 scripts/utils/randomCreator.js
    require(['Bird', 'randomCreator'], function (Bird, randomCreator) {
        // 代码
    });
    

    5.2 设置 baseUrl

    baseUrl 可通过 requirejs.config() 手动设置。如果没有显式指定 config
    data-main,则默认的 baseUrl 为包含 RequireJS 的那个 HTML 页面的所属目录。设置以后,加载模块时,路径都会被解析为 baseUrl + path。若想避开该解析过程,设置 path 时可以:

    • 以 ".js" 结束;
    • 以 "/" 开始;
    • 包含 URL 协议, 如 "http:" or "https:"。
    requirejs.config({
        baseUrl: "scripts/views",
        paths: {
            Bird: 'scripts/views/Bird',
            Block: 'scripts/views/Block',
            Counter: 'scripts/views/Counter',
            GameBg: 'scripts/views/GameBg',
            GameOver: 'scripts/views/GameOver',
            GrassLand: 'scripts/views/GrassLand',
            StartBtn: 'scripts/views/StartBtn',
            StartInfo: 'scripts/views/StartInfo',
            RandomCreator: '../utils/RandomCreator'
        }
    });
    
    // 加载 scripts/views/Bird.js 和 scripts/utils/randomCreator.js
    require(['Bird', 'RandomCreator'], function (Bird, randomCreator) {
        // 代码
    });
    

    如果某个模块在另一台主机上,也可直接指定其网址:

    require.config({
        paths: {
            "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min"
        }
    });
    

    require.js 要求,每个模块是一个单独的 js 文件。如果加载多个模块,就会发出多次HTTP请求,影响网页的加载速度。因此,require.js 提供了一个 优化工具,当模块部署完毕后,可用该工具将多个模块合并成一个文件,减少HTTP请求数。

    5.3 设置 shim

    理论上,require.js 加载的模块,必须是按照AMD规范、用 define() 函数定义的模块。但是实际上,虽然已经有一部分流行的函数库(比如jQuery)符合AMD规范,更多的库并不符合。
    为了能够加载非规范的模块,可设置 require.config() 方法 传入对象 中的 shim 属性。shim属性的值是一个对象,这个对象里包含一些模板对象,而这些模板对象有两个属性:
    ① exports值(输出的变量名):表明这个模块外部调用时的名称;
    ② deps数组:表明该模块的依赖什么模块。

    require.config({
        shim: {
            // underscore 模块,外部调用时使用 _ 指代该模块
            'underscore': {
                exports: '_'
            },
            // backbone模块,外部调用时使用 Backbone 指代该模块
            // 这个模块依赖于 underscore , jquery 模块
            'backbone': {
                deps: ['underscore', 'jquery'],
                exports: 'Backbone'
            }
        }
    });
    

    5.4 设置 map

    对于给定的模块前缀,使用一个不同的模块ID来加载该模块。

    requirejs.config({
        map: {
           'some/newmodule': {
                'foo': 'foo1.2'
            },
            'some/oldmodule': {
                'foo': 'foo1.0'
            }
        }
    });
    

    AMD/RequireJS 使用入门_第4张图片

    另外在map中支持 *,意思是 "对于所有的模块加载,使用本map配置"。如果还有更细化的map配置,会优先于 * 配置。

    requirejs.config({
        map: {
            '*': {
                'foo': 'foo1.2'
            },
            'some/oldmodule': {
                'foo': 'foo1.0'
            }
        }
    });
    

    意思是: 除了 some/oldmodule 外的所有模块,当要用 foo 时,使用 foo1.2 来替代。对于 some/oldmodule 自己,则使用 foo1.0

    5.5 设置 config

    若想设置一些配置信息(变量,方法)供对应模块使用,可以设置 require.config() 方法 传入对象 中的 config 属性。要获取这些信息的模块可以加载特殊的依赖 module,并调用 module.config()。如:

    // main.js
    requirejs.config({
        config: {
            'bar': {
                size: 'large'
            },
            'baz': {
                color: 'blue'
            }
        }
    });
    
    // bar.js
    define( function (require, exports, module) {
        var size = module.config().size;
        console.log(size);  // large
    });
    
    // baz.js
    define(['module'], function (module) {
        var color = module.config().color;
        console.log(color);  // blue
    });
    

    六、定义模块

    模块不同于传统的脚本文件,它良好地定义了一个作用域来避免全局名称空间污染。它可以显式地列出其依赖关系,并以函数(定义此模块的那个函数)参数的形式将这些依赖进行注入,而无需引用全局变量。

    RequireJS 定义模块采用AMD规范,使用 define() 方法。
    假定现在有一个GameBg.js文件,它定义了一个 GameBg 模块:

    // GameBg.js
    define( function () {
      
        var GameBg = {};
    
        return GameBg;
    });
    

    一个磁盘文件应该只定义 1 个模块。多个模块可以使用内置优化工具将其组织打包。

    6.1 简单的值对

    如果一个模块仅含值对,没有任何依赖,可在 define() 中直接定义这些值对:

    define({
        color: "black",
        size: "unisize"
    });
    

    6.2 函数式定义

    如果一个模块没有任何依赖,但需要一个做初始化或配置工作的函数,则在 define() 中定义该函数,并将其传给 define()

    // GameBg.js
    define( function () {
        
        // 一些初始化或配置工作
        // ...
    
        // 界面背景单例对象
        var GameBg = (function () {
            var _element = document.querySelector('#game-bg'),
                _width = document.querySelector('#game-bg').offsetWidth,
                _height = document.querySelector('#game-bg').offsetHeight;
    
            return {
                getElement : function () {
                    return _element;
                },
                getWidth : function () {
                    return _width;
                },
                getHeight : function () {
                    return _height;
                },
                // ...
            }
        })();
        return GameBg;
    });
    

    6.3 存在依赖的函数式定义

    模块函数以参数 GameBgGrassLand 使用这两个以 ./scripts/views/GameBg./scripts/views/GrassLand 名称指定的模块。在这两个模块加载完毕之前,模块函数不会被调用。RequireJS 不鼓励模块定义全局变量,返回的 object 定义了 Bird 模块。这种定义模式下,Bird 不作为一个全局变量而存在。

    // Bird.js
    define(['GameBg', 'GrassLand'], function (GameBg, GrassLand) {
    
        // Bird对象的构造函数
        var Bird = function (idName) {
    
           // ...
        };
        return Bird;
    });
    

    6.4 将模块定义为一个函数

    模块的返回值类型 不一定是 一个对象,也可以是 一个函数。此处是一个返回了函数的模块定义:

    // Bird.js
    define(['GameBg', 'GrassLand'], function (GameBg, GrassLand) {
    
        // Bird对象的构造函数
        return function (idName) {
    
           // ...
        };
    });
    

    6.5 简单包装CommonJS来定义模块

    define(function(require, exports, module) {
        var a = require('a'),
            b = require('b');
    
        // 返回模块
        return function () {};
    });
    

    6.6 定义一个命名模块

    你可能会看到一些define()中包含了一个模块名称作为首个参数。

    这些常由优化工具生成。你也可以自己显式指定模块名称,但这使模块更不具备移植性——就是说若你将文件移动到其他目录下,你就得重命名。一般最好避免对模块硬编码,而是交给优化工具去生成。优化工具需要生成模块名以将多个模块打成一个包,加快到浏览器的载人速度。

    define("FlappyBird",
        ["GameBg", "GrassLand"],
        function(GameBg, GrassLand) {
            //Define foo/title object in here.
       }
    );

你可能感兴趣的:(AMD/RequireJS 使用入门)