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


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



  • 相关阅读:
    C#随机数字生成的一种方法
    SqlServer2012自增主键跳跃增长的问题解决方案
    Mysql5.7初始化成空密码或随机密码的方式
    StyleCop的常见错误
    数据库同步相关的SQL语句
    Linux Shell角本中的条件判断
    Linux(CentOS)中使用Mono+jexus部署Asp.net4.5网站
    Mina.Net实现的UDP多路广播
    Linux CentOS 6.6安装JDK1.7
    linux 常用命令
  • 原文地址:https://www.cnblogs.com/muyuge/p/6333675.html
Copyright © 2020-2023  润新知