早期的 版本没有块级作用域、没有类、没有包、也没有模块,这样会带来一些问题,如复用、依赖、冲突、代码组织混乱等,随着前端的膨胀,模块化显得非常迫切。

\"\"

前端模块化规范如下:

\"\"

一、前端模块化概要

1.1、模块概要

在早期的设计中就没有模块、包、类的概念,开发者需要模拟出类似的功能,来隔离、组织复杂的 代码,我们称为模块化。

模块就是一个实现特定功能的文件,有了模块我们就可以更方便的使用别人的代码,要用什么功能就加载什么模块

模块化开发的四点好处:

  (1)、 避免变量污染,命名冲突

  (2)、提高代码复用率

  (3)、提高了可维护性

  (4)、方便依赖关系管理

为了避免缺少模块带来的问题,我们可以看看程序员应对的历程:

1.2、函数封装

我们在讲函数的时候提到,函数一个功能就是实现特定逻辑的一组语句打包,而且 的作用域就是基于函数的,所以把函数作为模块化的第一步是很自然的事情,在一个文件里面编写几个相关函数就是最开始的模块了

//函数1
function fn1(){
  //statement
}
//函数2
function fn2(){
  //statement

这样在需要的以后夹在函数所在文件,调用函数就可以了

缺点:

污染了全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间没什么关系

1.3、对象封装

为了解决上面问题,对象的写法应运而生,可以把所有的模块成员封装在一个对象中

var myModule = {
var1: 1,

var2: 2,

fn1: function(){

},

fn2: function(){

}
}

这样我们在希望调用模块的时候引用对应文件,然后

myModule.fn2();

这样避免了变量污染,只要保证模块名唯一即可,同时同一模块内的成员也有了关系

缺陷:外部可以随意修改内部成员,这样就会产生意外的安全问题

myModel.var1 = 100;

1.4、立即执行函数表达式(IIFE)

可以通过立即执行函数表达式(IIFE),来达到隐藏细节的目的

var myModule = (function(){
var var1 = 1;
var var2 = 2;

function fn1(){

}

function fn2(){

}

return {
fn1: fn1,
fn2: fn2
};
})();

这样在模块外部无法修改我们没有暴露出来的变量、函数

缺点:功能相对较弱,封装过程增加了工作量、仍会导致命名空间污染可能、闭包是有成本的。

最初的作用仅仅是验证表单,后来会添加一些动画,但是这些js代码很多在一个文件中就可以完成了,所以,我们只需要在html文件中添加一个 标签。

后来,随着前端复杂度提高,为了能够提高项目代码的可读性、可扩展性等,我们的js文件逐渐多了起来,不再是一个js文件就可以解决的了,而是把每一个js文件当做一个模块。那么,这时的js引入方式是怎样的呢?大概是下面这样:

  <  src=\"jquery.js\"></ >
  <  src=\"jquery.artDialog.js\"></ >
  <  src=\"main.js\"></ >
  <  src=\"app1.js\"></ >
  <  src=\"app2.js\"></ >
  <  src=\"app3.js\"></ >

即简单的将所有的js文件统统放在一起。但是这些文件的顺序还不能出错,比如jquery需要先引入,才能引入jquery插件,才能在其他的文件中使用jquery。

优点:

相比于使用一个js文件,这种多个js文件实现最简单的模块化的思想是进步的。 

缺点:

污染全局作用域。 因为每一个模块都是暴露在全局的,简单的使用,会导致全局变量命名冲突,当然,我们也可以使用命名空间的方式来解决。

对于大型项目,各种js很多,开发人员必须手动解决模块和代码库的依赖关系,后期维护成本较高。

依赖关系不明显,不利于维护。 比如main.js需要使用jquery,但是,从上面的文件中,我们是看不出来的,如果jquery忘记了,那么就会报错。

1.5、模块化规范

常见的的 模块规范有:CommonJS、AMD、CMD、UMD、原生模块化

1.5.1、CommonJS

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

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

例如:

// foobar.js

//私有变量
var test = 123;

//公有方法
function foobar () {

this.foo = function () {
// do someing ...
}
this.bar = function () {
//do someing ...
}
}

//exports对象上的方法和变量是公有的
var foobar = new foobar();
exports.foobar = foobar;
//require方法默认读取js文件,所以可以省略js后缀
var test = require(\'./foobar\').foobar;

test.bar();

CommonJS 加载模块是同步的,所以只有加载完成才能执行后面的操作。像Node.js主要用于服务器的编程,加载的模块文件一般都已经存在本地硬盘,所以加载起来比较快,不用考虑异步加载的方式,所以CommonJS规范比较适用。但如果是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。所以就有了 AMD CMD 解决方案。

1.5.2、AMD((Asynchromous Module Definition) 异步模块定义

AMD 是 RequireJS 在推广过程中对模块定义的规范化产出

AMD异步加载模块。它的模块支持对象 函数 构造器 字符串 JSON等各种类型的模块。

适用AMD规范适用define方法定义模块。

//通过数组引入依赖 ,回调函数通过形参传入依赖
define([\'someModule1\', ‘someModule2’], function (someModule1, someModule2) {

function foo () {
/// someing
someModule1.test();
}

return {foo: foo}
});

AMD规范允许输出模块兼容CommonJS规范,这时define方法如下:

define(function (require, exports, module) {

var reqModule = require(\"./someModule\");
requModule.test();

exports.asplode = function () {
//someing
}
});

1.5.3、CMD(Common Module Definition)通用模块定义

CMD是SeaJS 在推广过程中对模块定义的规范化产出

CMD和AMD的区别有以下几点:

1.对于依赖的模块AMD是提前执行,CMD是延迟执行。不过RequireJS从2.0开始,也改成可以延迟执行(根据写法不同,处理方式不通过)。

2.CMD推崇依赖就近,AMD推崇依赖前置。

//AMD
define([\'./a\',\'./b\'], function (a, b) {

//依赖一开始就写好
a.test();
b.test();
});

//CMD
define(function (requie, exports, module) {

//依赖可以就近书写
var a = require(\'./a\');
a.test();

...
//软依赖
if (status) {

var b = requie(\'./b\');
b.test();
}
});

虽然 AMD也支持CMD写法,但依赖前置是官方文档的默认模块定义写法。

3.AMD的api默认是一个当多个用,CMD严格的区分推崇职责单一。例如:AMD里require分全局的和局部的。CMD里面没有全局的 require,提供 seajs.use()来实现模块系统的加载启动。CMD里每个API都简单纯粹。

SeaJS 和 RequireJS的主要区别 在此有解释

1.5.4、UMD

UMD是AMD和CommonJS的综合产物。

AMD 浏览器第一的原则发展 异步加载模块。

CommonJS 模块以服务器第一原则发展,选择同步加载,它的模块无需包装(unwrapped modules)。

这迫使人们又想出另一个更通用的模式UMD (Universal Module Definition)。希望解决跨平台的解决方案。

UMD先判断是否支持Node.js的模块(exports)是否存在,存在则使用Node.js模块模式。

在判断是否支持AMD(define是否存在),存在则使用AMD方式加载模块。

(function (window, factory) {
if (typeof exports === \' \') {

module.exports = factory();
} else if (typeof define === \'function\' && define.amd) {

define(factory);
} else {

window.eventUtil = factory();
}
})(this, function () {
//module ...
});

1.5.5、原生JS模块化(Native JS)

上述的模块都不是原生 模块。它们只不过是我们用模块模式(module pattern)、CommonJS 或 AMD 模仿的模块系统。

标准制定者在 TC39(该标准定义了 ECMA 的语法与语义)已经为 ECMA 6(ES6)引入内置的模块系统了。

ES6 为导入(importing)导出(exporting)模块带来了很多可能性。下面是很好的资源:

http://jsmodules.io/

http://exploringjs.com/

相对于 CommonJS 或 AMD,ES6 模块如何设法提供两全其美的实现方案:简洁紧凑的声明式语法和异步加载,另外能更好地支持循环依赖。

1.5.6、小结

AMD(异步模块定义) 是 RequireJS 在推广过程中对模块定义的规范化产出,CMD(通用模块定义)是SeaJS 在推广过程中被广泛认知。RequireJs出自dojo加载器的作者James Burke,SeaJs出自国内前端大师玉伯。

两者的区别如下:

RequireJS 和 SeaJS 都是很不错的模块加载器,两者区别如下:

1. 两者定位有差异。RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。SeaJS 则专注于 Web 浏览器端,同时通过 Node 扩展的方式可以很方便跑在 Node 服务器端

2. 两者遵循的标准有差异。RequireJS 遵循的是 AMD(异步模块定义)规范,SeaJS 遵循的是 CMD (通用模块定义)规范。规范的不同,导致了两者 API 的不同。SeaJS 更简洁优雅,更贴近 CommonJS Modules/1.1 和 Node Modules 规范。

3. 两者社区理念有差异。RequireJS 在尝试让第三方类库修改自身来支持 RequireJS,目前只有少数社区采纳。SeaJS 不强推,而采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略。

4. 两者代码质量有差异。RequireJS 是没有明显的 bug,SeaJS 是明显没有 bug。

5. 两者对调试等的支持有差异。SeaJS 通过插件,可以实现 Fiddler 中自动映射的功能,还可以实现自动 combo 等功能,非常方便便捷。RequireJS 无这方面的支持。

6. 两者的插件机制有差异。RequireJS 采取的是在源码中预留接口的形式,源码中留有为插件而写的代码。SeaJS 采取的插件机制则与 Node 的方式一致:开放自身,让插件开发者可直接访问或修改,从而非常灵活,可以实现各种类型的插件。

二、CommonJS

CommonJS就是一个 模块化的规范,该规范最初是用在服务器端NodeJS中,前端的webpack也是对CommonJS原生支持的。

根据这个规范,每一个文件就是一个模块,其内部定义的变量是属于这个模块的,不会对外暴露,也就是说不会污染全局变量。

CommonJS的核心思想就是通过 require 方法来同步加载所要依赖的其他模块,然后通过 exports 或者 module.exports 来导出需要暴露的接口。

CommonJS API编写应用程序,然后这些应用可以运行在不同的 解释器和不同的主机环境中。

2009年,美国程序员Ryan Dahl创造了node.js项目,将 语言用于服务器端编程。这标志\" 模块化编程\"正式诞生。因为老实说,在浏览器环境下,以前没有模块也不是特别大的问题,毕竟网页程序的复杂性有限;但是在服务器端,一定要有模块,与操作系统和其他应用程序互动,否则根本没法编程。NodeJS是CommonJS规范的实现,webpack 也是以CommonJS的形式来书写。

CommonJS定义的模块分为:{模块引用(require)} {模块定义(exports)} {模块标识(module)}
//require()用来引入外部模块;
//exports对象用于导出当前模块的方法或变量,唯一的导出口;
//module对象就代表模块本身。

Nodejs的模块是基于CommonJS规范实现的,通过转换也可以运行在浏览器端。

\"\"

特点:

1、所有代码都运行在模块作用域,不会污染全局作用域。
2、模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
3、模块加载的顺序,按照其在代码中出现的顺序。

2.1、NodeJS中使用CommonJS模块管理

1、模块定义

根据commonJS规范,一个单独的文件是一个模块,每一个模块都是一个单独的作用域,也就是说,在该模块内部定义的变量,无法被其他模块读取,除非为global对象的属性。

模块只有一个出口,module.exports对象,我们需要把模块希望输出的内容放入该对象。

mathLib.js模块定义

var message=\"Hello CommonJS!\";

module.exports.message=message;
module.exports.add=(m,n)=>console.log(m+n);

2、模块依赖

加载模块用require方法,该方法读取一个文件并且执行,返回文件内部的module.exports对象。

myApp.js 模块依赖

var math=require(\'./mathLib\');
console.log(math.message);
math.add(333,888);

3、测试运行

安装好node.JS

打开控制台,可以使用cmd命令,也可以直接在开发工具中访问

\"\"

运行

\"\"

2.2、在浏览器中使用CommonJS 模块管理

由于浏览器不支持 CommonJS 格式。要想让浏览器用上这些模块,必须转换格式。浏览器不兼容CommonJS的根本原因,在于缺少四个Node.js环境的变量(module、exports、require、global)。只要能够提供这四个变量,浏览器就能加载 CommonJS 模块。
var math = require(\'math\');
math.add(2, 3);

第二行math.add(2, 3),在第一行require(\'math\')之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于\"假死\"状态

而browserify这样的一个工具,可以把nodejs的模块编译成浏览器可用的模块,解决上面提到的问题。本文将详细介绍Browserify实现Browserify是目前最常用的CommonJS格式转换的工具

请看一个例子,b.js模块加载a.js模块

// a.js
var a = 100;
module.exports.a = a;

// b.js
var result = require(\'./a\');
console.log(result.a);

index.html直接引用b.js会报错,提示require没有被定义

\"\"

//index.html

<!DOCTYPE html>
<html lang=\"en\">
<head>
    <  charset=\"UTF-8\">
    < > </ >
</head>
<body>
<  src=\"myApp_01.js\"></ >
</body>
</html>

这时,就要使用Browserify了

【安装】

使用下列命令安装browserify

npm install -g browserify

【转换】

使用下面的命令,就能将b.js转为浏览器可用的格式bb.js

$ browserify myApp.js > myApp_01.js

转换结果:

\"\"

查看myapp_01.js,browserify将mathLib.js和myApp.js这两个文件打包为MyApp01.js,使其在浏览器端可以运行

(function () {
    function r(e, n, t) {
        function o(i, f) {
            if (!n[i]) {
                if (!e[i]) {
                    var c = \"function\" == typeof require && require;
                    if (!f && c) return c(i, !0);
                    if (u) return u(i, !0);
                    var a = new Error(\"Cannot find module \'\" + i + \"\'\");
                    throw a.code = \"MODULE_NOT_FOUND\", a
                }
                var p = n[i] = {exports: {}};
                e[i][0].call(p.exports, function (r) {
                    var n = e[i][1][r];
                    return o(n || r)
                }, p, p.exports, r, e, n, t)
            }
            return n[i].exports
        }

        for (var u = \"function\" == typeof require && require, i = 0; i < t.length; i++) o(t[i]);
        return o
    }

    return r
})()({
    1: [function (require, module, exports) {
        var message = \"Hello CommonJS!\";

        module.exports.message = message;
        module.exports.add = (m, n) => console.log(m + n);


    }, {}], 2: [function (require, module, exports) {
        var math = require(\'./mathLib\');
        console.log(math.message);
        math.add(333, 888);
    }, {\"./mathLib\": 1}]
}, {}, [2]);

index.html引用bb.js,控制台显示100

//index.html
<!DOCTYPE html>
<html lang=\"en\">
<head>
<  charset=\"UTF-8\">
< >Document</ >
</head>
<body>
<  src=\"bb.js\"></ > 
</body>
</html> 
 
运行结果:
 
\"\"
 
虽然 Browserify 很强大,但不能在浏览器里操作,有时就很不方便。
 
纯浏览器的 CommonJS 模块加载器 require1k (https://github.com/Stuk/require1k)。完全不需要命令行,直接放进浏览器即可。

优点:

CommonJS规范在服务器端率先完成了 的模块化,解决了依赖、全局变量污染的问题,这也是js运行在服务器端的必要条件。

缺点:

此文主要是浏览器端js的模块化, 由于 CommonJS 是同步加载模块的,在服务器端,文件都是保存在硬盘上,所以同步加载没有问题,但是对于浏览器端,需要将文件从服务器端请求过来,那么同步加载就不适用了,所以,CommonJS是不太适用于浏览器端。

三、AMD

3.1、概要

CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。AMD规范则是非同步加载模块,允许指定回调函数。由于Node.js主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS规范比较适用。但是,如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用AMD规范。而AMD规范的实现,就是大名鼎鼎的require.js了。

3.1、require.js

Asynchronous Module Definition,中文名是异步模块定义。它是一个在浏览器端模块化开发的规范,由于不是js原生支持,使用AMD规范进行页面开发需要用到对应的函数库,也就是大名鼎鼎的RequireJS,实际上AMD是RequireJS在推广过程中对模块定义的规范化的产出。Asynchronous Module Definition,中文名是异步模块。它是一个在浏览器端模块化开发的规范,由于不是js原生支持,使用AMD规范进行页面开发需要用到对应的函数库,也就是大名鼎鼎的RequireJS,实际上AMD是RequireJS在推广过程中对模块定义的规范化的产出。

官网:http://www.requirejs.cn

requireJS主要解决两个问题:

1 多个js文件可能有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器。

2 js加载的时候浏览器会停止页面渲染,加载文件愈多,页面失去响应的时间愈长。

//定义模块
define([\'dependency\'],function(){ var name = \'foo\'; function printName(){ console.log(name); } return { printName:printName } }) //加载模块 require([\'myModule\'],function(my){ my.printName(); })

语法:

AMD标准中,定义了下面两个API:

1.require([module], callback)

2. define(id, [depends], callback)

即通过define来定义一个模块,然后使用require来加载一个模块。 并且,require还支持CommonJS的模块导出方式。

requireJS定义了一个函数define,它是全局变量,用来定义模块。

define(id,dependencies,factory)

——id 可选参数,用来定义模块的标识,如果没有提供该参数,脚本文件名(去掉拓展名)

——dependencies 是一个当前模块用来的模块名称数组

——factory 工厂方法,模块初始化要执行的函数或对象,如果为函数,它应该只被执行一次,如果是对象,此对象应该为模块的输出值。

在页面上使用require函数加载模块;

require([dependencies], function(){});

require()函数接受两个参数:

——第一个参数是一个数组,表示所依赖的模块;

——第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块

定义alert模块:

define(function () {
var alertName = function (str) {
alert(\"I am \" + str);
}
var alertAge = function (num) {
alert(\"I am \" + num + \" years old\");
}
return {
alertName: alertName,
alertAge: alertAge
};
});

引入模块:

require([\'alert\'], function (alert) {
alert.alertName(\'zhangsan\');
alert.alertAge(21);
});

但是,在使用require.js的时候,我们必须要提前加载所有的依赖,然后才可以使用,而不是需要使用时再加载。

优点:

适合在浏览器环境中异步加载模块。可以并行加载多个模块。

缺点:

提高了开发成本,并且不能按需加载,而是必须提前加载所有的依赖。

3.3、使用技巧

请记住使用requirejs的口诀:两函数一配置一属性

3.3.1、data-main属性

requirejs需要一个根来作为搜索依赖的开始,data-main用来指定这个根。

<  src=\" s/require.js\" data-main=\" s/app.js\"></ >

这里就指定了根是app.js,只有直接或者间接与app.js有依赖关系的模块才会被插入到html中。

3.3.2、require.config() 配置

通过这个函数可以对requirejs进行灵活的配置,其参数为一个配置对象,配置项及含义如下:

Url——用于加载模块的根路径。

paths——用于映射不存在根路径下面的模块路径。

shims——配置在脚本/模块外面并没有使用RequireJS的函数依赖并且初始化函数。假设underscore并没有使用 RequireJS定义,但是你还是想通过RequireJS来使用它,那么你就需要在配置中把它定义为一个shim。

deps——加载依赖关系数组

require.config({
//默认情况下从这个文件开始拉去取资源
     Url:\' s/app\',
//如果你的依赖模块以pb头,会从 s/pb加载模块。
    paths:{
        pb:\'../pb\'
    },
// load backbone as a shim,所谓就是将没有采用requirejs方式定义
//模块的东西转变为requirejs模块
    shim:{
        \'backbone\':{
            deps:[\'underscore\'],
            exports:\'Backbone\'
        }
    }
});

3.3.3、define()函数

该函数用于定义模块。形式如下。

//logger.js
define([\"a\"], function(a) {
    \'use strict\';
    function info() {
        console.log(\"我是私有函数\");
    }
    return {
        name:\"一个属性\",
        test:function(a){
            console.log(a+\"你好!\");
            a.f();
            info();
        }
    }
});

define函数就受两个参数。

* 第一个是一个字符串数组,表示你定义的模块依赖的模块,这里依赖模块a;

* 第二个参数是一个函数,参数是注入前面依赖的模块,顺序同第一参数顺序。在函数中可做逻辑处理,通过return一个对象暴露模块的属性和方法,不在return中的可以认为是私有方法和私有属性。

3.3.4、require()函数

该函数用于调用定义好的模块,可以是用define函数定义的,也可以是一个shim。形式如下:

//app.js
require([\'logger\'], function (logger) {
    logger.test(\"tom\");
    console.log(logger.name);
});
//输出结果:
//tom你好!
//不确定(取决于a模块的f方法)
//我是私有函数
//一个属性

示例:

\"\"\"\"
//index.html
<!DOCTYPE html>
<html lang=\"en\">
    <head>
    <  charset=\"UTF-8\">
    <  name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
    <  http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">
    < >Document</ >
    </head>
    <body>
    <  src=\"js/require.js\" data-main=\"app.js\"></ >
    </body>
    </html>

< >
//app.js
require.config({
//By default load any module IDs from js/
     Url: \'js\',
//except, if the module ID starts with \"pb\"
    paths: {
        pb: \'../pb\'
    },
    shim: {
        \'world\': {
            deps:[\'animalWorld\'],
            // use the global \'Backbone\' as the module name.
            exports: \'world\'
        }
    }
});
require([\'cat\',\'dog\',\'world\'], function (cat,dog,world) {
    world.world();
    cat.say();
    dog.say();
});

//animal.js
define([], function() {
    \'use strict\';
    function _showName(name){
        console.log(name);
    }
    return {
        say(words){
            console.log(words);
        },
        showName(name){ //练习私有方法
            _showName(name);
        }
    }
});

//cat.js
define([
    \'pb/animal\'
], function(animal) {
    \'use strict\';
    return {
        say(){
            animal.say(\"喵喵\");
            animal.showName(\"\");
        }
    }
});

//dog.js
define([
    \'pb/animal\'
], function(animal) {
    \'use strict\';
    return {
        say(){
            animal.say(\"汪汪\");
            animal.showName(\"\");
        }
    }
});

//animalWorld.js
window.animal = function() {
    console.log(\"这里是动物世界!\");
}
world.js

define([], function() {
    \'use strict\';
    return {
        world(){
            animal();
        }
    }
});
</ >
View Code

3.4、简单示例

目录结构:

\"\"

模块定义:

 mathModule.js

define(function () {
   return{
       message:\"Hello AMD!\",
       add:function (n1,n2) {
           return n1+n2;
       }
   }
});

模块依赖:

 app.js

require([\'mathModule\'],function (mathModule) {
    console.log(mathModule.message);
    console.log(mathModule.add(100,200));
});

测试运行:

<!DOCTYPE html>
<html lang=\"en\">
<head>
    <  charset=\"UTF-8\">
    < > </ >
</head>
<body>
<  src=\"../js/require2.1.11.js\" data-main=\"app.js\"></ >
</body>
</html>

结果:

\"\"

3.5、加载 文件

RequireJS的目标是鼓励代码的模块化,它使用了不同于传统< >标签的脚本加载步骤。可以用它来加速、优化代码,但其主要目的还是为了代码的模块化。它鼓励在使用脚本时以module ID替代URL地址。

RequireJS以一个相对于 Url的地址来加载所有的代码。 页面顶层< >标签含有一个特殊的属性data-main,require.js使用它来启动脚本加载过程,而 Url一般设置到与该属性相一致的目录。下列示例中展示了 Url的设置:

<!--This sets the  Url to the \" s\" directory, and
loads a   that will have a module ID of \'main\'-->
<  data-main=\" s/main.js\" src=\" s/require.js\"></ >

Url亦可通过RequireJS config手动设置。如果没有显式指定config及data-main,则默认的 Url为包含RequireJS的那个HTML页面的所属目录。

RequireJS默认假定所有的依赖资源都是js脚本,因此无需在module ID上再加\".js\"后缀,RequireJS在进行module ID到path的解析时会自动补上后缀。你可以通过paths config设置一组脚本,这些有助于我们在使用脚本时码更少的字。

有时候你想避开\" Url + paths\"的解析过程,而是直接指定加载某一个目录下的脚本。此时可以这样做:如果一个module ID符合下述规则之一,其ID解析会避开常规的\" Url + paths\"配置,而是直接将其加载为一个相对于当前HTML文档的脚本:

以 \".js\" 结束.
以 \"/\" 开始.
包含 URL 协议, 如 \"http:\" or \"https:\".

一般来说,最好还是使用 Url及\"paths\" config去设置module ID。它会给你带来额外的灵活性,如便于脚本的重命名、重定位等。 同时,为了避免凌乱的配置,最好不要使用多级嵌套的目录层次来组织代码,而是要么将所有的脚本都放置到 Url中,要么分置为项目库/第三方库的一个扁平结构,如下:

www/
index.html
js/
app/
sub.js
lib/
jquery.js
canvas.js
app.js

index.html:

<  data-main=\"js/app.js\" src=\"js/require.js\"></ >

app.js:

requirejs.config({
//By default load any module IDs from js/lib
     Url: \'js/lib\',
//except, if the module ID starts with \"app\",
//load it from the js/app directory. paths
//config is relative to the  Url, and

					
				
收藏 打印
您的足迹: