amd
Posted zyy-bk
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了amd相关的知识,希望对你有一定的参考价值。
requireJS
define([module-name?], [array-of-dependencies?], [module-factory-or-object]);
module-name: 模块标识,可以省略
array-of-dependencies: 所依赖的模块,可以省略
module-factory-or-object: 模块的实现,或者一个javascript对象
1.加载
<script src="js/require.js"></script>
2.入口文件(当requireJS自身加载执行后,就会再次异步加载data-main属性指向的main.js。这个main.js是当前网页所有逻辑的入口,理想情况下,整个网页只需要这一个script标记,利用requireJS加载依赖的其它文件)
<script data-main="scripts/main" src="js/require.js"></script>
[注意]在main.js中所设置的脚本是异步加载的。所以如果在页面中配置了其它JS加载,则不能保证它们所依赖的JS已经加载成功
在RequireJS内部,会使用head.appendChild()将每一个模块依赖加载为一个script标签。RequireJS等待所有的依赖加载完毕,计算出模块定义函数正确调用顺序,然后依次调用它们
3.模块
模块不同于传统的脚本文件,它良好地定义了一个作用域来避免全局名称空间污染。它可以显式地列出其依赖关系,并以函数(定义此模块的那个函数)参数的形式将这些依赖进行注入,而无需引用全局变量。RequireJS的模块是模块模式的一个扩展,其好处是无需全局地引用其他模块
RequireJS的模块语法允许它尽快地加载多个模块,虽然加载的顺序不定,但依赖的顺序最终是正确的。同时因为无需创建全局变量,甚至可以做到在同一个页面上同时加载同一模块的不同版本
一个文件应该只定义1个模块。多个模块可以使用内置优化工具将其组织打包
如果我们的代码不依赖任何其他模块,那么可以直接写入javascript代码
// main.js require([‘moduleA‘], function(a){ console.log(a); }); //moduleA.js define(function(){ return 1; })
define()定义的模块可以被调用,而require()不可以。主模块main.js是入口文件,需要调用别的模块,而不需要被别的模块调用,所以使用require()或define()都可以
4.上面的模块moduleA中,回调函数返回了一个数字。而实际上,模块可以有多种形式,比如一个简单的值对
define({ color: "black", size: "unisize" });
5.如果一个模块没有任何依赖,但需要一个做setup工作的函数,则在define()中定义该函数,并将其传给define()
define(function () { //Do setup work here return { color: "black", size: "unisize" } });
6.如果模块存在依赖:则第一个参数是依赖的名称数组;第二个参数是函数,在模块的所有依赖加载完毕后,该函数会被调用来定义该模块,因此该模块应该返回一个定义了本模块的object。依赖关系会以参数的形式注入到该函数上,参数列表与依赖名称列表一一对应
//moduleA.js define([‘moduleB‘], function(b) { var num = 10; return b.add(num); } ); ////moduleB.js define({ add: function(n){ return n+1; } });
这些常由优化工具生成。也可以自己显式指定模块名称,但这使模块更不具备移植性——就是说若将文件移动到其他目录下,就得重命名。一般最好避免对模块硬编码,而是交给优化工具去生成。优化工具需要生成模块名以将多个模块打成一个包,加快到浏览器的载入速度
8.路径配置
html中的base元素用于指定文档里所有相对URL地址的基础URL,requireJS的baseUrl跟这个base元素起的作用是类似的,由于requireJS总是动态地请求依赖的JS文件,所以必然涉及到一个JS文件的路径解析问题,requireJS默认采用一种baseUrl + moduleID的解析方式,requireJS对它的处理遵循如下规则:
1、在没有使用data-main和config的情况下,baseUrl默认为当前页面的目录
2、在有data-main的情况下,main.js前面的部分就是baseUrl,比如上面的js/
3、在有config的情况下,baseUrl以config配置的为准
上述三种方式,优先级由低到高排列
RequireJS以一个相对于baseUrl的地址来加载所有的代码。页面顶层script标签含有一个特殊的属性data-main,require.js使用它来启动脚本加载过程,而baseUrl一般设置到与该属性相一致的目录
RequireJS以一个相对于baseUrl的地址来加载所有的代码。页面顶层script标签含有一个特殊的属性data-main,require.js使用它来启动脚本加载过程,而baseUrl一般设置到与该属性相一致的目录
<script data-main="js/main.js" src="scripts/require.js"></script>
// main.js require([‘moduleA‘], function(a){ console.log(a); }); //moduleA.js define(function(){ return 1; })
入口文件main.js依赖于moduleA,直接写成[‘moduleA‘],默认情况下,require.js假定moduleA与main.js在同一个目录,即‘js/moduleA.js‘,文件名为moduleA.js,然后自动加载
使用require.config()方法,我们可以对模块的加载行为进行自定义。require.config()就写在主模块(main.js)的头部。参数就是一个对象,这个对象的paths属性指定各个模块的加载路径
require.config({ baseUrl: ‘test‘ }) require([‘moduleA‘], function(a){ console.log(a); });
RequireJS默认假定所有的依赖资源都是js脚本,因此无需在module ID上再加".js"后缀,RequireJS在进行module ID到path的解析时会自动补上后缀
如果一个模块的路径比较深,或者文件名特别长,比如‘js/lib/moduleA.min.js‘,则可以使用config配置对象中的paths属性
// main.js require.config({ paths:{ ‘moduleA‘:‘lib/moduleA.min‘ } }) require([‘moduleA‘], function(a){ console.log(a); }); //moduleA-min.js define(function(){ return 3; }
要注意的是,这里的paths的‘moduleA‘设置的是‘lib/moduleA.min‘,而不是‘js/lib/moduleA.min‘,是因为requireJS中的文件解析是一个"baseUrl + paths"的解析过程
如果一个module ID符合下述规则之一,其ID解析会避开常规的"baseUrl + paths"配置,而是直接将其加载为一个相对于当前HTML文档的脚本:1、以 ".js" 结束;2、包含 URL 协议,如 "http:" or "https:"
require.config({ baseUrl: ‘js/lib‘, paths:{ ‘moduleA‘:‘moduleA.min‘ } }) require([‘js/moduleA.js‘], function(a){ console.log(a); })
路径为‘js/moduleA.js‘,而不是‘js/lib/moduleA.min‘
一般来说,最好还是使用baseUrl及"paths" config去设置module ID。它会带来额外的灵活性,如便于脚本的重命名、重定位等。 同时,为了避免凌乱的配置,最好不要使用多级嵌套的目录层次来组织代码,而是要么将所有的脚本都放置到baseUrl中,要么分置为项目库/第三方库的一个扁平结构,如下
www/ index.html js/ app/ sub.js lib/ jquery.js canvas.js main.js
4.CommonJS
前面提到过,commonJS主要应用于服务器端编程,如nodejs。使用打包工具Browserify可以对CommonJS进行格式转换,使其可以在浏览器端进行
而requireJS支持一种简单包装CommonJS的方式,只要在commonJS代码的外层简单包裹一层函数,就可以在浏览器端直接运行
define(function(require, exports, module) { });
如果该模块还依赖其他模块,如依赖模块moduleA,则代码如下
define([‘moduleA‘],function(require, exports, module) { });
a.js和b.js的commonJS形式的代码如下
// a.js var a = 100; module.exports.a = a; // b.js var result = require(‘./a‘); console.log(result.a);
index.html直接引用b.js会报错,提示require没有被定义
将a.js和b.js进行改造之后,代码如下
define(function(require, exports, module) { var a = 100; module.exports.a = a; }); // b.js define(function(require, exports, module) { var result = require(‘./a‘); console.log(result.a); });
index.html将入口文件设置为‘js/b‘,则结果为100
<script src="require.js" data-main="js/b" defer async></script>
5.shim
shim属性为那些没有使用define()来声明依赖关系、设置模块的"浏览器全局变量注入"型脚本做依赖和导出配置,即加载非规范的模块
举例来说,underscore和backbone这两个库,都没有采用AMD规范编写。如果要加载它们的话,必须先定义它们的特征。具体来说,每个模块要定义(1)exports值(输出的变量名),表明这个模块外部调用时的名称;(2)deps数组,表明该模块的依赖性
require.config({ shim: { ‘underscore‘:{ exports: ‘_‘ }, ‘backbone‘: { deps: [‘underscore‘, ‘jquery‘], exports: ‘Backbone‘ } } });
require.js还提供一系列插件,实现一些特定的功能
domReady模块实现了一个跨浏览器的方法来判定何时DOM已经ready
// main.js require([‘domready!‘], function(){ console.log(‘ready‘); });
text插件可以用来加载如.html、.css等文本文件,可以通过该插件来实现完整组件(结构+逻辑+样式)的组件化开发
require(["some/module", "text!some/module.html", "text!some/module.css"], function(module, html, css) { } );
以上是关于amd的主要内容,如果未能解决你的问题,请参考以下文章
如何将遗留的 Dojo Toolkit 代码转换为 AMD?
macOS Beta代码泄密:苹果新款MacBook有望采用AMD处理器