首页 > 网站 > WEB开发 > 正文

JS模块化编程之加载器原理

2024-04-27 14:09:17
字体:
来源:转载
供稿:网友

JS模块化编程之加载器原理

  世面上有好多javaScript的加载器,比如 sea.js, require.js, yui loader, labJs...., 加载器的使用范围是一些比较大的项目, 个人感觉如果是小项目的话可以不用, 我用过seaJSrequireJS, 在项目中用过requireJS, requireJS是符合AMD,全称是(Asynchronous Module Definition)即异步模块加载机制 , seaJS是符合CMD规范的加载器。

  AMD__和__CMD

  AMD规范是依赖前置, CMD规范是依赖后置, AMD规范的加载器会把所有的JS中的依赖前置执行CMD是懒加载, 如果JS需要这个模块就加载, 否则就不加载, 导致的问题是符合AMD规范的加载器(requireJS), 可能第一次加载的时间会比较久, 因为他把所有依赖的JS全部一次性下载下来;

  常识,jQuery是支持AMD规范,并不支持CMD规范,也就是说, 如果引入的是seaJS,想要使用jQuery,要用alias配置, 或者直接把 http://cdn.bootCSS.com/jquery/2.1.4/jquery.js直接引入页面中;

//这是jQuery源码的最后几行, jQuery到了1.7才支持模块化;// Register as a named AMD module, since jQuery can be concatenated with other// files that may use define, but not via a PRoper concatenation script that// understands anonymous AMD modules. A named AMD is safest and most robust// way to register. Lowercase jquery is used because AMD module names are// derived from file names, and jQuery is normally delivered in a lowercase// file name. Do this after creating the global so that if an AMD module wants// to call noConflict to hide this version of jQuery, it will work.// Note that for maximum portability, libraries that are not jQuery should// declare themselves as anonymous modules, and avoid setting a global if an// AMD loader is present. jQuery is a special case. For more information, see// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anonif ( typeof define === "function" && define.amd ) {    define( "jquery", [], function() {        return jQuery;    });};

  

使用方法

  比如我们可以这样定义一个模块:

//文件所在的路径地址为:http://localhost:63342/module/script/dir2/1.jsdefine(function() {    return "!!!!";});

  也可以这样定义一个模块:

//这个文件的路径为http://localhost:63342/module/main.js ,而且有一个依赖, 加载器会自动去加载这个依赖, 当依赖加载完毕以后, 会把这个依赖(就是script/dir2/1.js)执行的返回值作为这个函数的参数传进去;require(["script/dir2/1.js"], function(module1) {    console.log(module1);});//实际上会打印出 "!!!!"

  一般来说,一个模块只能写一个define函数, define函数的传参主要有两种方式:

    1:正常上可以是一个函数;

    2:可以是一个数组类型依赖的列表, 和一个函数;

  如果一个模块写了多个define会导致模块失灵, 先定义的模块被后定义的模块给覆盖了 ( 当然了, 一般我们不那样玩);

  一个模块内可以写多个require, 我们可以直接理解require为匿名的define模块, 一个define模块内可以有多个require, 而且require过的模块会被缓存起来, 这个缓存的变量一般是在闭包内, 而且名字多数叫modules什么的.....;

  我们通过加载器开发实现的模块化开发要遵守一种规范, 规范了一个模块为一个JS,那么我们就可以新建几个目录为conroller,view, model, 也是为了后期更好的维护解耦

  

实现一个自己的加载器

  使用的方式:

//这个模块依赖的四个模块,加载器会分别去加载这四个模块;define(["依赖0","依赖1","依赖2","依赖3"], function(依赖0,依赖1,依赖2,依赖3){});//返回一个空对象define(function(){    return {};});//直接把require当作是define来用就好了;require(["依赖0","依赖1","依赖2","依赖3"], function(依赖0,依赖1,依赖2,依赖3) {    //执行依赖0;    依赖0(依赖1,依赖2,依赖3);});//这个加载器define函数和require函数的区别是,define我们可以传个name作为第一参数, 这个参数就是模块的名字????, 好吧, 不管这些了.....;

  以下为加载器的结构,因为代码量已经很少了, 所以每一函数都是必须的, 为了不影响全局, 把代码放在匿名自执行函数内部:

