require.js - 入门指南、进阶使用详解(附样例)

现在项目大都使用模块化开发,而 RequireJS 作为 AMD 模块开发的典范,还是很值得学习使用的。

目录

一、AMD 规范

1,AMD 基本介绍

2,AMD 模块规范

二、RequireJS 介绍 

1,什么是 RequireJS

2,使用 RequireJS 的好处

三、RequireJS 的配置和使用

1,下载最新版的 require.js

2,创建一个如下目录结构

3,效果图

4,代码说明

四、require.configh 函数配置说明

1,baseUrl

2,paths

3,shim

4,map 

5,config

五、不同类型的模块定义

1,简单的键值对模块

2,只有一个主函数的模块

3,包含多个函数方法和变量的模块

六 、相对路径的规则

七、循环依赖问题解决

1,什么是循环依赖

2,问题解决

八、错误处理

1,require 的错误回调函数

2,通过"paths"数组配置

3,全局的错误捕获:require.onError


一、AMD 规范

1,AMD 基本介绍

  • AMD 全称为 Asynchromous Module Definition(异步模块定义) 
  • AMD 是 RequireJS 在推广过程中对模块定义的规范化产出,它是一个在浏览器端模块化开发的规范。
  • AMD 模式可以用于浏览器环境并且允许非同步加载模块,同时又能保证正确的顺序,也可以按需动态加载模块。

 

2,AMD 模块规范

  • AMD 通过异步加载模块。模块加载不影响后面语句的运行。所有依赖某些模块的语句均放置在回调函数中。
  • AMD 规范只定义了一个函数 define,通过 define 方法定义模块。该函数的描述如下:

define(id?, dependencies?, factory)

  • id:指定义中模块的名字(可选)。如果没有提供该参数,模块的名字应该默认为模块加载器请求的指定脚本的名字。如果提供了该参数,模块名必须是“顶级”的和绝对的(不允许相对名字)。
  • dependencies:当前模块依赖的,已被模块定义的模块标识的数组字面量(可选)。
  • factory:一个需要进行实例化的函数或者一个对象。
  • AMD 规范允许输出模块兼容 CommonJS 规范,这时 define 方法如下:

1

2

3

4

5

6

7

8

define(function (require, exports, module) {

    var reqModule = require("./someModule");

    requModule.test();

      

    exports.asplode = function () {

        //someing

    }

});

 

二、RequireJS 介绍 

1,什么是 RequireJS

RequireJS 是一个 JavaScript 模块加载器。它非常适合在浏览器中使用,但它也可以用在其他脚本环境, 比如 Rhino 和 Node。使用 RequireJS 加载模块化脚本将提高代码的加载速度和质量。

 

2,使用 RequireJS 的好处

  • 异步“加载”。使用 RequireJS,会在相关的 js 加载后执行回调函数,这个过程是异步的,所以它不会阻塞页面。
  • 按需加载。通过 RequireJS,你可以在需要加载 js 逻辑的时候再加载对应的 js 模块,不需要的模块就不加载,这样避免了在初始化网页的时候发生大量的请求和数据传输。
  • 更加方便的模块依赖管理。通过 RequireJS 的机制,你能确保在所有的依赖模块都加载以后再执行相关的文件,所以可以起到依赖管理的作用。
  • 更加高效的版本管理。比如原来我们使用的 script 脚本引入的方式来引入一个 jQuery2.x 的文件,但如果有 100 个页面都是这么引用的,如果想换成 jQuery3.x,那你就不得不去改这 100 个页面。而使用 requireJS 只需要改一处地方,即修改 config 中 jQuery 的 path 映射即可。
  • 当然还有一些诸如 cdn 加载不到 js 文件,可以请求本地文件等其它的优点,这里就不一一列举了。

 

三、RequireJS 的配置和使用

1,下载最新版的 require.js

下载地址:http://requirejs.org/docs/download.html

 

2,创建一个如下目录结构

(1)lib 文件夹下放置一些需要用到的 js 库,这里除了 require.js 外,还有 jquery。

(2)script 文件夹下放置 RequireJS 的入口 js、以及模块 js 文件。

(3)index.html 则为主页面。

原文:RequireJS - 入门指南、进阶使用详解(附样例)

 

3,效果图

(1)页面初始化的时候显示一个按钮。

