Zepto源代码分析一~核心方法

今天抽出时间复习了一下Zepto的源代码,依照自己的理解进行凝视。

欢迎大家拍砖。

源代码版本号:v1.1.4
源代码下载地址:http://zeptojs.com/

分析总体代码之后,整理出架构图:


本次仅仅针对获取核心方法$()进行拨离,并用demo測试api。
var Zepto = (function() {
    // 变量初始化
    var $;
    var zepto = {};
    var fragmentRE = /^s*<(w+|!)[^>]*>/;
    var singleTagRE = /^<(w+)s*/?>(?:</1>|)$/;
    var tagExpanderRE = /<(?

!area|br|col|embed|hr|img|input|link|meta|param)(([w:]+)[^>]*)/>/ig; var undefined; var emptyArray = []; var slice = emptyArray.slice; var cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1, 'opacity': 1, 'z-index': 1, 'zoom': 1 }; // 特殊属性集合 var methodAttributes = [ 'val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset' ]; var table = document.createElement('table'); var tableRow = document.createElement('tr'); var containers = { 'tr': document.createElement('tbody'), 'tbody': table, 'thead': table, 'tfoot': table, 'td': tableRow, 'th': tableRow, '*': document.createElement('div') }; var readyRE = /complete|loaded|interactive/; var simpleSelectorRE = /^[w-]*$/; var class2type = {}; var toString = class2type.toString; var isArray = Array.isArray || function(object) { return object instanceof Array; }; function type(obj) { return obj == null ?

String(obj) : class2type[toString.call(obj)] || "object"; } function isFunction(value) { return type(value) == "function"; } function isWindow(obj) { return obj != null && obj == obj.window; } function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE; } function isObject(obj) { return type(obj) == "object"; } function isPlainObject(obj) { return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) == Object.prototype; } function likeArray(obj) { return typeof obj.length == 'number'; } function dasherize(str) { return str.replace(/::/g, '/') .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') .replace(/([a-zd])([A-Z])/g, '$1_$2') .replace(/_/g, '-') .toLowerCase() } function maybeAddPx(name, value) { return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value; } /** * `$.zepto.fragment`须要一个html字符串和一个可选标记名来生成dom * 产生的dom返回一个数组形式 * 该功能能够被插件覆盖 * 没有覆盖全部浏览器 */ zepto.fragment = function(html, name, properties) { var dom; var nodes; var container; // 标签特殊化处理 if (singleTagRE.test(html)) { dom = $(document.createElement(RegExp.$1)); } if (!dom) { if (html.replace) { html = html.replace(tagExpanderRE, "<$1></$2>"); } if (name === undefined) { name = fragmentRE.test(html) && RegExp.$1; } if (!(name in containers)) { name = '*'; } container = containers[name]; container.innerHTML = '' + html; dom = $.each(slice.call(container.childNodes), function() { container.removeChild(this); }); } if (isPlainObject(properties)) { nodes = $(dom); $.each(properties, function(key, value) { if (methodAttributes.indexOf(key) > -1) { nodes[key](value); } else { nodes.attr(key, value); } }); } return dom; }; /** * `$.zepto.Z`将给定`dom`节点数组的原型赋上`$.fn`提供的全部Zepto函数 * 请注意,`__proto__`不支持IE浏览器 */ zepto.Z = function(dom, selector) { dom = dom || []; dom.__proto__ = $.fn; dom.selector = selector || ''; return dom; }; // `$.zepto.isZ`检查给定的对象是一个Zepto的集合,可被插件覆盖 zepto.isZ = function(object) { return object instanceof zepto.Z; }; /** * `$.zepto.init`是Zepto借鉴jQuery的`$.fn.init` * 採用css选择器和一个可选的上下文(处理各种特殊情况) * 该方法可被插件覆盖 */ zepto.init = function(selector, context) { // 假设没有给出,返回一个空的Zepto集合 if (!selector) { return zepto.Z(); // 检測字符串类型 } else if (typeof selector == 'string') { selector = selector.trim(); /** * 假设是一个HTML片段,创建节点注意,在chrome21和FF15版本号, * DOM错误12不是以<被抛出 */ if (selector[0] == '<' && fragmentRE.test(selector)) { dom = zepto.fragment(selector, RegExp.$1, context); selector = null; } // 假设存在一个上下文环境。建立收集,并从中选择节点 else if (context !== undefined) { return $(context).find(selector); } // 假设是一个css选择器,用它来选择节点 else { dom = zepto.qsa(document, selector); } } // 假设一个函数存在,在domready就绪后触发 else if (isFunction(selector)) { return $(document).ready(selector); } // 假设zepto已经收集给出,直接返回 else if (zepto.isZ(selector)) { return selector; } else { // 假设节点已经为数组。进行聚合 if (isArray(selector)) { dom = compact(selector); } // 包装DOM节点 else if (isObject(selector)) { dom = [selector]; selector = null; } // 假设是一个HTML片段。对该片段创建节点 else if (fragmentRE.test(selector)) { dom = zepto.fragment(selector.trim(), RegExp.$1, context); selector = null; } // 假设存在上下文环境,先建立收集,并从中选择节点 else if (context !== undefined) { return $(context).find(selector); } // 假设是一个css选择器。用它来选择节点 else { dom = zepto.qsa(document, selector); } } // 对发现的节点创建一个新的Zepto集合 return zepto.Z(dom, selector); }; // `$`作为Zepto的元对象。当调用`$`该函数将转由`$.zepto.init`处理 $ = function(selector, context) { return zepto.init(selector, context); }; /** * `$.zepto.qsa`是Zepto的css选择器。使用document.querySelectorAll及特殊情况处理 * 可被插件覆盖 */ zepto.qsa = function(element, selector) { var found; var maybeID = (selector[0] == '#'); var maybeClass = !maybeID && selector[0] == '.'; // 确认下标从1開始后的字符名 var nameOnly = maybeID || maybeClass ? selector.slice(1) : selector; var isSimple = simpleSelectorRE.test(nameOnly); return (isDocument(element) && isSimple && maybeID) ? ((found = element.getElementById(nameOnly)) ?

[found] : []) : (element.nodeType !== 1 && element.nodeType !== 9) ?

