EasyUI学习笔记(四)——学习读源码--easyloader源码阅读分析

来源:互联网 发布:生意通软件手机版 编辑:程序博客网 时间:2024/05/21 11:34

easyloader :

在上篇笔记中简单概括下:

  • easyloader作用:

    easyloader模块是用来加载jquery easyui的js和css文件的,而且可以分析模块的依赖关系,先加载依赖项。模块加载好了会调用parse模块来解析页面。把class是easyui开头的标签都转化成easyui的控件。

  • 分析源代码:

    具体的分析笔记都已写在注释中:

/** * jQuery EasyUI 1.5 *  * Copyright (c) 2009-2016 www.jeasyui.com. All rights reserved. *  * Licensed under the freeware license: * http://www.jeasyui.com/license_freeware.php To use it on other terms please * contact us: info@jeasyui.com *  */(function() {    // modules : _1,(见第380行),将所有的插件、插件资源和依赖文件放进modules对象中    var _1 = {        draggable : { // 可拖拽            js : "jquery.draggable.js"        },        droppable : { // 可释放            js : "jquery.droppable.js"        },        resizable : { // 可调整尺寸            js : "jquery.resizable.js"        },        linkbutton : { // 链接按钮            js : "jquery.linkbutton.js",            css : "linkbutton.css"        },        progressbar : { // 进度条            js : "jquery.progressbar.js",            css : "progressbar.css"        },        tooltip : { // 工具提示框            js : "jquery.tooltip.js",            css : "tooltip.css"        },        pagination : { // 分页            js : "jquery.pagination.js",            css : "pagination.css",            dependencies : [ "linkbutton" ]        },        datagrid : { // 数据表格            js : "jquery.datagrid.js",            css : "datagrid.css",            dependencies : [ "panel", "resizable", "linkbutton", "pagination" ]        },        treegrid : { // 树形表格            js : "jquery.treegrid.js",            css : "tree.css",            dependencies : [ "datagrid" ]        },        propertygrid : { // 属性表格            js : "jquery.propertygrid.js",            css : "propertygrid.css",            dependencies : [ "datagrid" ]        },        datalist : { // 数据列表            js : "jquery.datalist.js",            css : "datalist.css",            dependencies : [ "datagrid" ]        },        panel : { // 面板            js : "jquery.panel.js",            css : "panel.css"        },        window : { // 窗口            js : "jquery.window.js",            css : "window.css",            dependencies : [ "resizable", "draggable", "panel" ]        },        dialog : { // 对话框            js : "jquery.dialog.js",            css : "dialog.css",            dependencies : [ "linkbutton", "window" ]        },        messager : { // 消息框            js : "jquery.messager.js",            css : "messager.css",            dependencies : [ "linkbutton", "dialog", "progressbar" ]        },        layout : { // 布局            js : "jquery.layout.js",            css : "layout.css",            dependencies : [ "resizable", "panel" ]        },        form : { // 表单            js : "jquery.form.js"        },        menu : { // 菜单            js : "jquery.menu.js",            css : "menu.css"        },        tabs : { // 标签页选项卡            js : "jquery.tabs.js",            css : "tabs.css",            dependencies : [ "panel", "linkbutton" ]        },        menubutton : { // 菜单按钮            js : "jquery.menubutton.js",            css : "menubutton.css",            dependencies : [ "linkbutton", "menu" ]        },        splitbutton : { // 拆分按钮            js : "jquery.splitbutton.js",            css : "splitbutton.css",            dependencies : [ "menubutton" ]        },        switchbutton : { // 开关按钮            js : "jquery.switchbutton.js",            css : "switchbutton.css"        },        accordion : { // 手风琴            js : "jquery.accordion.js",            css : "accordion.css",            dependencies : [ "panel" ]        },        calendar : { // 日历            js : "jquery.calendar.js",            css : "calendar.css"        },        textbox : { // 文本框            js : "jquery.textbox.js",            css : "textbox.css",            dependencies : [ "validatebox", "linkbutton" ]        },        passwordbox : { // 密码输入框            js : "jquery.passwordbox.js",            css : "passwordbox.css",            dependencies : [ "textbox" ]        },        filebox : { // 文件框            js : "jquery.filebox.js",            css : "filebox.css",            dependencies : [ "textbox" ]        },        combo : { // 组合            js : "jquery.combo.js",            css : "combo.css",            dependencies : [ "panel", "textbox" ]        },        combobox : { // 组合框            js : "jquery.combobox.js",            css : "combobox.css",            dependencies : [ "combo" ]        },        combotree : { // 组合树            js : "jquery.combotree.js",            dependencies : [ "combo", "tree" ]        },        combogrid : { // 组合网格            js : "jquery.combogrid.js",            dependencies : [ "combo", "datagrid" ]        },        combotreegrid : { // 下拉表格树            js : "jquery.combotreegrid.js",            dependencies : [ "combo", "treegrid" ]        },        validatebox : { // 验证框            js : "jquery.validatebox.js",            css : "validatebox.css",            dependencies : [ "tooltip" ]        },        numberbox : { // 数字框            js : "jquery.numberbox.js",            dependencies : [ "textbox" ]        },        searchbox : { // 搜索框            js : "jquery.searchbox.js",            css : "searchbox.css",            dependencies : [ "menubutton", "textbox" ]        },        spinner : { // 微调器            js : "jquery.spinner.js",            css : "spinner.css",            dependencies : [ "textbox" ]        },        numberspinner : { // 数值微调器            js : "jquery.numberspinner.js",            dependencies : [ "spinner", "numberbox" ]        },        timespinner : { // 时间微调器            js : "jquery.timespinner.js",            dependencies : [ "spinner" ]        },        tree : { // 树            js : "jquery.tree.js",            css : "tree.css",            dependencies : [ "draggable", "droppable" ]        },        datebox : { // 日期框            js : "jquery.datebox.js",            css : "datebox.css",            dependencies : [ "calendar", "combo" ]        },        datetimebox : { // 日期时间框            js : "jquery.datetimebox.js",            dependencies : [ "datebox", "timespinner" ]        },        slider : { // 滑块            js : "jquery.slider.js",            dependencies : [ "draggable" ]        },        parser : { // 解析器            js : "jquery.parser.js"        },        mobile : {            js : "jquery.mobile.js"        }    };    // 将国际化文件放入一个locales对象中    var _2 = {        "af" : "easyui-lang-af.js",        "ar" : "easyui-lang-ar.js",        "bg" : "easyui-lang-bg.js",        "ca" : "easyui-lang-ca.js",        "cs" : "easyui-lang-cs.js",        "cz" : "easyui-lang-cz.js",        "da" : "easyui-lang-da.js",        "de" : "easyui-lang-de.js",        "el" : "easyui-lang-el.js",        "en" : "easyui-lang-en.js",        "es" : "easyui-lang-es.js",        "fr" : "easyui-lang-fr.js",        "it" : "easyui-lang-it.js",        "jp" : "easyui-lang-jp.js",        "nl" : "easyui-lang-nl.js",        "pl" : "easyui-lang-pl.js",        "pt_BR" : "easyui-lang-pt_BR.js",        "ru" : "easyui-lang-ru.js",        "sv_SE" : "easyui-lang-sv_SE.js",        "tr" : "easyui-lang-tr.js",        "zh_CN" : "easyui-lang-zh_CN.js",        "zh_TW" : "easyui-lang-zh_TW.js"    };    // _3=queues,加载队列,定义一个局部变量,做循环遍历时候,存放状态     var _3 = {};    // _4=loadJs,_5=url _6=callback ,加载js文件函数,过程就是动态创建一个script标签,然后添加到head标签中去    function _4(_5, _6) {        // _7=done,标志变量,js是否加载并执行        var _7 = false;        // _8=script,创建script dom        var _8 = document.createElement("script");        _8.type = "text/javascript";        _8.language = "javascript";        _8.src = _5; // _5=url        /*         * 监听了script标签的两个事件函数,一个是onload,另一个是onreadystatechange,这个主要是针对IE和非IE浏览器准备的。         *  onload是firefox 浏览器事件,onreadystatechange是ie的,为了兼容,两个都写上,这样写会导致内存泄露         */         _8.onload = _8.onreadystatechange = function() {            /*             * script.readyState只是ie下有这个属性:             * 如果这个值为undefined,说明是在firefox,就直接可以执行下面的代码了;             * 反之为ie,需要对script.readyState状态具体值进行判别:             * loaded和complete状态表示,脚本加载了并执行了             */            if (!_7                    && (!_8.readyState || _8.readyState == "loaded" || _8.readyState == "complete")) {                _7 = true;                // 释放内存,还会泄露                _8.onload = _8.onreadystatechange = null;                // 加载后执行回调                if (_6) {                    _6.call(_8);                }            }        };        // 具体的加载动作,上面的onload是注册事件        document.getElementsByTagName("head")[0].appendChild(_8);    }    ;    /**      * _9=runJs,运行js文件。就是把js文件加载进来,在js执行之后将这个script再remove删除掉 ,主要用来加载国际化文件     * @param url js的url      * @callback 回调函数,执行完js时会调用这个函数      */     function _9(_a, _b) {        _4(_a, function() {            document.getElementsByTagName("head")[0].removeChild(this);            if (_b) {                _b();            }        });    }    ;    /**      * 加载css文件。和加载js文件一样,动态创建一个link标签,然后追加到head标签中去      * @param url css的url      * @param callback 回调函数,加载完成后调用此函数      */      function _c(_d, _e) {        var _f = document.createElement("link");        _f.rel = "stylesheet";        _f.type = "text/css";        _f.media = "screen";        _f.href = _d;        document.getElementsByTagName("head")[0].appendChild(_f);        if (_e) {            _e.call(_f);        }    }    ;    /**      * _10=loadSingle(name,callback),加载单独的一个plugin      * 分析module可以发现plugin之间通过dependence依赖构造了一棵依赖树     */    function _10(_11, _12) { // 加载具体树中的一个节点        // queues[name] , 加载队列存入该plugin名,并把整个plugin的状态设置为loading        _3[_11] = "loading";        // var module = modules[name];根据模块名,取出该模块定义        var _13 = _1[_11];        // jsStatus,js的加载状态,把js状态设置为loading        var _14 = "loading";        // _15=cssStatus,css加载状态,从这里可以看出easyloader.css就是一个开关变量,控制是否加载模块相应的css文件        //_13=module 如果允许css,并且plugin有css,则加载css,否则设置加载过了,其实就是不加载        var _15 = (easyloader.css && _13["css"]) ? "loading" : "loaded";        // 是css文件,加载css,plugin 的css,如果是全称,就用全称,否则把简写换成全称,所以简写的css文件要放入到themes/type./文件下        if (easyloader.css && _13["css"]) {            if (/^http/i.test(_13["css"])) {                var url = _13["css"];            } else {                var url = easyloader.base + "themes/" + easyloader.theme + "/"                        + _13["css"];            }            _c(url, function() {                _15 = "loaded";                // js, css都加载完,才调用回调                if (_14 == "loaded" && _15 == "loaded") {                    _16(); // _16=finish                }            });        }        // 如果是js文件,就用LoadJs来加载js,全称用全称,简写补全        if (/^http/i.test(_13["js"])) {            var url = _13["js"];        } else {            var url = easyloader.base + "plugins/" + _13["js"];        }        _4(url, function() {            _14 = "loaded";            if (_14 == "loaded" && _15 == "loaded") {                _16();            }        });        /**         * _16()=finish()函数,来结束加载。         * 加载完调用的方法,并触发onProgress函数,每加载成功一个模块,就调用一次onProgress,改变plugin状态         */        function _16() {            _3[_11] = "loaded";            // 调用正在加载的方法,其实已经加载完了            easyloader.onProgress(_11);            if (_12) {                _12();            }        }        ;    }    ;    /**      * loadModule(name, callback),easyui模块加载函数      * @param name 模块名,可以是string,也可以是数组      * @param callback 回调函数,当加载结束后会调用此函数      */      function _17(_18, _19) {        // 模块名数组,根据依赖关系,从前到后,依次排开,最后是形成的是依赖插件列表,最独立的插件放在首位,name是末尾        var mm = [];                //_1a=doLoad,加载标识,当其值为true时,表示需要加载的模块已经加载好了        var _1a = false;            //模块名name支持两种,一种是string ,一种是string array,这样一次可以加载多个plugin,都是调用add方法进行添加          if (typeof _18 == "string") {            // 是string的时候,调用add方法把模块名push到mm数组中去             add(_18);        } else {            for (var i = 0; i < _18.length; i++) {                // 是数组的时候,循环调用add()方法把模块名统统push到mm数组中去                add(_18[i]);            }        }        /**          * loadModule函数中内嵌的一个函数,用来加载模块名到变量mm数组中去          * @param name 模块名,只能是string          */         function add(_1b) {            // 相当于一个保护措施,如果在modules中该模块名不存在,也就是说没有这个plugin不存在,我们就不要加载了直接退出            if (!_1[_1b]) {                return;            }            //如果modules有这个plugin,就去查看它是否依赖其他plugin            var d = _1[_1b]["dependencies"];            //如果它依赖了其它plugin,就加载依赖的plugin。同时再加载依赖的plugin的依赖。            if (d) {                for (var i = 0; i < d.length; i++) {                    add(d[i]);  //在循环中调用了add() 也就是是递归                }            }            mm.push(_1b);// 把模块名放到mm中        }        ;        /**          * 当一个模块plugin及其依赖模块加载完成时,执行回调函数,并且触发onLoad函数          */        function _1c() {            if (_19) {                _19();            }            //调用onLoad,传递name 为参数              easyloader.onLoad(_18);        }        ;//形成依赖树,下面就是做实质性工作——加载        //_1d = time ,加载用时          var _1d = 0;        /**          * _1e() = loadMm(),定义一个加载方法,加载所需要的模块,定义后直接调用         *  需要的模块,我们已经统计好了,并按依赖关系,先后push到mm中去了          */        function _1e() {            //如果mm有长度,长度!=0,加载plugin,为0,即加载完毕,开始加载国际化文件            if (mm.length) {    // 判断mm是不是空的                var m = mm[0];  //第一个module                // 加载队列不包含此模块,状态序列中没有这个plugin的信息,说明没有加载这个plug,调用laodSingle进行加载                if (!_3[m]) {                    _1a = true; // 把doLoad置成true,表示开始加载                    // 调用loadSingle方法来加载模块,加载成功后会把此模块从mm中shift掉,然后继续调用loadMM方法,就形成了递归调用                    _10(m, function() {                        mm.shift();//加载完成后,将这个元素从数组去除,在继续加载,直到数组内的元素都加载完                         _1e();                    });                } else {                    //加载队列已经加载过此模块了,不需要在加载了,直接从mm中shift掉即可                    if (_3[m] == "loaded") {                        mm.shift();                        _1e();                    }                     else {                        // 正在加载该模块,累计所用时间如果没有超过timeout  超过10毫秒再调用一次loadMm函数                        if (_1d < easyloader.timeout) {//若是超时了,10秒钟调用一次loadMn()                            _1d += 10;                            setTimeout(arguments.callee, 10);//arguments.callee代表函数本身                        }                    }                }            } else {                // 走到这里,表示该加载的模块都已经加载好了                if (easyloader.locale && _1a == true && _2[easyloader.locale]) {                    // 如果设置了国际化,并且已经加载好了,而且该国际化资源还存在,那么加载该资源js                    var url = easyloader.base + "locale/"+ _2[easyloader.locale];                    // runJs执行js完事后,调用finish方法                    _9(url, function() {                        _1c();                    });                }                 else {// 没定义国际化文件,就直接调用finish方法                    _1c();                }            }        }        ;        _1e();    }    ;    /**      * 定义一个加载器     * easyloader定义为全局变量 没有var     */     easyloader = {        // 各个模块文件的定义,包括js、css和依赖模块        modules : _1,        // 国际化资源文件        locales : _2,        // jquery-easyui的根目录,该属性是为了加载js,记录文件夹路径的,在加载easyloader时,会自动根据放置的位置而改变        base : ".",         // 控件的主题        theme : "default",  //默认主题        // 一个开关变量,控制easyloader加载模块时,要不要加载相应的css文件        css : true,//默认是需要加载的        /*国际化语言,可以根据window.navigator.language或者window.navigator.userLanguage来获取当前浏览器的语言。              有两个属性,主要因为IE浏览器只认识userLanguage和sysLanguage*/        locale : null,        // 加载超时事件,加载一个模块的最长时间,超过这个时间,就开始加载下一个模块了        timeout : 2000,         /**         * easyloader.load(name, callback),该模块加载的调用方法,先加载css,然后加载js          * name是模块名,callback是加载成功后执行的函数         */        load : function(_1f, _20) {            if (/\.css$/i.test(_1f)) {// 如果模块名是以.css结尾                 if (/^http/i.test(_1f)) { // 如果模块名是以http开头,那么css是一个文件的url                    _c(_1f, _20);                } else {                    //不是http的,说明模块名相对于jquery easyui根目录来说的,加上base.文件夹路径                     _c(easyloader.base + _1f, _20);                }            }             //加载js文件            else {                if (/\.js$/i.test(_1f)) {// 如果模块名是以.js结尾                     if (/^http/i.test(_1f)) {// 如果模块名是以http开头,那么js是一个文件的url                        _4(_1f, _20);                    }                     else {// 否则,说明模块名相对于jquery easyui根目录来说的                        _4(easyloader.base + _1f, _20);                    }                }                 else { //以上两种都不是,直接传递一个插件名,说明是easyui自己的模块,就去modole数组中直接使用loadModule来加载。该方法是重点,也是easyui自带的plugin加载方式                    _17(_1f, _20);                }            }        },        // 当一个模块加载完会触发此函数        onProgress : function(_21) {        },        // 当一个模块和其依赖都加载完会触发此函数        onLoad : function(_22) {        }    };    //     //      /**      * 以上一直在定义函数、变量,此处是真正开始执行处      * _23=scripts 获取页面的所有的script,主要是为了获取现在解释的easyloader.js文件路径,来设置base属性     * 就是查找jquery-easyui的根目录,并赋值给easyloader的base属性上 这样easyloader再加载css文件和js文件就很方便定位了。      */    var _23 = document.getElementsByTagName("script");    for (var i = 0; i < _23.length; i++) {        var src = _23[i].src;        if (!src) {            continue;        }        var m = src.match(/easyloader\.js(\W|$)/i);    //判断文件是否含有easyloader.js         if (m) {            //如果有,base就是easyuiloader.js的相同前缀            easyloader.base = src.substring(0, m.index);        }    }    /**      * 定义一个简化调用接口      * 这个就起一个别名的作用,比如页面中可以想如下这么下:      * using('window');      * 这样window模块就加载进来了!      */    window.using = easyloader.load;    /**      * easyloader.js加载的第一个模块是parse模块,     * parser模块调用parse方法,可以解析页面上的easyui控件      */      if (window.jQuery) {        jQuery(function() {            //系统数据加载完后,加载parser.js插件,该插件是渲染界面的             easyloader.load("parser", function() {                jQuery.parser.parse();//渲染方法            });        });    }})();

  • 属性:
名称 属性 描述 默认值 modules(模块) object(对象) 预定义模块 locales(多语言) object(预定义对象) 预定义多语言 base(目录) string(字符串) easyui根目录,必须以’/’结尾 easyui根目录将会被自动设置为相对于easyload.js的位置 theme(主题名称) string(字符串) ‘themes’目录中的主题的名称 default css(层叠样式表) boolean(布尔型) 定义是否在加载模块的同时加载css文件 true locale(自定义语言) string(字符串) 语言名称 null timeout(超时) number(数字) 超时单位为毫秒,出现超时就触发 2000
  • 事件
名称 参数 描述 onProgress name 当一个模块成功载入时触发 onLoad name 当一个模块及其所有依赖关系(可以理解为载入该模块所需要的其他模块、属性、方法等)载入时触发
  • 使用示例:
<%@ page language="java" pageEncoding="UTF-8"%><!DOCTYPE html><head>    <title>EasyUI学习笔记————EasyUI的easyLoader源码分析</title>    <!-- 引入jQuery 核心库-->        <script type="text/javascript" src="${pageContext.request.contextPath}/jquery-easyui-1.5/jquery.min.js"></script>        <!-- 引入easyLoader.js -->        <script type="text/javascript" src="${pageContext.request.contextPath}/jquery-easyui-1.5/easyloader.js"></script>    <script>        function load1(){            using('calendar', function(){                $('#cc').calendar({                    width:180,                    height:180                });            });        }        function load2(){            using(['dialog','messager'], function(){                $('#dd').dialog({                    title:'Dialog',                    width:300,                    height:200                });                $.messager.show({                    title:'info',                    msg:'dialog created'                });            });        }    </script></head><body>    <h1>EasyLoader Test</h1>    <a href="#" class="easyui-linkbutton" onclick="load1()">Load Calendar</a>    <a href="#" class="easyui-linkbutton" onclick="load2()">Load Dialog</a>    <div id="cc"></div>    <div id="dd"></div></html>
  • 运行效果:

    这里写图片描述


最近在学习前端的有关技术,也思考一个问题:
之前自己用过的东西,再次使用的时候,为什么效率还是那么低,相比较第一次没有进步。分析原因,就是太依赖于搜索引擎,实现什么样的效果或功能,自己写一点遇到问题直接上网搜一下,从来不会看框架源码也不看demo文档。一搜问题轻松解决了,好像是自己能会使用这个东西了,而且相比较一行行地读源码分析解决问题快多了。但下次遇到相同的东西,还是继续上网搜。小的项目这样看不出什么,如果真要做开发工作,这样的效率很低,因为没有形成自己逻辑思维,也没有自己思考总结,不管用多少次,都没有自己地东西。个人觉得,如果可以的话,尝试自己读一读源码,一是可以熟悉自己所使用框架或技术的基本原理,二对自己的思维逻辑提升也有好处,毕竟好的框架能够被广泛使用势必有其它框架不能比的优势。搜索再强大也比不上从自己脑子里读取快。

0 0