(2)点击按钮会调用 hello 模块的方法,将信息显示在页面上。

   原文:RequireJS - 入门指南、进阶使用详解(附样例)      原文:RequireJS - 入门指南、进阶使用详解(附样例)

 

4,代码说明

(1)index.html

1

2

3

4

5

6

7

8

9

10

11

12

<!DOCTYPE html>

<html>

  <head>

    <meta charset="utf-8">

    <title>hangge.com</title>

    <script data-main="js/script/main.js" src="js/lib/require.js"></script>

  </head>

  <body>

    <div id="messageBox"></div>

    <button id="btn" type="button" name="button">点击</button>

  </body>

</html>

注意 script 标签,除了指定 require.js 路径外,还有个 data-main 属性:
这属性指定在加载完 reuqire.js 后,就用 requireJS 加载该属性值指定路径下的 JS 文件并运行,所以一般该 JS 文件称为主 JS 文件(其 .js 后缀可以省略)。

 

(2)main.js

1

2

3

4

5

6

7

8

9

10

11

12

require.config({

    baseUrl: 'js',

    paths: {

        jquery: 'lib/jquery-1.11.1',

    }

});

 

require(['jquery', 'script/hello'],function ($, hello) {

    $("#btn").click(function(){

      hello.showMessage("hangge.com");

    });

});

要改变 RequireJS 的默认配置,可以使用 require.configh 函数传入一个可选参数对象。下面是一些可以使用的配置:

  • baseUrl:用于加载模块的根路径。在配置这个属性后,以后的文件都是在这个路径下查找内容了。
  • paths:用于一些常用库或者文件夹路径映射,方便后面使用,省得每次都要输入一长串路径。(js 文件的后缀可以省略)
  • shim:加载非 AMD 规范的 js,并解决其载入顺序。

require方法:

require 函数用于加载模块依赖,这里我们加载了 jQuery 以及 hello 这个自定义模块。在加载完毕的回调中,给按钮添加个点击事件,同时点击后会调用 hello 模块中的 showMessage 方法。

 

(3)hello.js

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

define(['jquery'],function($){

    //变量定义区

    var moduleName = "hello module";

    var moduleVersion = "1.0";

 

    //函数定义区

    var showMessage = function(name){

        if(undefined === name){

            return;

        }else{

            $('#messageBox').html('欢迎访问 ' + name);

        }

    };

 

    //暴露(返回)本模块API

    return {

        "moduleName":moduleName,

        "version": moduleVersion,

        "showMessage": showMessage

    }

});

我们通过 define 方法定义一个 js 模块,并通过 return 对外暴露出接口(两个属性,一个方法)。同时该模块也是依赖于 jQuery。

 

四、require.configh 函数配置说明

要改变 RequireJS 的默认配置,可以使用 require.configh 函数传入一个可选参数对象。上面只演示了其中 baseUrl 和 paths 这两个配置,下面是一个完整的配置:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

require.config({

    baseUrl: 'js',

    paths: {

        jquery: 'lib/jquery-1.11.1'

    },

    shim: {

        'backbone': {

            deps: ['underscore', 'jquery'],

            exports: 'Backbone'

        },

        'underscore': {

            exports: '_'

        },

        'modal':{//模态框插件不是模块化

            deps:['jquery'],

            export:"modal"

        },

    },

    map: {

        'script/newmodule': {

            'foo': 'foo1.2'

        },

        'script/oldmodule': {

            'foo': 'foo1.0'

        }

    },

    config: {

        'script/bar': {

            size: 'large'

        },

        'script/baz': {

            color: 'blue'

        }

    }

});

 

1,baseUrl

用于加载模块的根路径。在配置这个属性后,以后的文件都是在这个路径下查找内容了。

 

2,paths

用于一些常用库或者文件夹路径映射,方便后面使用,省得每次都要输入一长串路径。(js 文件的后缀可以省略)

 

3,shim

虽然目前已经有一部分流行的函数库(比如 jQuery)符合 AMD 规范,但还有很多库并不符合。shim 就是为了加载这些非 AMD 规范的 js,并解决其载入顺序的。

比如上面样例,我们想通过 RequireJS 来使用 backbone,那么你就需要在配置中把它定义为一个 shim。同时通过 deps 配置其依赖关系,可以保证 underscore、jquery 先被加载。

