高级4-AMD、CMD、RequireJS
题目1: 为什么要使用模块化?
解决命名冲突
对于复杂的网站,js中代码很多,其中有很多小文件,如果将所有文件放在htmlscript
标签中引用,js中的文件全部都是全局变量,当在编辑其中一个文件时不能确保其他文件没有使用编辑文件的变量
依赖管理
网站中有很多js,有很多库和框架及组件、应用。执行分先后顺序,相互有依赖性
提高代码可读性
所有代码放在一起,有很多字节,将其中一些功能拆分后(例:每个功能在文件中)使代码间有模块隔离,这样每次修改功能的时候就可以通过文件或者模块名来找到需要修改的代码
提高代码复用性
题目2: CMD、AMD、CommonJS 规范分别指什么?有哪些应用
CMD
Common Module Definition,是又国内发展出来,由SeaJS框架支撑,和CommonJS语法类似,按需加载,也就是一个模块一个文件,遵循统一的写法,SeaJS目前基本已经被废弃了。
定义模块
define(id?, dependencies?, factory)
CMD推崇一个文件一个模块,所以经常就用文件名作为模块id,一般不写
CMD推崇依赖就近,所以一般不在define的参数中写依赖,在factory中写,所以dependencies参数省略
factory: function(require, exports, module){},require 是一个方法,接受模块标识
作为唯一参数,用来获取其他模块提供的接口。exports 是一个对象,用来向外提供模块接口。
module 是一个对象,上面存储了与当前模块相关联的一些属性和方法。
//定义myModule.js
define(function(require, exports, module) {
var $ = require('jquery.js')
$('body').text('hello world');
});
// 加载
seajs.use(['myModule.js'], function(my){});
AMD
Asynchronous Module Definition(异步模块定义),是一个在浏览器端模块化开发的规范,AMD不是JavaScript原生支持,使用AMD规范进行页面开发需要用到对应的函数库RequireJS。
RequireJS主要解决两个问题:
1.多个js文件可能有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器。
2.js加载时浏览器页面会停止渲染,加载文件越多,页面失去响应时间越长。
AMD是提前加载好所依赖的模块,就是说所有的require都被提前执行。在语法上也是提前写好被依赖的模块,假设有100个需要提前加载的模块就要写100个。
实现AMD的库有`RequireJS` 、`curl` 、`Dojo` 等。
定义模块:
define(id?, dependencies?, factory)
id: 可选,用来定义模块的标识,若没有提供,默认为脚本文件名(去掉拓展名)
dependencies: 可选,是一个当前模块依赖的模块名称数组,默认为["require", "exports", "module"],
若factory的参数小于3,加载器会选择参数个数调用工厂方法。
factory: 工厂方法,模块初始化要执行的函数或对象。若为函数,只被执行一次。若为对象,应该为模块的输出值
加载模块:
require([dependencies], function(){});
第一个参数是数组,表示所依赖的模块
第二个参数是回调函数,当前面的模块都加载成功后将被调用。加载的模块会以参数形式传入该函数,从而在回调函数
内部就可以使用这些模块。
require()函数在加载依赖模块时是异步加载的,这样浏览器不会失去响应,它的回调函数只有模块都加载成功后才运行,解决了依赖性的问题。
//定义
define('myModule', ['jquery'], function($) {
// $ 是 jquery 模块的输出
$('body').text('hello world');
});
// 加载
require(['myModule'], function(myModule) {})
CommonJS
CommonJS 是用于服务器端模块的规范。Node.js采用了这个规范。Node.JS首先采用了js模块化的概念。
module.exports将当前的东西导出去,是模块的唯一出口。
require是node内部定义好方法,读取一个文件并执行,负责加载module.exports传过来的对象。
一个文件代表一个模块每一个模块都是一个单独的作用域,在该模块内部定义 的变量,无法被其他模块读取,除非定义为global对象的属性。模块可以给别人去用,使用别的模块使用require
,希望被别的模块使用就用
module.exports
。
require('./a')=people
module.exports
等于的值改变,require也会改变
存在的问题:
CommonJS在服务端是一个同步的过程,因为文件都在本地require
的同时执行module.exports
,并不是异步。如果在浏览器中执行require('./a')
假设a.js
在服务器上需要下载下来执行完a.js
但是如果加载多条var p = require('./a');
需要多次下载。在浏览器端加载JavaScript最佳最容易的方法是在document中加入script标签,但脚本标签天生异步,CommonJS模块在浏览器环境中无法正常加载。
解决方法:
1.开发一个服务端组件,对模块代码静态分析,将模块与它的依赖列表一起返回给浏览器端,需要服务器安装额外的组件,因此需要调整一系列底层架构 。
2.用一套标准末班来封装模块定义,但是对于模块应该怎么定义和加载,又产生分歧。
AMD与CMD区别:
AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块。
CMD推崇就近依赖,只有在用到某个模块的时候再去require。
AMD和CMD最大的区别是对依赖模块的执行时机处理不同,注意不是加载的时机或者方式不同。
很多人说 RequireJS 是异步加载模块,SeaJS 是同步加载模块,这么理解实际上是不准确的,其实加载模块都是异步的,只不过AMD依赖前置,js可以方便知道依赖模块是谁,立即加载,而CMD就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是很多人诟病CMD的一点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到可以忽略
题目3: 使用 requirejs 完善入门任务15,包括如下功能:
1首屏大图为全屏轮播
2有回到顶部功能
3图片区使用瀑布流布局(图片高度不一),下部有加载更多按钮,点击加载更多会加载更多数据
4(可选)使用 r.js 打包应用