(function() {    定义一个局部的difine;    var define;    //我偷偷加了个全局变量,好调试啊;    window.modules = {    };    //通过一个名字获取绝对路径比如传"xx.js"会变成"http://www.mm.com/"+ baseUrl + "xx.html";    var getUrl = function(src) {};    //动态加载js的模块;    var loadScript = function(src) {};    //获取根路径的方法, 一般来说我们可以通过config.baseUrl配置这个路径;    var getBasePath = function() {};    //获取当前正在加载的script标签DOM节点;    var getCurrentNode = function() {};    //获取当前script标签的绝对src地址;    var getCurrentPath = function() {};    //加载define或者require中的依赖, 封装了loadScript方法;    var loadDpt = function(module) {};    //这个是主要模块, 完成了加载依赖, 检测依赖等比较重要的逻辑    var checkDps = function() {};    定义了define这个方法    define = function(deps, fn, name) {};    window.define = define;    //require是封装了define的方法, 就是多传了一个参数而已;    window.require = function() {        //如果是require的话那么模块的名字就是一个不重复的名字,避免和define重名;        window.define.apply([], Array.prototype.slice.call(arguments).concat( "module|"+setTimeout(function() {},0) ));    };});

  加载器源码实现(兼容,Chrome, FF, IE6 ==>> IE11),IE11没有了readyState属性, 也没有currentScript属性,坑爹啊, 无法获取当前正在执行的JS路径, 所以要用hack;

<!DOCTYPE html><html><head lang="en">    <meta charset="UTF-8">    <title></title>    <script>    (function() {        var define;        window.modules = {        };        var getUrl = function(src) {            var scriptSrc = "";            //判断URL是否是            // ./或者            // /或者            // 直接是以字符串开头            // 或者是以http://开头;            if( src.indexOf("/") === 0 || src.indexOf("./") === 0 ) {                scriptSrc = require.config.base + src.replace(/^///,"").replace(/^/.///,"");            }else if( src.indexOf("http:") === 0 ) {                scriptSrc = src;            }else if( src.match(/^[a-zA-Z1-9]/) ){                scriptSrc = require.config.base + src;            }else if(true) {                alert("src错误!");            };            if (scriptSrc.lastIndexOf(".js") === -1) {                scriptSrc += ".js";            };            return scriptSrc;        };        var loadScript = function(src) {            var scriptSrc = getUrl(src);            var sc = document.createElement("script");            var head = document.getElementsByTagName("head")[0];            sc.src = scriptSrc;            sc.onload = function() {                console.log("script tag is load, the url is : " + src);            };            head.appendChild( sc );        };        var getBasePath = function() {            var src = getCurrentPath();            var index = src.lastIndexOf("/");            return  src.substring(0,index+1);        };        var getCurrentNode = function() {            if(document.currentScript) return document.currentScript;            var arrScript = document.getElementsByTagName("script");            var len = arrScript.length;            for(var i= 0; i<len; i++) {                if(arrScript[i].readyState === "interactive") {                    return arrScript[i];                };            };            //IE11的特殊处理;            var path = getCurrentPath();            for(var i= 0; i<len; i++) {                if(path.indexOf(arrScript[i].src)!==-1) {                    return arrScript[i];                };            };            throw new Error("getCurrentNode error");        };        var getCurrentPath = function() {            var repStr = function(str) {                return (str || "").replace(/[/&/?]{1}[/w/W]+/g,"") || "";            };            if(document.currentScript) return repStr(document.currentScript.src);            //IE11没有了readyState属性, 也没有currentScript属性;            // 参考 https://github.com/samyk/jiagra/blob/master/jiagra.js            var stack            try {                a.b.c() //强制报错,以便捕获e.stack            } catch (e) { //safari的错误对象只有line,sourceId,sourceURL                stack = e.stack                if (!stack && window.Opera) {                    //opera 9没有e.stack,但有e.Backtrace,但不能直接取得,需要对e对象转字符串进行抽取                    stack = (String(e).match(/of linked script /S+/g) || []).join(" ")                }            }            if (stack) {                /**e.stack最后一行在所有支持的浏览器大致如下:                 *chrome23:                 * at http://113.93.50.63/data.js:4:1                 *Firefox17:                 *@http://113.93.50.63/query.js:4                 *opera12:http://www.oldapps.com/opera.php?system=Windows_XP                 *@http://113.93.50.63/data.js:4                 *IE10:                 *  at Global code (http://113.93.50.63/data.js:4:1)                 *  //firefox4+ 可以用document.currentScript
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表