在Bootstrap.js文件中,总共有1500行(包含注释和空行),使用编辑器的代码折叠功能就如下图可以一窥全貌了。

从代码可以看到,这里主要定义了Ext.Boot、Ext.globalEval、Ext.Microloader和Ext.manifest这4个对象或属性。关键代码是最后一句的调用Ext.Microloader的load方法,下面来研究一下这个load方法,代码如下:

load: function (manifestDef) {
var manifest = Microloader.initManifest(manifestDef),
loadOrder = manifest.loadOrder,
loadOrderMap = (loadOrder) ? Boot.createLoadOrderMap(loadOrder) : null,
urls = [],
js = manifest.js || [],
css = manifest.css || [],
resources = js.concat(css),
resource, i, len, include,
loadedFn = function () {
_loaded = true;
Microloader.notify();
}; for (len = resources.length, i = 0; i < len; i++) {
resource = resources[i];
include = true;
if (resource.platform && !Microloader.filterPlatform(resource.platform)) {
include = false;
}
if (include) {
urls.push(resource.path);
}
} if (loadOrder) {
manifest.loadOrderMap = loadOrderMap;
} Boot.load({
url: urls,
loadOrder: loadOrder,
loadOrderMap: loadOrderMap,
sequential: true,
success: loadedFn,
failure: loadedFn
});
},

代码第一句执行了Microloader的initManifest方法,代码如下:

initManifest: function (manifest) {
Microloader.init();
var tmpManifest = manifest || Ext.manifest; if (typeof tmpManifest === "string") {
var url = Boot.baseUrl + tmpManifest + ".json",
content = Boot.fetchSync(url);
tmpManifest = JSON.parse(content.content);
} Ext.manifest = tmpManifest;
return tmpManifest;
},

根据load方法的调用,可以知道manifest为null,不过这里第一句又先调用了Microloader的init方法,代码如下:

init: function () {
Microloader.initPlatformTags();
Ext.filterPlatform = Microloader.filterPlatform;
},

又要跳到initPlatformTags方法,快给转晕了,代码如下:

initPlatformTags: function () {
Microloader.platformTags = Microloader.detectPlatformTags(Microloader.platformTags);
},

还跳,这里省去n字,继续去看detectPlatformTags方法,代码如下:

detectPlatformTags: function (tags) {
var ua = navigator.userAgent,
isMobile = tags.isMobile = /Mobile(\/|\s)/.test(ua),
isPhone, isDesktop, isTablet, touchSupported, isIE10, isBlackberry,
element = document.createElement('div'),
uaTagChecks = [
'iPhone',
'iPod',
'Android',
'Silk',
'Android 2',
'BlackBerry',
'BB',
'iPad',
'RIM Tablet OS',
'MSIE 10',
'Trident',
'Chrome',
'Tizen',
'Firefox',
'Safari',
'Windows Phone'
],
isEventSupported = function(name, tag) {
if (tag === undefined) {
tag = window;
} var eventName = 'on' + name.toLowerCase(),
isSupported = (eventName in element); if (!isSupported) {
if (element.setAttribute && element.removeAttribute) {
element.setAttribute(eventName, '');
isSupported = typeof element[eventName] === 'function'; if (typeof element[eventName] !== 'undefined') {
element[eventName] = undefined;
} element.removeAttribute(eventName);
}
} return isSupported;
},
uaTags = {},
len = uaTagChecks.length, check, c; for (c = 0; c < len; c++) {
check = uaTagChecks[c];
uaTags[check] = new RegExp(check).test(ua);
} isPhone =
(uaTags.iPhone || uaTags.iPod) ||
(!uaTags.Silk && (uaTags.Android && (uaTags['Android 2'] || isMobile))) ||
((uaTags.BlackBerry || uaTags.BB) && uaTags.isMobile) ||
(uaTags['Windows Phone']); isTablet =
(!tags.isPhone) && (
uaTags.iPad ||
uaTags.Android ||
uaTags.Silk ||
uaTags['RIM Tablet OS'] ||
(uaTags['MSIE 10'] && /; Touch/.test(ua))
); touchSupported =
// if the browser has touch events we can be reasonably sure the device has
// a touch screen
isEventSupported('touchend') ||
// browsers that use pointer event have maxTouchPoints > 0 if the
// device supports touch input
// http://www.w3.org/TR/pointerevents/#widl-Navigator-maxTouchPoints
navigator.maxTouchPoints ||
// IE10 uses a vendor-prefixed maxTouchPoints property
navigator.msMaxTouchPoints; isDesktop = !isPhone && !isTablet;
isIE10 = uaTags['MSIE 10'];
isBlackberry = uaTags.Blackberry || uaTags.BB; apply(tags, Microloader.loadPlatformsParam(), {
phone: isPhone,
tablet: isTablet,
desktop: isDesktop,
touch: touchSupported,
ios: (uaTags.iPad || uaTags.iPhone || uaTags.iPod),
android: uaTags.Android || uaTags.Silk,
blackberry: isBlackberry,
safari: uaTags.Safari && isBlackberry,
chrome: uaTags.Chrome,
ie10: isIE10,
windows: isIE10 || uaTags.Trident,
tizen: uaTags.Tizen,
firefox: uaTags.Firefox
}); if (Ext.beforeLoad) {
tags = Ext.beforeLoad(tags);
} return tags;
},

