zoukankan      html  css  js  c++  java
  • 我的模块加载系统 v18

    本版本亮点是加入本地储存的支持与版本控制。

    根据我们正常的思路,上线前我们会把这个页面的所有JS文件打包成一个,或尽可能少的JS文件,其目的是减少请求数。然后,模块化带来的一个直接结果是把一个框架打散成N多JS文件,真是反其道而行。但是一个网站是由许多页面组成,每个页面其实都是引用N个相同的JS文件(包括基础库,工具库与通用组件库),与针对这个页面的专用JS文件。因此想搞鼓出最优的打包方案是很难的,打包后我们图的是利用浏览器的静态缓存减少对服务器的压力!不过,请神容易送神难,静态缓存后,我们想刷掉这缓存比较麻烦,不能要求用户按F5或Ctrl+F5来清吧!涉及CDN就更麻烦了。而且合并JS后,意味着服务器的内存保存着对应的合并副本,文件越多,组合越多!因此我结合本地储存,得出的结论是不对JS文件进行缓存!下面是我的详细思路:

    页面引入mass Framework的种子模块mass.js,然后我们开始写业务代码,用到什么功能就用$.require("xxx,yyy",fn)引入对应的模块就行了。

    在mass Framework中,模块即文件,于是加载文件去。在加载之前,我们有两个判定,判定此模块是否已经加载过,即它是否存在于内存!没有再判定它是否保存过,即它是否序列化到用户机器上,针对不同的浏览器,我们分别使用userData与localStorage。如果不支持这两种最流行的本地储存就算了,我也懒得兼容比IE5.5更2B的浏览器!如果在内存中,我们可以直接用,如果在本地储存中,我们把这些字符串还原为函数也可以用!如果两者都不,就远程加载。因此上面的require操作,我们是加载了两个JS文件,然后把它们的模块工厂,JS文件路径,依赖列表等都立即保存到本地储存去!之后所有操作都以原来的一样。

    本地储存应用后的区别在于,以后无论是什么刷新页面,或跳转到本站的其他页面,只要你用到这个模块,它都不发出请求了!如果是静态缓存还会发出请求的。带来的好处是,整个网站,除了种子模块之外,其他JS文件都是请求一次!由于模块系统的存在,让按需加载成为现实,因此用户实际请求数可能更少!

    想应用本地储存功能,不需要调用函数,也不要传参,只要在引用种子模块的script标签添加一个storage属性就行,值为1或true。

      <script src="/scripts/mass.js" storage="1"></script>
    

    好了,当我们对一个JS模块进行了修改,那怎么办呢?框架提供了两种方式把它从本地储存中清掉。一种是服务器端的,写入一个cookie,键名为erase(这个也可以配置),值为一个序列化后的对象。这个对象是个样子的:

      {
        "http://localhost:3000/aaa.js":1347544195924,
        "http://localhost:3000/bbb.js":1347544195925,
        "http://localhost:3000/ccc.js":1347544195926
       }
    

    键名为模块的URL,值为它的版本号。我是使用时间戮做版本号的。只有这个JS文件发生变化,我才把它放进此对象,然后"erase="+ JSON.stringify(object)发到前端去。

    在前端,框架会读取cookie,发现有erase这个键名,就把它的值取出来,然后还原为一个对象,然后遍历它的键值对,比较本地储存中那个模块的版本号,如果它比现在的旧,就把它清掉!

    下面是框架的具体执行代码:

      var rerase = new RegExp('(?:^| )' + $.config.erase + '(?:(?:=([^;]*))|;|$)')
        var match = String(DOC.cookie).match( rerase );
        //读取从后端过来的cookie指令,转换成一个对象,键名为模块的URL,值为版本号(这是一个时间戮)
        if(match && match[1]){
            try{
                var obj = eval("0,"+match[1]);
                for(var i in obj){//$.erase会版本号比现在小的模块从本地储存中删掉
                    $.erase(i, obj[i])
                }
            }catch(e){}
        }
    

    这就是通过后端的cookie指令清除指定模块!

    我们也可以在前端自己调用$.erase(url, version)会删掉指定模块!

    上面也提到过cookie指令的名字也可以配置的,也和storage一样,在引用种子模块的script标签添加一个erase属性,值为你想要的名字就行了!

    本次版本的升级只添加了一个erase方法,所有的改进都在内部进行的!

    mass Framework的API文档

    + function( global, DOC ){
        var $$ = global.$//保存已有同名变量
        var rmakeid = /(#.+|\W)/g;
        var NsKey = DOC.URL.replace( rmakeid,'')
        var NsVal = global[ NsKey ];//公共命名空间
        var W3C   = DOC.dispatchEvent //w3c事件模型
        var HTML  = DOC.documentElement;
        var HEAD  = DOC.head || DOC.getElementsByTagName( "head" )[0]
        var loadings = [];//正在加载中的模块列表
        var mass = 1;//当前框架的版本号
        var postfix = "";//用于强制别名
        var cbi = 1e5 ; //用于生成回调函数的名字
        var all = "lang_fix,lang,support,class,flow,query,data,node,attr,css_fix,css,event_fix,event,ajax,fx"
        var class2type = {
            "[object HTMLDocument]"   : "Document",
            "[object HTMLCollection]" : "NodeList",
            "[object StaticNodeList]" : "NodeList",
            "[object IXMLDOMNodeList]": "NodeList",
            "[object DOMWindow]"      : "Window"  ,
            "[object global]"         : "Window"  ,
            "null"                    : "Null"    ,
            "NaN"                     : "NaN"     ,
            "undefined"               : "Undefined"
        }
        var toString = class2type.toString;
        function $( expr, context ){//新版本的基石
            if( $.type( expr,"Function" ) ){ //注意在safari下,typeof nodeList的类型为function,因此必须使用$.type
                return  $.require( all+",ready", expr );
            }else{
                if( !$.fn )
                    throw "node module is required!"
                return new $.fn.init( expr, context );
            }
        }
        //多版本共存
        if( typeof NsVal !== "function"){
            NsVal = $;//公用命名空间对象
            NsVal.uuid = 1;
        }
        if(NsVal.mass !== mass  ){
            NsVal[ mass ] = $;//保存当前版本的命名空间对象到公用命名空间对象上
            if(NsVal.mass || ($$ && $$.mass == null)) {
                postfix = ( mass + "" ).replace(/\D/g, "" ) ;//是否强制使用多库共存
            }
        }else{
            return;
        }
        /**
         * 糅杂,为一个对象添加更多成员
         * @param {Object} receiver 接受者
         * @param {Object} supplier 提供者
         * @return  {Object} 目标对象
         */
        var has = Object.prototype.hasOwnProperty
        function mix( receiver, supplier ){
            var args = Array.apply([], arguments ),i = 1, key,//如果最后参数是布尔,判定是否覆写同名属性
            ride = typeof args[args.length - 1] == "boolean" ? args.pop() : true;
            if(args.length === 1){//处理$.mix(hash)的情形
                receiver = !this.window ? this : {} ;
                i = 0;
            }
            while((supplier = args[i++])){
                for ( key in supplier ) {//允许对象糅杂,用户保证都是对象
                    if ( has.call(supplier,key) && (ride || !(key in receiver))) {
                        receiver[ key ] = supplier[ key ];
                    }
                }
            }
            return receiver;
        }
    
        mix( $, {//为此版本的命名空间对象添加成员
            html: HTML,
            head: HEAD,
            mix: mix,
            rword: /[^, ]+/g,
            mass: mass,//大家都爱用类库的名字储存版本号,我也跟风了
            "@bind": W3C ? "addEventListener" : "attachEvent",
            //将内部对象挂到window下,此时可重命名,实现多库共存  name String 新的命名空间
            exports: function( name ) {
                $$ && ( global.$ = $$ );//多库共存
                name = name || $.config.nick;//取得当前简短的命名空间
                $.config.nick = name;
                global[ NsKey ] = NsVal;
                return global[ name ]  = this;
            },
            /**
             * 数组化
             * @param {ArrayLike} nodes 要处理的类数组对象
             * @param {Number} start 可选。要抽取的片断的起始下标。如果是负数,从后面取起
             * @param {Number} end  可选。规定从何处结束选取
             * @return {Array}
             */
            slice: function ( nodes, start, end ) {
                var ret = [], n = nodes.length;
                if(end === void 0 || typeof end == "number" && isFinite(end)){
                    start = parseInt(start,10) || 0;
                    end = end == void 0 ? n : parseInt(end, 10);
                    if(start < 0){
                        start += n;
                    }
                    if(end > n){
                        end = n;
                    }
                    if(end < 0){
                        end += n;
                    }
                    for (var i = start; i < end; ++i) {
                        ret[i - start] = nodes[i];
                    }
                }
                return ret;
            },
            /**
             * 用于取得数据的类型(一个参数的情况下)或判定数据的类型(两个参数的情况下)
             * @param {Any} obj 要检测的东西
             * @param {String} str 可选,要比较的类型
             * @return {String|Boolean}
             */
            type: function ( obj, str ){
                var result = class2type[ (obj == null || obj !== obj ) ? obj :  toString.call( obj ) ] || obj.nodeName || "#";
                if( result.charAt(0) === "#" ){//兼容旧式浏览器与处理个别情况,如window.opera
                    //利用IE678 window == document为true,document == window竟然为false的神奇特性
                    if( obj == obj.document && obj.document != obj ){
                        result = 'Window'; //返回构造器名字
                    }else if( obj.nodeType === 9 ) {
                        result = 'Document';//返回构造器名字
                    }else if( obj.callee ){
                        result = 'Arguments';//返回构造器名字
                    }else if( isFinite( obj.length ) && obj.item ){
                        result = 'NodeList'; //处理节点集合
                    }else{
                        result = toString.call( obj ).slice( 8, -1 );
                    }
                }
                if( str ){
                    return str === result;
                }
                return result;
            },
            //$.log(str, showInPage=true, 5 )
            //level Number,通过它来过滤显示到控制台的日志数量。0为最少,只显示最致命的错误,
            //7则连普通的调试消息也打印出来。 显示算法为 level <= $.config.level。
            //这个$.colre.level默认为9。下面是level各代表的含义。
            //0 EMERGENCY 致命错误,框架崩溃
            //1 ALERT 需要立即采取措施进行修复
            //2 CRITICAL 危急错误
            //3 ERROR 异常
            //4 WARNING 警告
            //5 NOTICE 通知用户已经进行到方法
            //6 INFO 更一般化的通知
            //7 DEBUG 调试消息
            log: function (str){
                var  show = true, page = false
                for(var i = 1 ; i < arguments.length; i++){
                    var el = arguments[i]
                    if(typeof el == "number"){
                        show = el <=  $.config.level
                    }else if(el === true){
                        page = true;
                    }
                }
                if(show){
                    if( page === true ){
                        $.require( "ready", function(){
                            var div =  DOC.createElement("pre");
                            div.className = "mass_sys_log";
                            div.innerHTML = str +"";//确保为字符串
                            DOC.body.appendChild(div)
                        });
                    }else if( global.console ){
                        global.console.log( str );
                    }
                }
                return str
            },
            //用于建立一个从元素到数据的引用,用于数据缓存,事件绑定,元素去重
            getUid: global.getComputedStyle ? function( obj ){
                return obj.uniqueNumber || ( obj.uniqueNumber = NsVal.uuid++ );
            }: function( obj ){
                if(obj.nodeType !== 1){
                    return obj.uniqueNumber || ( obj.uniqueNumber = NsVal.uuid++ );
                }
                var uid = obj.getAttribute("uniqueNumber");
                if ( !uid ){
                    uid = NsVal.uuid++;
                    obj.setAttribute( "uniqueNumber", uid );
                }
                return +uid;//确保返回数字
            },
            /**
             * 生成键值统一的对象,用于高速化判定
             * @param {Array|String} array 如果是字符串,请用","或空格分开
             * @param {Number} val 可选,默认为1
             * @return {Object}
             */
            oneObject : function( array, val ){
                if( typeof array == "string" ){
                    array = array.match( $.rword ) || [];
                }
                var result = {}, value = val !== void 0 ? val :1;
                for(var i = 0, n = array.length; i < n; i++){
                    result[ array[i] ] = value;
                }
                return result;
            },
            config: function( settings ) {
                var kernel  = $.config;
                for ( var p in settings ) {
                    if (!settings.hasOwnProperty( p ))
                        continue
                    var prev = kernel[ p ];
                    var curr = settings[ p ];
                    if (prev && p === 'alias') {
                        for (var c in curr) {
                            if (curr.hasOwnProperty( c )) {
                                var prevValue = prev[ c ];
                                var currValue = curr[ c ];
                                if( prevValue && prev !== curr ){
                                    throw c + "不能重命名"
                                }
                                prev[ c ] = currValue;
                            }
                        }
                    } else {
                        kernel[ p ] = curr;
                    }
                }
                return this
            }
        });
        (function(scripts, cur){
            cur = scripts[ scripts.length - 1 ];//FF下可以使用DOC.currentScript
            var url = cur.hasAttribute ?  cur.src : cur.getAttribute( 'src', 4 );
            url = url.replace(/[?#].*/, '');
            var a = cur.getAttribute("debug");
            var b = cur.getAttribute("storage");
            var kernel = $.config;
            kernel.debug = a == 'true' || a == '1';
            kernel.storage = b == 'true' || b == '1';
            kernel.base = url.substr( 0, url.lastIndexOf('/') ) +"/";
            kernel.nick = cur.getAttribute("nick") || "$";
            kernel.erase = cur.getAttribute("erase") || "erase";
            kernel.alias = {};
            kernel.level = 9;
    
        })(DOC.getElementsByTagName( "script" ));
    
        $.noop = $.error = $.debug = function(){};
    
        "Boolean,Number,String,Function,Array,Date,RegExp,Window,Document,Arguments,NodeList".replace( $.rword, function( name ){
            class2type[ "[object " + name + "]" ] = name;
        });
    
        var Module = function (id, parent) {
            this.id = id;
            this.exports = {};
            this.parent = parent;
            var m = Module._load[parent]
            m && m.children.push(this);
            this.children = [];
        }
        Module._load = function( url, parent) {
            url = Module._resolve( url, parent.id )[0];
            var module = Module._cache[ url ];
            if (module) {
                return module.exports;
            }
        };
        Module._update = function(id, parent, factory, state, deps, args){
            var module =  Module._cache[id]
            if( !module){
                module = new Module(id, parent || $.config.base);
                Module._cache[id] = module;
            }
            module.callback = factory || $.noop;
            module.state = state || module.state;
            module.deps = deps || module.deps || {};
            module.args = args || module.args || [];
        }
        Module.prototype.require = function(a){
            var self = this;
            if(typeof a == "string"){
                return Module._load(path, self)
            }
            return function(path){
                return Module._load(path, self)
            }
        }
        Module._resolve = function(url, parent, ret){
            //[]里面,不是开头的-要转义,因此要用/^[-a-z0-9_$]{2,}$/i而不是/^[a-z0-9_-$]{2,}
            //别名至少两个字符;不用汉字是避开字符集的问题
            if( url === "ready"){//特别处理ready标识符
                return ["ready", "js"];
            }
            if(/^[-a-z0-9_$]{2,}$/i.test(url) && $.config.alias[url] ){
                ret = $.config.alias[url];
            }else{
                parent = parent.substr( 0, parent.lastIndexOf('/') )
                if(/^(\w+)(\d)?:.*/.test(url)){  //如果用户路径包含协议
                    ret = url
                }else {
                    var tmp = url.charAt(0);
                    if( tmp !== "." && tmp != "/"){  //相对于根路径
                        ret = $.config.base + url;
                    }else if(url.slice(0,2) == "./"){ //相对于兄弟路径
                        ret = parent + "/" + url.substr(2);
                    }else if( url.slice(0,2) == ".."){ //相对于父路径
                        var arr = parent.replace(/\/$/,"").split("/");
                        tmp = url.replace(/\.\.\//g,function(){
                            arr.pop();
                            return "";
                        });
                        ret = arr.join("/")+"/"+tmp;
                    }
                }
            }
            var ext = "js";
            tmp = ret.replace(/[?#].*/, '');
            if(/\.(\w+)$/.test( tmp )){
                ext = RegExp.$1;
            }
            if( tmp == ret && !/\.js$/.test(ret)){//如果没有后缀名会补上.js
                ret += ".js";
            }
            return [ret, ext];
        }
    
        var modules = $.modules = Module._cache = {};
        Module._update( "ready" );
        var rrequire = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g;
        var rcomment =  /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g
        var rparams =  /[^\(]*\(([^\)]*)\)[\d\D]*/;//用于取得函数的参数列表
        $.mix({
            //绑定事件(简化版)
            bind: W3C ? function( el, type, fn, phase ){
                el.addEventListener( type, fn, !!phase );
                return fn;
            } : function( el, type, fn ){
                el.attachEvent && el.attachEvent( "on"+type, fn );
                return fn;
            },
            unbind: W3C ? function( el, type, fn, phase ){
                el.removeEventListener( type, fn || $.noop, !!phase );
            } : function( el, type, fn ){
                if ( el.detachEvent ) {
                    el.detachEvent( "on" + type, fn || $.noop );
                }
            },
            //请求模块(依赖列表,模块工厂,加载失败时触发的回调)
            require: function( list, factory, parent ){
                var deps = {}, // 用于检测它的依赖是否都为2
                args = [],      // 用于依赖列表中的模块的返回值
                dn = 0,         // 需要安装的模块数
                cn = 0;         // 已安装完的模块数
                String(list).replace( $.rword, function(el){
                    var array = Module._resolve(el, parent || $.config.base ), url = array[0];
                    if(array[1] == "js"){
                        dn++
                        //如果没有注册,则先尝试通过本地获取,如果本地不存在或不支持,则才会出请求
                        loadStorage( url )
                        if( (!modules[ url ])  ){//&& loadStorage( url )
                            loadJS( url, parent );
                        }else if( modules[ url ].state === 2 ){
                            cn++;
                        }
                        if( !deps[ url ] ){
                            args.push( url );
                            deps[ url ] = "司徒正美";//去重
                        }
                    }else if(array[1] === "css"){
                        loadCSS( url );
                    }
                });
                var id = parent || "@cb"+ ( cbi++ ).toString(32);
                //创建或更新模块的状态
                Module._update(id, 0, factory, 1, deps, args);
                if( dn === cn ){//如果需要安装的等于已安装好的
                    return install( id, args, factory );//装配到框架中
                }
                //在正常情况下模块只能通过_checkDeps执行
                loadings.unshift( id );
                $._checkDeps();//FIX opera BUG。opera在内部解析时修改执行顺序,导致没有执行最后的回调
            },
            //定义模块
            define: function( parent, deps ){//模块名,依赖列表,模块本身
                var args = arguments;
                if( typeof deps === "boolean" ){//用于文件合并, 在标准浏览器中跳过补丁模块
                    if( deps ){
                        return;
                    }
                    [].splice.call( args, 1, 1 );
                }
                if( args.length === 2 ){//处理只有两个参数的情况,补允依赖列表
                    [].splice.call( args, 1, 0, [] );
                }
                if(typeof args[2] == "function"){
                    var factroy = args[2].toString()
                    .replace(rcomment,"")
                    .replace(rrequire,function(a,b){
                        args[1].push(b);//将模块工厂中以node.js方式加载的模块也加载进来
                        return a;
                    });
                    if(this.exports && this.id && $.config.storage && !Storage.getItem( this.id) ){
                        Storage.setItem( this.id, factroy);
                        Storage.setItem( this.id+"_deps", args[1]+"");
                        Storage.setItem( this.id+"_parent",  this.parent);
                        Storage.setItem( this.id+"_version", new Date - 0);
                    }
                }else{
                    var ret = args[2];
                    args[2] = function(){
                        return ret
                    }
                }
                $.require( args[1], args[2], parent ); //0,1,2 --> 1,2,0
            },
            _checkFail : function(  doc, id, error ){
                doc && (doc.ok = 1);
                if( error || !modules[ id ].state ){
                    $.log( (error || modules[ id ].state )+"   "+id, 3);
                    this.log("Failed to load [[ "+id+" ]]"+modules[ id ].state);
                }
            },
            //检测此JS模块的依赖是否都已安装完毕,是则安装自身
            _checkDeps: function (){
                loop:
                for ( var i = loadings.length, id; id = loadings[ --i ]; ) {
                    var obj = modules[ id ], deps = obj.deps;
                    for( var key in deps ){
                        if( deps.hasOwnProperty( key ) && modules[ key ].state != 2 ){
                            continue loop;
                        }
                    }
                    //如果deps是空对象或者其依赖的模块的状态都是2
                    if( obj.state != 2){
                        loadings.splice( i, 1 );//必须先移除再安装,防止在IE下DOM树建完后手动刷新页面,会多次执行它
                        install( obj.id, obj.args, obj.callback );
                        $._checkDeps();
                    }
                }
            },
            erase : function( id, v ){
                if(id == void 0){
                    Storage.clear();
                }else{
                    var old = Storage.getItem( id+"_version" );
                    if(old && (!v || v > Number(old)) ){
                        Storage.removeItem( id );
                        Storage.removeItem( id+"_deps" )
                        Storage.removeItem( id+"_parent" )
                        Storage.removeItem( id+"_version" )
                    }
                }
            }
        });
        var Storage =  {
            setItem: $.noop,
            getItem: $.noop,
            removeItem: $.noop,
            clear: $.noop
        }
        if( global.localStorage){
            Storage = localStorage;
        }else if( HTML.addBehavior){
            HTML.addBehavior('#default#userData');
            HTML.save("massdata");
            //https://github.com/marcuswestin/store.js/issues/40#issuecomment-4617842
            //在IE67它对键名非常严格,不能有特殊字符,否则抛throwed an This name may not contain the '~' character: _key-->~<--
            var rstoragekey = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", "g")
            function curry(fn) {
                return function(a, b) {
                    HTML.load("massdata");
                    a = String(a).replace(rstoragekey, function(w){
                        return w.charCodeAt(0);
                    })
                    var result = fn( a, b );
                    HTML.save("massdata");
                    return result
                }
            }
            Storage = {
                setItem : curry(function(key, val){
                    HTML.setAttribute(key, val);
                }),
                getItem: curry(function(key){
                    return HTML.getAttribute(key);
                }),
                removeItem: curry(function(key){
                    HTML.removeAttribute(key);
                }),
                clear: function(){
                    var attributes = HTML.XMLDocument.documentElement.attributes
                    for (var i=0, attr; attr=attributes[i]; i++) {
                        HTML.removeAttribute(attr.name)
                    }
                }
            }
        }
        var rerase = new RegExp('(?:^| )' + $.config.erase + '(?:(?:=([^;]*))|;|$)')
        var match = String(DOC.cookie).match( rerase );
        //读取从后端过来的cookie指令,转换成一个对象,键名为模块的URL,值为版本号(这是一个时间戮)
        if(match && match[1]){
            try{
                var obj = eval("0,"+match[1]);
                for(var i in obj){//$.erase会版本号比现在小的模块从本地储存中删掉
                    $.erase(i, obj[i])
                }
            }catch(e){}
        }
        function loadStorage( id ){
            var factory =  Storage.getItem( id);
            if(factory && !modules[id]){
                var parent = Storage.getItem(id+"_parent");
                var deps = Storage.getItem(id+"_deps");
                deps = deps ?  deps.match( $.rword ) : "";
                Module._update( id, parent );
                var module = $.modules[ id ];
                module.state =  module.state || 1;
                var fn = Function( "$,module,exports,require,define","return "+ factory )
                ($, module, module.exports, module.require());
                $.define( id, deps, fn );
            }
        }
    
        function loadCSS(url){
            var id = url.replace(rmakeid,"");
            if (DOC.getElementById(id))
                return
            var link =  DOC.createElement('link');
            link.charset = "utf-8"
            link.rel = 'stylesheet'
            link.href = url;
            link.type="text/css"
            link.id = id
            HEAD.insertBefore( link, HEAD.firstChild );
        }
    
        var loadJS = function( url, parent ){
            Module._update( url, parent );
            var iframe = DOC.createElement("iframe"),//IE9的onload经常抽疯,IE10 untest
            codes = ['<script>var nick ="', url, '", $ = {}, Ns = parent.', $.config.nick,
            '; $.define = ', innerDefine, ';var define = $.define;<\/script><script src="',url,'" ',
            (DOC.uniqueID ? 'onreadystatechange="' : 'onload="'),
            "if(/loaded|complete|undefined/i.test(this.readyState) ){  Ns._checkDeps(); ",
            'Ns._checkFail(self.document, nick);}',
            '" onerror="Ns._checkFail(self.document, nick, true);" ><\/script>' ];
            iframe.style.display = "none";//opera在11.64已经修复了onerror BUG
            //http://www.tech126.com/https-iframe/ http://www.ajaxbbs.net/post/webFront/https-iframe-warning.html
            if( !"1"[0] ){//IE6 iframe在https协议下没有的指定src会弹安全警告框
                iframe.src = "javascript:false"
            }
            HEAD.insertBefore( iframe, HEAD.firstChild );
            var doc = iframe.contentDocument || iframe.contentWindow.document;
            doc.write( codes.join('') );
            doc.close();
            $.bind( iframe, "load", function(){
                if( global.opera && doc.ok != 1 ){//ok写在$._checkFail里面
                    $._checkFail(doc, url, true );//模拟opera的script onerror
                }
                doc.write( "<body/>" );//清空内容
                HEAD.removeChild( iframe );//移除iframe
                iframe = null;
            });
        }
        var innerDefine = function(  ){
            var args = Array.apply([],arguments);
            if(typeof args[0] == "string"){
                args.shift()
            }
            args.unshift( nick );  //劫持第一个参数,置换为当前JS文件的URL
            var module = Ns.modules[ nick ];
            module.state = 1
            var last = args.length - 1;
            if( typeof args[ last ] == "function"){
                //劫持模块工厂,将$, exports, require, module等对象强塞进去
                args[ last ] =  parent.Function( "$,module,exports,require","return "+ args[ last ] )
                (Ns, module, module.exports, module.require());//使用curry方法劫持模块自身到require方法里面
            }
            Ns.define.apply(module, args);  //将iframe中的函数转换为父窗口的函数
        }
    
        
        function install( id, deps, callback ){
            for ( var i = 0, array = [], d; d = deps[i++]; ) {
                array.push( modules[ d ].exports );//从returns对象取得依赖列表中的各模块的返回值
            }
            var module = Object( modules[id] ), ret;
            var common = {
                exports: module.exports,
                require: typeof module.require == "function" ? module.require() : $.noop,
                module:  module
            }
            var match = callback.toString().replace(rparams,"$1").replace(rcomment,"").match($.rword)||[]
            var a = common[match[0]];
            var b = common[match[1]];
            var c = common[match[2]];
            if( a && b && a != b && b != c  ){//exports, require, module的位置随便
                ret =  callback.apply(global, [a, b, c]);
            }else{
                ret =  callback.apply(global, array);
            }
            module.state = 2;
            if( ret !== void 0 ){
                modules[ id ].exports = ret
            }
            return ret;
        }
        all.replace($.rword,function(a){
            $.config.alias[ "$"+a ] = $.config.base + a + ".js"
        });
        //domReady机制
        var readyFn, ready =  W3C ? "DOMContentLoaded" : "readystatechange" ;
        function fireReady(){
            modules[ "ready" ].state = 2;
            $._checkDeps();
            if( readyFn ){
                $.unbind( DOC, ready, readyFn );
            }
            fireReady = $.noop;//隋性函数,防止IE9二次调用_checkDeps
        };
        function doScrollCheck() {
            try {
                HTML.doScroll( "left" ) ;
                fireReady();
            } catch(e) {
                setTimeout( doScrollCheck, 31 );
            }
        };
    
        if ( DOC.readyState === "complete" ) {
            fireReady();//如果在domReady之外加载
        }else {
            $.bind( DOC, ready, readyFn = function(){
                if ( W3C || DOC.readyState === "complete" ){
                    fireReady();
                }
            });
            if( HTML.doScroll && self.eval === parent.eval)
                doScrollCheck();
        }
        var rdebug =  /^(init|constructor|lang|query)$|^is|^[A-Z]/;
        function debug(obj, name, module, p){
            var fn = obj[name];
            if( obj.hasOwnProperty(name) && typeof fn == "function" && !fn["@debug"]){
                if( rdebug.test( name )){
                    fn["@debug"] = name;
                }else{
                    var method = obj[name] = function(){
                        try{
                            return  method["@debug"].apply(this,arguments)
                        }catch(e){
                            $.log( module+"'s "+(p? "$.fn." :"$.")+name+" method error "+e);
                            throw e;
                        }
                    }
                    for(var i in fn){
                        method[i] = fn[i];
                    }
                    method["@debug"] = fn;
                    method.toString = function(){
                        return fn.toString()
                    }
                    method.valueOf = function(){
                        return fn.valueOf();
                    }
                }
            }
        }
        $.debug = function(name){
            if(!$.config.debug )
                return
            for( var i in $){
                debug($, i, name);
            }
            for( i in $.prototype){
                debug($.prototype, i, name,1);
            }
        }
        global.VBArray && ("abbr,article,aside,audio,bdi,canvas,data,datalist,details,figcaption,figure,footer," +
            "header,hgroup,mark,meter,nav,output,progress,section,summary,time,video").replace( $.rword, function( tag ){
            DOC.createElement(tag);
        });
        for(var i in $){
            if( !$[i].mass && typeof $[i] == "function"){
                $[i]["@debug"] = i;
            }
        }
        //https://developer.mozilla.org/en/DOM/window.onpopstate
        $.bind( global, "popstate", function(){
            NsKey = DOC.URL.replace(rmakeid,'');
            $.exports();
        });
        $.exports( $.config.nick +  postfix );//防止不同版本的命名空间冲突
    /*combine modules*/
    
    }( self, self.document );//为了方便在VS系列实现智能提示,把这里的this改成self或window
    

    历史回顾!不断完善,臻于完美!

    /*
    v17 http://www.cnblogs.com/rubylouvre/archive/2012/08/30/2662477.html
    v16 http://www.cnblogs.com/rubylouvre/archive/2012/04/26/2470700.html
    v15 http://www.cnblogs.com/rubylouvre/archive/2012/01/30/2329342.html
    v14 http://www.cnblogs.com/rubylouvre/archive/2011/12/19/2293878.html
    v13 http://www.cnblogs.com/rubylouvre/archive/2011/11/17/2251868.html
    v12 http://www.cnblogs.com/rubylouvre/archive/2011/10/27/2226228.html
    v11 http://www.cnblogs.com/rubylouvre/archive/2011/10/09/2203826.html
    v10 http://www.cnblogs.com/rubylouvre/archive/2011/09/25/2189529.html
    v9 http://www.cnblogs.com/rubylouvre/archive/2011/08/22/2147058.html
    v8 http://www.cnblogs.com/rubylouvre/archive/2011/08/08/2129951.html
    v7 http://www.cnblogs.com/rubylouvre/archive/2011/08/05/2127791.html
    v6 http://www.cnblogs.com/rubylouvre/archive/2011/07/12/2104777.html
    v5 http://www.cnblogs.com/rubylouvre/archive/2011/04/12/2011175.html
    v4 http://www.cnblogs.com/rubylouvre/archive/2011/03/01/1968397.html
    v3 http://www.cnblogs.com/rubylouvre/archive/2011/02/11/1951104.html
    v2 没有保留下来
    v1 没有保留下来
    */
    
  • 相关阅读:
    开源框架.netCore DncZeus学习(五)下拉树的实现
    开源框架.netCore DncZeus学习(四)项目升级
    github下载更新代码到本地
    AndroidStudio替换空行
    Ext.net获取选中行数据
    OZCode
    禁止密码显示框
    Android layout_weight理解
    微信页面关于点击按钮关注公众号放到链接里无关注按钮
    进入页面就触发了popstate事件。
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2684061.html
Copyright © 2011-2022 走看看