shim配置的注意事项:

  • shim 配置仅设置了代码的依赖关系,想要实际加载 shim 指定的或涉及的模块,仍然需要一个常规的 require/define 调用。设置 shim 本身不会触发代码的加载。
  • 请仅使用其他"shim"模块作为 shim 脚本的依赖,或那些没有依赖关系,并且在调用 define() 之前定义了全局变量(如 jQuery 或 lodash )的 AMD 库。否则,如果你使用了一个 AMD 模块作为一个 shim 配置模块的依赖,在 build 之后,AMD 模块可能在 shim 托管代码执行之前都不会被执行,这会导致错误。终极的解决方案是将所有 shim 托管代码都升级为含有可选的 AMD define() 调用。

 

4,map 

(1)对于给定的模块前缀,使用一个不同的模块 ID 来加载该模块。该手段对于某些大型项目很重要。

比如上面配置以后,不同的模块会使用不同版本的"foo":

  • 当 some/newmodule 调用了 require('foo'),它将获取到 foo1.2.js 文件。
  • 当 some/oldmodule 调用 require('foo'),时它将获取到 foo1.0.js 文件。

 

(2)map 还支持“*”,意思是“对于所有的模块加载,使用本 map 配置”。如果还有更细化的 map 配置,会优先于“*”配置。

比如下面配置,除了“some/oldmodule”外的所有模块,当要用“foo”时,都使用“foo1.2”来替代。

1

2

3

4

5

6

7

8

9

10

requirejs.config({

    map: {

        '*': {

            'foo': 'foo1.2'

        },

        'some/oldmodule': {

            'foo': 'foo1.0'

        }

    }

});

 

5,config

我们需要将配置信息传给一个模块。这些配置往往是 application 级别的信息,需要一个手段将它们向下传递给模块。这个通过 requirejs.config() 的 config 配置项就可以实现。

 

(1)可以通过加载特殊的依赖“module”来获取这些信息。

1

2

3

4

5

// script/info.js

define(['module'], function (module) {

    var color = module.config().color;  //blue

 

});


(2)也可通过符合 CommonJS 规范的模块获取

1

2

3

4

5

// script/info.js

define(function (require, exports, module) {

    var color = module.config().color;  //blue

 

});

 

五、不同类型的模块定义

  • 模块不同于传统的脚本文件,它良好地定义了一个作用域来避免全局名称空间污染。
  • 它可以显式地列出其依赖关系,并以函数(定义此模块的那个函数)参数的形式将这些依赖进行注入,而无需引用全局变量。同时因为无需创建全局变量,甚至可以做到在同一个页面上同时加载同一模块的不同版本。
  • RequireJS 的模块语法允许它尽快地加载多个模块,虽然加载的顺序不定,但依赖的顺序最终是正确的。
  • 一个磁盘文件应该只定义 1 个模块。多个模块可以使用内置优化工具将其组织打包。

 

1,简单的键值对模块

(1)如果一个模块仅含键值对,没有任何依赖,则在 define() 中定义这些值对就好了

1

2

3

4

5

// script/shirt.js

define({

    color: "black",

    size: "unisize"

});

 

下面调用并测试这个模块

1

2

3

require(['script/shirt'],function (shirt) {

    console.log("颜色:" + shirt.color);

});

原文:RequireJS - 入门指南、进阶使用详解(附样例)


(2)下面还是一个简单的键值对模块,没有任何依赖,但需要做一些初始化 setup 工作。

1

2

3

4

5

6

7

8

9

10

// script/shirt.js

define(function () {

    //在这里做一些setup工作

    console.log("Do setup work here...");

 

    return {

        color: "black",

        size: "unisize"

    }

});

 

2,只有一个主函数的模块

(1)没有任何依赖

1

2

3

4

5

6

// script/info.js

define(function () {

    return function (){

      alert("欢迎访问 hangge.com");

    };

});

 

下面调用并测试这个模块:

1

2

3

require(['script/info'],function (info) {

    info();

});


(2)存在相关依赖

1

2

3

4

5

6

// script/info.js

define(['jquery'],function($){

    return function (){

      $('#messageBox').html('欢迎访问 hangge.com');

    };

});

 

下面调用并测试这个模块:

1

2

3

require(['script/info'],function (info) {

    info();

});

 

3,包含多个函数方法和变量的模块

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