好了,这次不用再跳了。代码先调用navigator.userAgent返回了浏览器用于 HTTP 请求的用户代理头的值,这个值可用来检查浏览器和版本号。如果值包含了字符串Mobile,说明是移动设备,这时候isMobile为true。在定义了一堆变量后,在页面中添加了一个div元素。接下来的uaTagChecks根据变量名可以知道,这是要检测的标记了。

接下来定义了isEventSupported函数,看名字就知道是用来检测是否支持事件的。根据函数内容,可以看到检测方式有两种,第一种就是检测事件名是否在刚才创建的元素div内,如果在,说明支持。第二种方法就是div元素上添加事件属性,然后判断元素对象内的事件属性是否为function,如果是,说明支持,否则就是不支持了。

定义结束后,就开始使用循环来检测平台属性了,检测结果将保存在uaTags对象中,对象中的属性名称就是uaTagChecks中的字符串,值就是检测值。

检测完之后就要给几个变量赋值了,赋值完成后,会调用apply方法将对象的成员复制到tags中。在调用apply方法时,还调用了loadPlatformsParam方法,该方法我就不列了,它的主要作用就是可通过访问地址的platformTags参数来自定义平台参数,这样做的目的是可以通过浏览器做一些模拟效果,如桌面pc模拟平板的效果。

下一句判断Ext.beforeLoad是否存在,在当前情况是不存在的,所以,这段代码可以忽略。最后是将平台检测结果返回了。

返回initPlatformTags方法,可以知道Microloader.platformTags现在指向的平台检测结果。再返回init方法,在计算出平台检测结果后,会将Ext.filterPlatform属性指向Microloader.filterPlatform方法,也就是说,在调用Ext的filterPlatform方法时,会执行Microloader.的filterPlatform方法,该方法的主要作用就是把不需要的平台过滤掉。

好了,现在返回initManifest方法,在执行完init方法后,会给tmpManifest赋值,由于在当前情况下,manifest为null,所以tmpManifes的值将会是Ext.manifest的值,而从图中可以知道,Ext.manifes的值是bootstrap,也就是说,现在tmpManifes的值是bootstrap。接下来判断tmpManifes是否为字符串,当前情况下,tmpManifes是字符串,所以要执行判断语句内的代码。先给url赋值,这个由Boot.baseUrl、tmpManifest和“.json”三部分构成,先不管Boot.baseUrl,可以知道,这里要找的是bootstrap.json文件。接下来会调用Boot.fetchSync方法,代码如下:

fetchSync: function(url) {
var exception, xhr, status, content; exception = false;
xhr = new XMLHttpRequest(); try {
xhr.open('GET', url, false);
xhr.send(null);
} catch (e) {
exception = true;
} status = (xhr.status === 1223) ? 204 :
(xhr.status === 0 && ((self.location || {}).protocol === 'file:' ||
(self.location || {}).protocol === 'ionp:')) ? 200 : xhr.status;
content = xhr.responseText; xhr = null; // Prevent potential IE memory leak return {
content: content,
exception: exception,
status: status
}; },

从代码中的new XMLHttpRequest这语句就知道,这段代码的主要作用就是使用Ajax去加载bootstrap.json文件了。现在假定能正确加载bootstrap.json文件并返回initManifest方法。

在initManifest方法内,接下来要做的是调用JSON.parse将返回的数据解析为JSON对象,并Ext.manifest属性指向该对象。最后将JSON对象返回laod方法。

在load方法的第二句,会先从返回的对象中取出loadOrder的值。在bootstrap.json文件中,loadOrder是一个由对象组成的数组,而每一个对象包含path、requires、uses和idx这4个成员。如果对于Ext JS有一定理解,那么要理解这4个成员不难。成员paths的值就是Ext JS类的脚本的路径,requires和uese指的是这个类所需要的类和使用到的类,而idx则是这个类的唯一标识。在requires和uese中就是使用这个唯一标识来指定所需或使用到的类文件的。

