回顾
- 早期前端开发的问题(变量污染全局作用域,资源依赖关系不明确,资源引入的顺序有讲究)
- CMD和AMD,主要讲的是CMD
- seaJs的使用
- 定义模块:defined(工厂函数)
- require, exports, module
- seajs.use(模块路径)
- 在cmd规范中,一个js文件就是一个模块,这个模块必须使用define包裹起来,然后对外开放接口;
fis3图片懒加载特效的实现
关键代码如下:
$(function () {
setTimeout(function () {
var html = `<img class="lazy" data-original="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1542088089898&di=f9227911f22709e8c0dec571daabad29&imgtype=0&src=http%3A%2F%2Fimgsrc.baidu.com%2Fimgad%2Fpic%2Fitem%2F32fa828ba61ea8d3d8d6c33f9c0a304e251f5810.jpg"
alt="" srcset="">`;
$(".hello").append(html);
$("img.lazy").lazyload({ effect: "fadeIn" });
}, 2000);
});
注意:必须在dom渲染完毕后,执行图片懒加载特效!
编写计算器模块
define(function (require, exports, module) {
console.log("计算器模块");
var _settings = {};//私有属性
//私有方法
var modFns = {
add: function (val1, val2) {
return parseInt(val1) + parseInt(val2);
},
jf: function (val1, val2) {
return val1 - val2;
},
cf: function (val1, val2) {
return val1 * val2;
},
chuf: function (val1, val2) {
return val1 / val2;
}
};
/**公共的对外接口 */
//上面已经扩展
// exports.add = function () {}
// exports.jf = function () {}
// exports.cf = function () {}
//重写公共的方法
//对外开放接口
module.exports = {
fn3: function () {
console.log("重写方法");
},
add: modFns.add,
jf: modFns.jf,
//如果需要对该开放的接口进行修改,那么就使用以下的方式
cf: function (val1, val2) {
modFns.cf(val1, val2);
},
chuf: function (val1, val2) {
modFns.chuf(val1, val2);
}
};
/**公共的对外接口__end */
});
define(function (require, exports, module) {
var calcuor = require("calculator.js");
console.log(calcuor);
var $ = require("jquery.js");//加载jQuery模块
console.log($);
var doms = {
$val1: $("#val1"),
$val2: $("#val2"),
$jfbtn: $("#jfbtn"),
jianfbtn: document.getElementById("jianfbtn"),
chenbtn: document.getElementById("chenbtn"),
chubtn: document.getElementById("chubtn"),
$reslut: $("#reslut")
}
console.log(val1);
var modfns = {
jiaHandle: function () {
// doms.jfbtn.addEventListener("click", function () {
// reslut.value = calcuor.add(val1.value, val2.value);
// }, false);
doms.$jfbtn.click(function () {
doms.$reslut.val(calcuor.add(doms.$val1.val(), doms.$val2.val()));
});
},
jianHandle: function () {
},
chenHandle: function () {
},
chuHandle: function () {
},
fire: function () {
this.jiaHandle();
}
};
modfns.fire();
});
注意相关的事项
-如果使用了exports扩展对外开放的接口,就避免重写module.exports,原因就是会覆盖已经写好的exports扩展接口;
推荐
使用私有方法和module.exports组合使用
引入jQuery库的注意事项
必须使用define将jQuery库的代码包裹好,这才可以使用。
相关关键代码如下:
if (typeof module === "object" && typeof module.exports === "object") {
// For CommonJS and CommonJS-like environments where a proper window is present,
// execute the factory and get jQuery
// For environments that do not inherently posses a window with a document
// (such as Node.js), expose a jQuery-making factory as module.exports
// This accentuates the need for the creation of a real window
// e.g. var jQuery = require("jquery")(window);
// See ticket #14549 for more info
module.exports = global.document ?
factory(global, true) :
function (w) {
if (!w.document) {
throw new Error("jQuery requires a window with a document");
}
return factory(w);
};
} else {
factory(global);
}
seajs相关api的学习
模块化别名配置
代码如下:
seajs.config({
alias: {
// 'm1': "modules/module1",
'jQuery': 'jquery'
}
});
define('mod1', function (require, exports, module) {
console.log("执行mod1模块");
var $ = require("jQuery");
console.log($);
$(".mod1").html("我是mod1模块,依赖jQuery库");
});
模块化资源引入的特点
mod1代码如下:
define('mod1', function (require, exports, module) {
console.log("执行mod1模块");
var $ = require("jQuery");
console.log($);
$(".mod1").html("我是mod1模块,依赖jQuery库");
});
doIt.js模块代码:
define(function (require, exports, module) {
var calcuor = require("calculator.js");
console.log(calcuor);
var $ = require("jquery.js");//加载jQuery模块
console.log($);
总结:
如果有2个模块,并且都依赖jQuery库,这个时候seajs有且只会加载一次jQuery的模块资源,并且做到重复使用。
seajs路径配置
当目录比较深,或需要跨目录调用模块时,可以使用 paths 来简化书写。
seajs.config({
alias: {
// 'm1': "modules/module1",
'jQuery': 'jquery'
},
paths: {
'gallery': 'https://aaa.blipayobjects.com/gallery',
'child': 'child/child/child',
}
});
define('mod2', function (require, exports, module) {
var a = require("child/a");
console.log(a);
});
seajs.use("mod2");
seajs变量的配置
变量配置。有些场景下,模块路径在运行时才能确定,这时可以使用 vars 变量来配置。
vars 配置的是模块标识中的变量值,在模块标识中用 {key} 来表示变量。
代码如下:
seajs.config({
vars: {
'mp': 'other2',
},
alias: {
// 'm1': "modules/module1",
'jQuery': 'jquery'
},
paths: {
'gallery': 'https://aaa.blipayobjects.com/gallery',
'child': 'child/child/child',
}
});
define('mod2', function (require, exports, module) {
// var a = require("child/a");
// console.log(a);
var b = require("{mp}/b");
console.log(b);
});
define('mod3', function (require, exports, module) {
// var a = require("child/a");
// console.log(a);
var b = require("{mp}/b");
console.log(b);
});
define('mod4', function (require, exports, module) {
// var a = require("child/a");
// console.log(a);
var b = require("{mp}/b");
console.log(b);
});
// seajs.use("doIt.js");
// seajs.use("mod1");
seajs.use("mod2");
seajs.use("mod3");
seajs.use("mod4");
seajs中 map
该配置可对模块路径进行映射修改,可用于路径转换、在线调试等。
代码
seajs.config({
map: [
['.js', '-debug.js']
]
});
define(function (require, exports, module) {
var a = require('./a');
//=> 加载的是 path/to/a-debug.js
});
备注:-debug.js文件是调试文件,也就是说,设置了map映射后,可以将正式文件,改成调试文件
作为简单了解的资料
preload
使用 preload 配置项,可以在普通模块加载前,提前加载并初始化好指定模块。
// 在老浏览器中,提前加载好 ES5 和 json 模块seajs.config({ preload: [ Function.prototype.bind ? ‘’ : ‘es5-safe’, this.JSON ? ‘’ : ‘json’ ] });
preload 中的空字符串会被忽略掉。
注意:preload 中的配置,需要等到 use 时才加载。比如:
seajs.config({ preload: ‘a’ }); // 在加载 b 之前,会确保模块 a 已经加载并执行好 seajs.use(’./b’);
注意:preload 配置不能放在模块文件里面:
seajs.config({ preload: ‘a’ }); define(function(require, exports) { // 此处执行时,不能保证模块 a 已经加载并执行好 });
案例:预加载jQuery库
debug
值为 true 时,加载器会使用 console.log 输出所有警告和错误。 默认为 false, 加载器只抛出异常。
另外,还可以将 debug 值设为 2 . 这种情况下, 每个脚本请求都会加上唯一时间戳。这在测试期间很有用,可以强制浏览器每次都请求最新版本。
base
SeaJS 在解析顶级标识时,会相对 base 路径来解析。(顶级标识:参阅:https://github.com/seajs/seajs/issues/258)
注意:一般请不要配置 base 路径,把 sea.js 放在合适的路径往往更简单一致。
案例:
总结一下原理:基准模块文件:modules文件夹,基本都是。
charset
获取模块文件时,
noConflict
为了避免冲突,或者需要定制全局命名空间以符合自己的口味时,可以使用 noConflict 方法来实现。
var myLoader = seajs.noConflict(); myLoader.use(’./main’); /* main.js / define(function(require, exports, module) { // snip… });
还可以通过给该方法传递 true,来释放 define 方法。 很少会有这么做的必要, 请三思而后行。
var myLoader = seajs.noConflict(true); myLoader.use(’./main’); / main.js */ myLoader.define(function(require, exports, module) { // snip… });
seajs.reslove
类似require.resolve,会利用模块系统的内部机制对传入的字符串参数进行路径解析。
代码如下:
seajs.resolve(‘jquery’);
// => http://path/to/jquery.js
seajs.resolve(’./a’, ‘http://example.com/to/b.js’);
案例如下:
seaJs基本开发原则
使用SeaJS开发JavaScript的基本原则就是:一切皆为模块。引入SeaJS后,编写JavaScript代码就变成了编写一个又一个模块,SeaJS中模块的概念有点类似于面向对象中的类——模块可以拥有数据和方法,数据和方法可以定义为公共或私有,公共数据和方法可以供别的模块调用。
另外,每个模块应该都定义在一个单独js文件中,即一个对应一个模块。
所以,Sea.js可以说是一套代码规范,并且用这些规则规范来组织js要如何组织和编写。Sea.js并不像jQuery这种具体负责某个自能的js库。
amd和cmd规范的区别
- AMD 是 RequireJS 在推广过程中对模块定义的规范化产出。
- CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。
区别
- 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.
- CMD 推崇依赖就近,AMD 推崇依赖前置。看代码:
// CMD
define(function(require, exports, module) {
var a = require('./a')
a.doSomething()
var b = require('./b')
b.doSomething()
...
})
// AMD
define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好
a.doSomething()
...
b.doSomething()
...
})
问题:为啥不能用别名和依赖前置组合使用?
原因,实际编码过程中,不会对每个模块进行别名的设置。
设置别名,只是用于常用模块,类似jQuery模块,全局的功能模块,这种类型的模块,通常使用别名。
总结:seaJs中推荐使用依赖就近原则!
define使用总结
define(工厂函数):
define(function (require, exports, module){})
define(“模块的ID”,工厂函数):
define(“mod1”,function (require, exports, module){})
define(“模块ID”,deps,工厂函数)
define(“mod1”,[‘模块1’,‘模块2’],function (require, exports, module){})
注意:不推荐使用第三种写法!
注意:如果使用require加载.js文件的模块的时候,该js文件模块不能有模块ID,否则报错!代码如下:
too.js代码
//定义模块的语法:
define("tool", function (require, exports, module) {
console.log("hello tool.js");
module.exports = {
fn1: 45645,
addClass: function () {
console.log("执行添加类名的功能");
}
};
});
mod2模块代码:
define('mod2', function (require, exports, module) {
var mod1 = require("mod1");
var tool = require("too");
console.log(mod1);
console.log(tool);
});
结果:
出现undefined,并且不执行 too模块
hello mod1
index5.html:49 {fn1: 45645}
index5.html:50 undefined
工厂函数之require
require 接受 (模块标识| 模块的路径) 作为唯一参数 模块依赖解析,靠的是三个重要的规则: 不能重命名 require 不能覆盖 require require 的参数必须是字符串字面量,不可以 require(foo()) 或者 require(bar), 也不可以是 require(should_be_a ? ‘a’ : ‘b’)。
第一种用法:require(“路径|模块ID”)
代码如下:
var mod1 = require("mod1");
注意:以上的代码是同步加载的,也就是说,必须要等到mod1模块加载完毕之后,才能执行后面程序。
第二种写法:require.async()
定义:
require.async(id, callback) async 方法可用来异步加载模块,并在加载完成后执行指定回调。
具体语法如下:
- require.async(“路径|模块ID”,模块加载完后的回调函数)
- require.async([‘路径|模块ID’,‘模块2’],模块加载完后的回调函数)
代码如下:
define('mod2', function (require, exports, module) {
var mod1 = require("mod1");
// var tool = require("too");//同步加载模块,直接返回模块对象
//注意,当模块文件比较大的时候,可以考虑使用异步加载模块(require.async)
//注意:当使用了require.async异步加载模块后,只能在回调函数中获取到加载完毕的模
require.async("too", function (too) {
console.log(too);
})
console.log(mod1);
console.log(tool);//undefined
});
注意:
- 当模块文件比较大的时候,可以考虑使用异步加载模块(require.async)
- 当使用了require.async异步加载模块后,只能在回调函数中获取到加载完毕的模块对象
require.resolve
require.resolve(id) 使用模块系统内部的路径解析机制来解析并返回模块路径。该函数不会加载模块,只返回解析后的绝对路径。
代码如下:
define(function(require, exports) {
console.log(require.resolve('./b'));
// ==> 'http://example.com/js/b.js'
});
整体感知和总结
- 定义模块:使用define定义模块;
- 工厂函数;
- require;
- exports;
- module.exports;
- AMD和CMD规范的区别;
- CMD推崇写法,依赖就近原则;