// script/hello.js

define(['jquery'],function($){

    //变量定义区

    var moduleName = "hello module";

    var moduleVersion = "1.0";

 

    //函数定义区

    var showMessage = function(name){

        if(undefined === name){

            return;

        }else{

            $('#messageBox').html('欢迎访问 ' + name);

        }

    };

 

    //暴露(返回)本模块API

    return {

        "moduleName":moduleName,

        "version": moduleVersion,

        "showMessage": showMessage

    }

});

 

下面调用并测试这个模块:

1

2

3

4

5

require(['jquery', 'script/hello'],function ($, hello) {

    $("#btn").click(function(){

      hello.showMessage("hangge.com");

    });

});

 

六 、相对路径的规则

不管是在配置中写路径还是直接在 require 函数中写路径,我们需要了解 requireJS 在不同情况下的相对路径。以下是相对路径的规则:

  • 如果 <script/> 标签引入 require.js 时没有指定 data-main 属性,则以引入该 js 的 html 文件所在的路径为根路径。
  • 如果有指定 data-main 属性,也就是有指定入口文件,则以入口文件所在的路径为根路径。
  • 如果在 require.config() 中有配置 baseUrl,则以 baseUrl 的路径为根路径。

以上三条优先级逐级提升,如果有重叠,后面的根路径覆盖前面的根路径。

 

七、循环依赖问题解决

1,什么是循环依赖

(1)假设我们有如下 a、b 两个互相依赖的模块(a 依赖 b,b 同时依赖 a)

--- a.js ---

1

2

3

4

5

6

7

8

9

10

11

12

// script/a.js

define(['script/b'],function(b){

    //函数定义区

    var showMessage = function(){

        console.log("欢迎访问 hangge.com" + b.getName());

    };

 

    //暴露(返回)本模块API

    return {

        "showMessage": showMessage

    }

});

 

--- b.js ---

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

// script/a.js

define(['script/a'],function(a){

    //函数定义区

    var doSomething = function(){

        a.showMessage();

    };

 

    var getName = function(){

        return "hangge.com";

    };

 

    //暴露(返回)本模块API

    return {

        "doSomething": doSomething,

        "getName": getName

    }

});


(2)我们如果调用 b 模块的 doSomething 方法

1

2

3

require(['script/b'],function (b) {

    b.doSomething();

});

发现 b 调用 a 正常,但是 a 中调用 b 方法会报 undefined 错误。

原文:RequireJS - 入门指南、进阶使用详解(附样例)

 

2,问题解决

循环依赖比较罕见,它也是一个重构代码重新设计的警示灯。但不管怎样,有时候还是要用到循环依赖。对于循环依赖,只要依赖环中任何一条边是运行时依赖,这个环理论上就是活的。而如果全部边都是装载时依赖,这个环就是死的。

为避免循环依赖引发的问题,其实只要把一边改成运行时依赖就可以了,我们有如下几种方法。

 

方法1:使用 require() 去获取一个模块

我们对模块 a 进行如下修改,即不再依赖前置加载。而是通过引入 require 依赖,然后再通过 require() 方法去载入模块 b,并在回调中去执行。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

// script/a.js

define(['require'],function(require){

    //函数定义区

    var showMessage = function(){

        require(['script/b'],function (b) {

          console.log("欢迎访问 " + b.getName());

        });

    };

 

    //暴露(返回)本模块API

    return {

        "showMessage": showMessage

    }

});

再次调用 b 模块的 doSomething 方法,可以发现运行成功了:

原文:RequireJS - 入门指南、进阶使用详解(附样例)

 

方法2:通过注入 exports 来解决

我们将模块 b 修改成如下代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

// script/b.js

define(['script/a', 'exports'],function(a, exports){

    //函数定义区

    var doSomething = function(){

        a.showMessage();

    };

 

    var getName = function(){

        return "hangge.com";

    };

 

    //暴露本模块API

    exports.doSomething = doSomething;

    exports.getName = getName;

});


方法3:直接使用 CommonJS 规范定义模块
我们将模块 b 修改成如下代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

// script/b.js

define(function(require, exports, module) {

  var a = require("script/a");

 

  //函数定义区

  var doSomething = function(){

      a.showMessage();

  };

 

  var getName = function(){

      return "hangge.com";

  };

 

  //暴露本模块API

  exports.doSomething = doSomething;

  exports.getName = getName;

});

 