[] : slice.call((isSimple && !maybeID) ? maybeClass ? element.getElementsByClassName(nameOnly) : //class名称 element.getElementsByTagName(selector) : // tag名称 element.querySelectorAll(selector) // 查询全部匹配到的 ); }; function setAttribute(node, name, value) { value == null ?

node.removeAttribute(name) : node.setAttribute(name, value); } // 函数參数 function funcArg(context, arg, idx, payload) { return isFunction(arg) ? arg.call(context, idx, payload) : arg; } $.type = type; $.isFunction = isFunction; $.isWindow = isWindow; $.isArray = isArray; $.isPlainObject = isPlainObject; // Zepto对象迭代器 $.each = function(elements, callback) { var i; var key; if (likeArray(elements)) { for (i = 0; i < elements.length; i++) { if (callback.call(elements[i], i, elements[i]) === false) { return elements; } } } else { for (key in elements) { if (callback.call(elements[key], key, elements[key]) === false) { return elements; } } } return elements; }; // 配置类型映射 $.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { class2type["[object " + name + "]"] = name.toLowerCase(); }); /** * 定义的方法。适用于全部的Zepto对象 */ $.fn = { slice: function() { return $(slice.apply(this, arguments)); }, ready: function(callback) { // 检查document.body存在且文档渲染完毕 if (readyRE.test(document.readyState) && document.body) { callback($); } else { document.addEventListener('DOMContentLoaded', function() { callback($); }, false); } }, each: function(callback) { emptyArray.every.call(this, function(el, idx) { return callback.call(el, idx, el) !== false; }); return this; }, text: function(text) { return 0 in arguments ? this.each(function(idx) { var newText = funcArg(this, text, idx, this.textContent); this.textContent = (newText == null) ? '' : '' + newText; }) : (0 in this ? this[0].textContent : null); }, attr: function(name, value) { var result; return (typeof name == 'string' && !(1 in arguments)) ?

(!this.length || this[0].nodeType !== 1 ? undefined : (!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result ) : this.each(function(idx) { if (this.nodeType !== 1) { return; } if (isObject(name)) { for (key in name) { setAttribute(this, key, name[key]); } } else { setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name))); } }); }, // css属性设置 css: function(property, value) { if (arguments.length < 2) { var element = this[0]; var computedStyle = getComputedStyle(element, ''); if (!element) { return; } if (typeof property == 'string') { return element.style[camelize(property)] || computedStyle.getPropertyValue(property); } else if (isArray(property)) { var props = {}; $.each(isArray(property) ?

property : [property], function(_, prop) { props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop)); }); return props; } } var css = ''; if (type(property) == 'string') { if (!value && value !== 0) { this.each(function() { this.style.removeProperty(dasherize(property)); }); } else { css = dasherize(property) + ":" + maybeAddPx(property, value); } } else { for (key in property) { if (!property[key] && property[key] !== 0) { this.each(function() { this.style.removeProperty(dasherize(key)); }); } else { css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';'; } } } return this.each(function() { this.style.cssText += ';' + css; }); } }; // 继承 zepto.Z.prototype = $.fn; // 在`$.zepto`命名空间导出内部API函数 zepto.uniq = uniq; zepto.deserializeValue = deserializeValue; $.zepto = zepto; return $; })(); // 全局变量接口 window.Zepto = Zepto; window.$ === undefined && (window.$ = Zepto);

演示样例流程:
当我们运行一个$("#test"),详细过程例如以下:
1. 运行zepto.init
2. 获取这个元素,最后返回一个数组,然后运行 zepto.Z(dom, selector)
3. 将数组化后的节点列表的__proto__运行zepto.Z.prototype。注意到zepto.Z.prototype = $.fn,$.fn下的全部方法都挂在了zepto.Z的prototype下,也就是说$("#test")已经拥有了$.fn的全部方法
4. 返回数组

相应的demo页面代码:
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
        <meta charset="utf-8" />
        <title>Zepto源代码分析</title>
        <link rel="stylesheet" href="demo.css" type="text/css" />
    </head>
    <body>
        <div id="test">
            測试zepto源代码
            <span class="aa">22</span>
            <span class="aa">2332</span>
        </div>
        <div class="wrap">content</div>
        <script src="zepto-dev.js"></script>
        <script>
            console.log($('div'));
            console.log($('.aa'));
            console.log($('<div>这是測试内容</div>'));
            console.log($("<span />", { text: "測试測试111", id: "ceshi_111", css: { color: 'red' } }));
            Zepto(function($) {
                console.log('Ready to Zepto!');
            });
        </script>
    </body>
</html>

最后的效果截图例如以下:





原文地址:https://www.cnblogs.com/yutingliuyl/p/6724779.html