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