Ext JS 5初探(二) ——Bootstrap.js

来源:互联网 发布:专业直销软件开发 编辑:程序博客网 时间:2024/05/03 04:19

在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的启动过程了。现在的问题是,我们怎么去加载本地化文件。



4 0
原创粉丝点击