把这个loadOrder取出后,会调用Boot.createLoadOrderMap方法进行处理,代码如下:

createLoadOrderMap: function(loadOrder) {
var len = loadOrder.length,
loadOrderMap = {},
i, element; for(i = 0; i < len; i++) {
element = loadOrder[i];
loadOrderMap[element.path] = element;
} return loadOrderMap;
},

代码的作用只是把loadOrder数组转换为对象,对象的属性名称就是类文件的路径,值就是类对象本身。

返回到load方法,在处理完loadOrder数组后,会继续从bootstrap.json文件中把js和css的值取出来,然后合并到resources数组中。在当前项目中,bootstrap.json文件中的js和css的定义如下:

"js":[{"path":"app.js"}],
"css":[{"path":"bootstrap.css"}

这样对于理解后面的循环就容易多了,由于在定义中,没有platform这个成员,所以循环中的第一个判断就会被跳过,直接执行第二个判断了,也就是把路径信息推人urls数组中。

处理完这个,就开始调用Boot.load方法了,代码如下:

load: function (request) {
if (request.sync || _syncMode) {
return this.loadSync(request);
} // Allow a raw array of paths to be passed.
if (!request.url) {
request = {
url: request
};
} // If there is a request in progress, we must
// queue this new request to be fired when the current request completes.
if (_currentRequest) {
_suspendedQueue.push(request);
} else {
Boot.expandLoadOrder(request); var url = request.url,
urls = url.charAt ? [ url ] : url,
length = urls.length,
i; // Start the counter here. This is reduced as we notify this fellow of script
// loads.
request.urls = urls;
request.loaded = 0;
request.loading = length;
request.charset = request.charset || _config.charset;
request.buster = (('cache' in request) ? !request.cache : _config.disableCaching) &&
(_config.disableCachingParam + '=' + (+new Date())); _currentRequest = request;
request.sequential = false; for (i = 0; i < length; ++i) {
Boot.loadUrl(urls[i], request);
}
} return this;
},

在这段代码中,前面的代码都是与处理请求地址有关,而当这些都准备好了以后,就会调用Boot.loadUrl方法去加载文件了。而在Boot.loadUrl方法内,会调用Boot.create方法去创建加载标记,代码如下:

        create: function (url, key) {
var css = url && cssRe.test(url),
el = doc.createElement(css ? 'link' : 'script'),
prop; if (css) {
el.rel = 'stylesheet';
prop = 'href';
} else {
el.type = 'text/javascript';
if (!url) {
return el;
}
prop = 'src'; if(Boot.hasAsync) {
el.async = false;
}
} key = key || url;
return _items[key] = {
key: key,
url: url,
css: css,
done: false,
el: el,
prop: prop,
loaded: false,
evaluated: false
};
},

从代码doc.createElement这句就可以看到,在这里会创建SCRIPT或LINK标记去加载脚本或样式。

这么简单的东西搞得那么复杂的一个原因是要确保类的加载顺序,以确保不会出现类初始化时找不到依赖类的情况。因而,在整个加载过程中,需要监控每个脚本的加载情况,在依赖类没有加载完成之前,不去加载该类。

在bootstrap.json文件中,已经把app.js、bootstrap.css等文件加进去了,所以,在index.html文件中,只需要加载bootstrap.js文件就行了。

至此,我们已经基本了解了Ext JS 5的启动过程了。现在的问题是,我们怎么去加载本地化文件。

Ext JS 5初探(二) ——Bootstrap.js的更多相关文章

  1. bootstrap.js 文件使用指南

    介绍 使用 Bootstrap v3.3.7 时,需要引入三个脚本文件. https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.m ...

  2. 关于 ajax 动态返回数据 css 以及 js 失效问题(动态引入JS)

    ajax 毕竟是异步的 所以动态加载出来的数据 难免遇到 css 或者 js 失效的问题,所以要动态加载 css ji等文件了 1.公共方法 load //动态加载 js /css function ...

  3. 初探JavaScript(二)——JS如何动态操控HTML

    除去五一三天,我已经和<JavaScript Dom编程艺术>磨合了六天,第一印象很好.慢慢的,我发现这是一块排骨,除了肉还有骨头.遇到不解的地方就会多看几遍,实在不懂的先跳过,毕竟,初次 ...

  4. Ext JS 5初探(三)

    在上文提到了本地化文件的问题,然后在Ext JS 5的包里找了找,居然还没包含本地化包.我估计目前还不到考虑本地化的时候.在Sencha Touch中,是没有本地化包的,但是要让Ext JS也不包含本 ...

  5. Ext JS 5初探(一)

    在开始前,先安装好Sencha Cmd 5.然后输入以下命令创建一个测试用的应用程序: sencha -sdk c:\ext5 generate app TestExt5 C:\TestExt5 想不 ...

  6. JS组件系列——基于Bootstrap Ace模板的菜单和Tab页效果分享(你值得拥有)

    前言:最近园子里多了许多谈语言.谈环境.谈逼格(格局)的文章,看看笑笑过后,殊不知其实都是然并卵.提升自己的技术才是王道.之前博主分享过多篇bootstrap组件的文章,引起了很多园友的关注和支持,看 ...

  7. JS组件系列——基于Bootstrap Ace模板的菜单Tab页效果优化

    前言:之前发表过一篇  JS组件系列——基于Bootstrap Ace模板的菜单和Tab页效果分享(你值得拥有) ,收到很多园友的反馈,当然也包括很多诟病,因为上篇只是将功能实现了,很多细节都没有处理 ...

  8. 转战JS(1) 初探与变量类型、运算符、常用函数与转换

    转战JS(1)初探与变量类型.运算符.常用函数与转换 做为一名.NET后台开发人员,正考滤向Web前端开发转型,之前也写过一代前端代码,可是当再回头看JS,并有转向它的意愿的时候,突然发现:原来JS不 ...

  9. ExtJS 4.2 教程-02:bootstrap.js 工作方式

    转载自起飞网,原文地址:http://www.qeefee.com/extjs-course-2-bootstrap-js ExtJS 4.2 教程-01:Hello ExtJS ExtJS 4.2 ...

随机推荐

  1. Docker的名字空间

    名字空间是 Linux 内核一个强大的特性.每个容器都有自己单独的名字空间,运行在其中的应用都像是在独立的操作系统中运行一样.名字空间保证了容器之间彼此互不影响. pid 名字空间 不同用户的进程就是 ...

  2. OpenResty 操作cookies

    在or中简单的使用cookies 复杂的操作请使用 [lua_resty_cookies](https://github.com/cloudflare/lua-resty-cookie) 基本操作 获 ...

  3. (译)openURL 在 iOS10中已弃用

    翻译自:openURL Deprecated in iOS10 译者:Haley_Wong 苹果在iOS 2 推出了 openURL:方法 作为一种打开外部链接的方式.而与之相关的方法 canOpen ...

  4. GDAL库进度信息编写示例

    GDAL进度信息编写 GDAL库中的算法以及读写数据的时候一般都会提供两个与进度信息相关的参数,下面分别进行描述: GDALProgressFunc pfnProgress void * pProgr ...

  5. python模块:时间处理模块

    http://blog.csdn.net/pipisorry/article/details/53067168 常用python自带时间处理模块 python自带的时间处理模块参考[操作系统服务:ti ...

  6. springMVC源码分析--BeanNameUrlHandlerMapping(七)

    在上一篇博客springMVC源码分析--AbstractDetectingUrlHandlerMapping(五)中我们介绍了AbstractUrlHandlerMapping,其定义了一个抽象函数 ...

  7. 1.Cocos2dx 3.2中vector,ValueMap,Touch触摸时间的使用.iconv字符编解码

     Cocos2dx3.2以后使用Vector<T>代替了CCArray.案例如下: 头文件:T02Vector.h #ifndef __T02Vector_H__ #define __ ...

  8. hive日志位置(日志定位报错:Failed with exception Unable to move sourcehdfs://namenode/tmp/hive-pmp_bi/h)

    Hive中的日志分为两种 1. 系统日志,记录了hive的运行情况,错误状况. 2. Job 日志,记录了Hive 中job的执行的历史过程. 日志查看方法 1,在本地运行机器上 hive日志存储位置 ...

  9. Microsoft Dynamics CRM 2011 当您在 大型数据集上执行 RetrieveMultiple 查询很慢的解决方法

    症状 当您在 Microsoft Dynamics CRM 2011 年大型数据集上执行 RetrieveMultiple 查询时,您会比较慢. 原因 发生此问题是因为大型数据集缓存 Retrieve ...

  10. 在ubuntu上部署一个samba服务器

    今天公司装了一天新电脑,准备把它装成服务器,于是为了方便开发的使用,我在上面部署了一个samba,用来实现window和linux的联系: 具体步骤,我详细的查看了百度,高手云集,以下就是总结了网友的 ...