模块化封装

回顾

  • 早期前端开发的问题(变量污染全局作用域,资源依赖关系不明确,资源引入的顺序有讲究)
  • 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 在推广过程中对模块定义的规范化产出。

区别

  1. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.
  2. 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推崇写法,依赖就近原则;

猜你喜欢

转载自blog.csdn.net/knowledge_bird/article/details/87888488