`
hideto
  • 浏览: 2692585 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Ext源码解析:2, DomQuery.js

阅读更多
fromhttp://www.beyondrails.com/blogs/19/edit

Extjs Introduction中提到:
引用

DomQuery is 2~3 times faster than jQuery/dojo/Mootools, Prototype is the most slowest one!

Speed Test测试页面: http://extjs.com/playpen/slickspeed/

Ext的DomQuery为啥这么快呢?
一是因为DomQuery的byId/byTag/byClassName/byAttribute/byPseudo等基本查询方法实现的比较好
二是因为DomQuery良好的结构和模块设计
三是因为DomQuery有一个查询缓存

DomQuery的Dom查询器分四种类型:
1,Element Selector
2,Attribute Selector
3,Pseudo Class Selector
4,CSS Value Selector

Ext的查询方法是Ext.query(String selector, [Node root]) : Array
Ext.query = Ext.DomQuery.select;

select方法的实现:
select : function(path, root, type){
    if(!root || root == document){
        root = document;
    }
    if(typeof root == "string"){
        root = document.getElementById(root);
    }
    var paths = path.split(",");
    var results = [];
    for(var i = 0, len = paths.length; i < len; i++){
        var p = paths[i].replace(trimRe, "");
        if(!cache[p]){
            cache[p] = Ext.DomQuery.compile(p);
            if(!cache[p]){
                throw p + " is not a valid selector";
            }
        }
        var result = cache[p](root);
        if(result && result != document){
            results = results.concat(result);
        }
    }
    if(paths.length > 1){
        return nodup(results);
    }
    return results;
}

可以看到,Ext对于selector做了一个cache,缓存结果为Ext.DomQuery.compile方法返回的一个function
返回的function接收一个参数root来指示从那个Dom元素开始查询
compile : function(path, type){
    type = type || "select";

    var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
    var q = path, mode, lq;
    var tk = Ext.DomQuery.matchers;
    var tklen = tk.length;
    var mm;

    // accept leading mode switch
    var lmode = q.match(modeRe);
    if(lmode && lmode[1]){
        fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
        q = q.replace(lmode[1], "");
    }
    // strip leading slashes
    while(path.substr(0, 1)=="/"){
        path = path.substr(1);
    }

    while(q && lq != q){
        lq = q;
        var tm = q.match(tagTokenRe);
        if(type == "select"){
            if(tm){
                if(tm[1] == "#"){
                    fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
                }else{
                    fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
                }
                q = q.replace(tm[0], "");
            }else if(q.substr(0, 1) != '@'){
                fn[fn.length] = 'n = getNodes(n, mode, "*");';
            }
        }else{
            if(tm){
                if(tm[1] == "#"){
                    fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
                }else{
                    fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
                }
                q = q.replace(tm[0], "");
            }
        }
        while(!(mm = q.match(modeRe))){
            var matched = false;
            for(var j = 0; j < tklen; j++){
                var t = tk[j];
                var m = q.match(t.re);
                if(m){
                    fn[fn.length] = t.select.replace(tplRe, function(x, i){
                                            return m[i];
                                        });
                    q = q.replace(m[0], "");
                    matched = true;
                    break;
                }
            }
            // prevent infinite loop on bad selector
            if(!matched){
                throw 'Error parsing selector, parsing failed at "' + q + '"';
            }
        }
        if(mm[1]){
            fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
            q = q.replace(mm[1], "");
        }
    }
    fn[fn.length] = "return nodup(n);\n}";
    eval(fn.join(""));
    return f;
}

而compile方法会使用正则表达式匹配selector,然后分别去选择调用quickId/getNodes/byId/byTag/byClassName等特定查询模式的实现function
正则表达式匹配selector:
matchers : [{
        re: /^\.([\w-]+)/,
        select: 'n = byClassName(n, null, " {1} ");'
    }, {
        re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
        select: 'n = byPseudo(n, "{1}", "{2}");'
    },{
        re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
        select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
    }, {
        re: /^#([\w-]+)/,
        select: 'n = byId(n, null, "{1}");'
    },{
        re: /^@([\w-]+)/,
        select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
    }
]

看看quickId/byId的实现:
function quickId(ns, mode, root, id){
    if(ns == root){
       var d = root.ownerDocument || root;
       return d.getElementById(id);
    }
    ns = getNodes(ns, mode, "*");
    return byId(ns, null, id);
}
function byId(cs, attr, id){
    if(cs.tagName || cs == document){
        cs = [cs];
    }
    if(!id){
        return cs;
    }
    var r = [], ri = -1;
    for(var i = 0,ci; ci = cs[i]; i++){
        if(ci && ci.id == id){
            r[++ri] = ci;
            return r;
        }
    }
    return r;
};

如果是在默认的document下查找一个指定id的元素,则直接调用document.getElementById
否则用getNodes得到所有的子Dom元素,再用byId来匹配Id,第一个匹配上的返回

操作符匹配:
operators : {
    "=" : function(a, v){
        return a == v;
    },
    "!=" : function(a, v){
        return a != v;
    },
    "^=" : function(a, v){
        return a && a.substr(0, v.length) == v;
    },
    "$=" : function(a, v){
        return a && a.substr(a.length-v.length) == v;
    },
    "*=" : function(a, v){
        return a && a.indexOf(v) !== -1;
    },
    "%=" : function(a, v){
        return (a % v) == 0;
    },
    "|=" : function(a, v){
        return a && (a == v || a.substr(0, v.length+1) == v+'-');
    },
    "~=" : function(a, v){
        return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
    }
}

pseudo class匹配:
first-child
last-child
nth-child
only-child
empty
contains
nodeValue
checked
not
any
odd
even
nth
first
last
has
next
prev
分享到:
评论

相关推荐

    ExtJS 3.2的中文参考手册

    **EXT源码概述** - **源代码结构**: ExtJS的源代码组织清晰,易于理解和扩展。 - **发布细节**: 在发布源码时,会包含必要的文档和示例代码,方便开发者快速上手。 #### 9. **EXT程序规划入门** - **准备工作**:...

    EXT 中文手册内具实例代码

    ### EXT 中文手册知识点解析 #### 1. EXT 简介 - **EXT** 是一个基于 JavaScript 的开源框架,用于构建丰富的交互式 Web 应用程序。它提供了大量的 UI 组件、强大的数据处理能力以及与服务器端进行 Ajax 通信的支持...

    ext学习文档

    - **EXT源码概述**: 对EXT的源代码进行了初步解析。 - **EXT程序规划入门**: 提供了关于如何规划和构建EXT应用程序的基本指导。 #### 3. Element:Ext的核心 Element是EXT框架的核心组成部分之一,它封装了DOM操作...

    ExtJS 设计模式之一.docx

    ### ExtJS 单例设计模式解析 #### 一、引言 随着Web应用的发展,JavaScript框架不断进化,其中ExtJS以其强大的功能和丰富的组件库在前端开发领域占据了一席之地。ExtJS不仅提供了丰富的UI组件,还在其核心设计中...

    EXT 中文手册

    - **发布Ext源码时的一些细节**:EXT 在发布时会对源码进行压缩和优化,以减少文件大小,提高加载速度。 - **我应该从哪里开始**:初学者可以从研究核心组件和常用 API 开始,逐步深入理解框架的设计理念。 #### 八...

    asp.net ext 中文手册

    EXT源码概述 深入理解EXT的源码对于高级开发人员来说至关重要。源码分析可以帮助开发者更好地掌握框架的工作原理,从而更有效地进行调试和优化。源码概述章节通常会介绍EXT的架构设计、关键模块的功能及其实现方式...

    Ext帮助文档很详细的资源信息

    #### EXT源码概述 Ext的源码组织清晰,遵循模块化原则。源码中包含了丰富的注释,便于开发者理解和调试。通过阅读源码,可以深入了解Ext的工作原理和实现细节。 #### 适配器Adapters 适配器是Ext中的一个重要概念...

Global site tag (gtag.js) - Google Analytics