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

23 篇文章 0 订阅
11 篇文章 0 订阅

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

  • 事件
名称参数描述
onProgressname当一个模块成功载入时触发
onLoadname当一个模块及其所有依赖关系(可以理解为载入该模块所需要的其他模块、属性、方法等)载入时触发

  • 使用示例:
<%@ 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文档。一搜问题轻松解决了,好像是自己能会使用这个东西了,而且相比较一行行地读源码分析解决问题快多了。但下次遇到相同的东西,还是继续上网搜。小的项目这样看不出什么,如果真要做开发工作,这样的效率很低,因为没有形成自己逻辑思维,也没有自己思考总结,不管用多少次,都没有自己地东西。个人觉得,如果可以的话,尝试自己读一读源码,一是可以熟悉自己所使用框架或技术的基本原理,二对自己的思维逻辑提升也有好处,毕竟好的框架能够被广泛使用势必有其它框架不能比的优势。搜索再强大也比不上从自己脑子里读取快。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值