八、错误处理

一般我们使用 RequireJS 时碰到的错误主要是 404(未找到)错误、网络超时或加载的脚本含有错误。RequireJS 提供了如下三种方式来处理这些错误。

 

1,require 的错误回调函数

下面代码先用 jQuery 库的一个 CDN 版本,如果其加载出错,则切换到本地版本。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

require.config({

    //如果你打算支持Internet Explorer捕获加载错,并使用了define()或shim,则要将enforceDefine设置为true。

    enforceDefine: true,

    paths: {

        jquery: 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min'

    }

});

 

require(['jquery'], function ($) {

    //Do something with $ here

}, function (err) {

    //获取加载错误的模块

    var failedId = err.requireModules && err.requireModules[0];

    if (failedId === 'jquery') {

        //通过undef方法让所有依赖jQuery的模块都暂不加载,直到正确的jQuery被加载。

        requirejs.undef(failedId);

        //将jQuery设置为本地路径

        requirejs.config({

            paths: {

                jquery: 'local/jquery'

            }

        });

        //再次尝试加载jQuery

        require(['jquery'], function () {});

    } else {

        //处理其他模块错误

    }

});

 

2,通过"paths"数组配置

paths 配置项允许我们使用数组,下面代码先尝试加载 CDN 版本,如果出错,则退回到本地的 lib/jquery.js。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

requirejs.config({

    //如果你打算支持Internet Explorer捕获加载错,并使用了define()或shim,则要将enforceDefine设置为true。

    enforceDefine: true,

    paths: {

        jquery: [

            'http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min',

            //如果CDN文件加载失败,则自动加载本地版本

            'lib/jquery'

        ]

    }

});

 

//Later

require(['jquery'], function ($) {

   

});

 

3,全局的错误捕获:require.onError

为了捕获在局域的 errback 中未捕获的异常,可以重载 require.onError() 来实现全局的异常捕获。

1

2

3

4

5

6

7

require.onError = function (err) {

    console.log(err.requireType);

    if (err.requireType === 'timeout') {

        console.log('modules: ' + err.requireModules);

    }

    throw err;

};


原文出自:www.hangge.com  转载请保留原文链接:https://www.hangge.com/blog/cache/detail_1702.html

  • 13
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`js.commonjs-require-cwd` 参数用于指定 CommonJS 模块的当前工作目录。默认情况下,该参数设置为 `false`,表示使用 Node.js 的默认行为。如果需要自定义当前工作目录,可以将该参数设置为一个字符串,表示当前工作目录的绝对路径。 如,如果你的项目文件结构如下: ``` my-project/ ├── node_modules/ ├── src/ │ ├── index.js │ └── utils/ │ └── helper.js └── package.json ``` 其中,`index.js` 文件中使用了 `helper.js` 模块: ```js const helper = require('./utils/helper'); ``` 如果你想要在 Webpack 中模拟 Node.js 的 `__dirname` 行为,可以将 `js.commonjs-require-cwd` 参数设置为 `__dirname`,如下所示: ```js module.exports = { // ... module: { rules: [ { test: /\.js$/, loader: 'babel-loader', options: { presets: ['@babel/preset-env'], }, }, ], }, resolve: { fallback: { path: require.resolve('path-browserify'), }, }, experiments: { asyncWebAssembly: true, }, output: { globalObject: 'this', }, // 设置 js.commonjs-require-cwd 参数 // 表示使用当前文件所在的目录作为 CommonJS 模块的当前工作目录 // 这样可以模拟 Node.js 的 __dirname 行为 experiments: { asyncWebAssembly: true, }, output: { globalObject: 'this', }, // 设置 js.commonjs-require-cwd 参数 // 表示使用当前文件所在的目录作为 CommonJS 模块的当前工作目录 // 这样可以模拟 Node.js 的 __dirname 行为 node: { __dirname: true, }, // ... }; ``` 这样,在 Webpack 中编译 `index.js` 文件时,`helper.js` 模块的路径就会根据 `index.js` 文件所在的目录来解析。如果 `js.commonjs-require-cwd` 参数设置为 `false`,则 `helper.js` 模块的路径会根据 Webpack 的当前工作目录来解析,可能会导致路径不正确的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值