Phân tích mã nguồn Zepto: Mô-đun core

  • Mô-đun này xác định cấu trúc chuỗi nguyên mẫu của thư viện, tạo biến Zepto và đăng ký nó vào window với tên 'Zepto' và '$', sau đó bắt đầu triển khai các mô-đun khác.
  • Bên trong mô-đun, ngoài việc thực hiện chọn lọc và đối tượng zepto, còn có nhiều phương thức công cụ và phương thức nguyên mẫu được định nghĩa.
  • Đáng chú ý là, nhiều thực hiện bên trong sử dụng các phương thức mảng gốc, và nhiều API cũng được mở rộng dựa trên các phương thức nội bộ hoặc công khai.

Nội dung thực hiện

var Zepto = (function () {
    // 1. Định nghĩa biến cơ bản
    // 2. Thực hiện phương thức nội bộ
    // 3. Thực hiện đối tượng zepto - $('chọn lọc')
    // 4. Thực hiện phương thức $.extend
    // 5. Phương thức nội bộ zepto.qsa và 5 phương thức nội bộ khác
    // 6. Thực hiện phương thức toàn cục - $.phương_thức
    // 7. Thực hiện phương thức nguyên mẫu - $().phương_thức
    // 8. Thiết lập cấu trúc chuỗi nguyên mẫu
}();

  • Trên đây là nội dung thực hiện chính, thứ tự định nghĩa mã tương tự như trên, một số nơi sẽ xen kẽ định nghĩa.

Phân tích chi tiết

Chọn lọc

Logic thực hiện chọn lọc được đặt trong phương thức zepto.init, được gọi từ $() nội bộ, xử lý chọn lọc để tạo bộ dom, sau đó chuyển bộ này vào phương thức nội bộ tạo đối tượng zepto, logic chính như sau:

zepto.init = function(selector, context) {
    var dom
    if (!selector) return zepto.Z()
    else if (typeof selector == 'string') {
        selector = selector.trim()
        if (selector[0] == '<' && fragmentRE.test(selector))
            dom = zepto.fragment(selector, RegExp.$1, context), selector = null
        else if (context !== undefined) return $(context).find(selector)
        else dom = zepto.qsa(document, selector)
    }
    else if (isFunction(selector)) return $(document).ready(selector)
    else if (zepto.isZ(selector)) return selector
    else {
        if (isArray(selector)) dom = compact(selector)
        else if (isObject(selector))
            dom = [selector], selector = null
        else if (fragmentRE.test(selector))
            dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null
        else if (context !== undefined) return $(context).find(selector)
        else dom = zepto.qsa(document, selector)
    }
    return zepto.Z(dom, selector)
}

// Xử lý chọn lọc CSS
zepto.qsa = function(element, selector){
    var found,
        maybeID = selector[0] == '#',
        maybeClass = !maybeID && selector[0] == '.',
        nameOnly = maybeID || maybeClass ? selector.slice(1) : selector,
        isSimple = simpleSelectorRE.test(nameOnly)
    return (element.getElementById && isSimple && maybeID) ? 
        ( (found = element.getElementById(nameOnly)) ? [found] : [] ) :
        (element.nodeType !== 1 && element.nodeType !== 9 && element.nodeType !== 11) ? [] :
        slice.call(
            isSimple && !maybeID && element.getElementsByClassName ? 
            maybeClass ? element.getElementsByClassName(nameOnly) : 
            element.getElementsByTagName(selector) : 
            element.querySelectorAll(selector)
        )
}

// Xử lý chuỗi thẻ
zepto.fragment = function(html, name, properties) {
    var dom, nodes, 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
}

Đối tượng Zepto

Đối tượng Zepto được tạo ra thông qua một loạt các phương thức nội bộ liên quan, mã nguồn thực hiện như sau:

function Z(dom, selector) {
    var i, len = dom ? dom.length : 0
    for (i = 0; i < len; i++) this[i] = dom[i]
    this.length = len
    this.selector = selector || ''
}

zepto.Z = function(dom, selector) {
    return new Z(dom, selector)
}

zepto.isZ = function(object) {
    return object instanceof zepto.Z
}

zepto.init = function(selector, context) {
    return zepto.Z(dom, selector)
}

$ = function(selector, context){
    return zepto.init(selector, context)
}

$.fn = {}

zepto.Z.prototype = Z.prototype = $.fn

Một số phương thức API

  • Có quá nhiều phương thức công cụ và nguyên mẫu, chỉ chọn một số điển hình.
  • Thực hiện của extend và ready khá đơn giản.
  • Phương thức css chủ yếu dựa trên element.style và getComputedStyle, xem xét việc chuyển đổi tên thuộc tính và tự động thêm đơn vị kiểu.

$.extend

function extend(target, source, deep) {
    for (key in source)
    if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
        if (isPlainObject(source[key]) && !isPlainObject(target[key]))
            target[key] = {}
        if (isArray(source[key]) && !isArray(target[key]))
            target[key] = []
        extend(target[key], source[key], deep)
    }
    else if (source[key] !== undefined) target[key] = source[key]
}

$.extend = function(target){
    var deep, args = slice.call(arguments, 1)
    if (typeof target == 'boolean') {
        deep = target
        target = args.shift()
    }
    args.forEach(function(arg){ extend(target, arg, deep) })
    return target
}

$.fn.ready

$.fn = {
    ready: function(callback){
        if (readyRE.test(document.readyState) && document.body) callback($)
        else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false)
        return this
    }
}

$.fn.css

$.fn = {
    css: function(property, value){
        if (arguments.length < 2) {
          var element = this[0]
          if (typeof property == 'string') {
            if (!element) return
            return element.style[camelize(property)] || getComputedStyle(element, '').getPropertyValue(property)
          } else if (isArray(property)) {
            if (!element) return
            var props = {}
            var computedStyle = getComputedStyle(element, '')
            $.each(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 })
      }
}

$.fn.insert

$.fn = {
traverseNode: function(node, fun) {
fun(node)
for (var i = 0, len = node.childNodes.length; i < len; i++)
traverseNode(node.childNodes[i], fun)
}

adjacencyOperators.forEach(function(operator, operatorIndex) {
var inside = operatorIndex % 2

$.fn[operator] = function(){
var argType, nodes = $.map(arguments, function(arg) {
var arr = []
argType = type(arg)
if (argType == "array") {
arg.forEach(function(el) {
if (el.nodeType !== undefined) return arr.push(el)
else if ($.zepto.isZ(el)) return arr = arr.concat(el.get())
arr = arr.concat(zepto.fragment(el))
})
return arr
}
return argType == "object" || arg == null ?
arg : zepto.fragment(arg)
}),
parent, copyByClone = this.length > 1

if (nodes.length < 1) return this

return this.each(function(_, target){
parent = inside ? target : target.parentNode
target = operatorIndex == 0 ? target.nextSibling :
operatorIndex == 1 ? target.firstChild :
operatorIndex == 2 ? target :
null
var parentInDocument = $.contains(document.documentElement, parent)
nodes.forEach(function(node){
if (copyByClone) node = node.cloneNode(true)
else if (!parent) return $(node).remove()
parent.insertBefore(node, target)
if (parentInDocument) traverseNode(node, function(el){
if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' &&
(!el.type || el.type === 'text/javascript') && !el.src){
var target = el.ownerDocument ? el.ownerDocument.defaultView : window
target['eval'].call(target, el.innerHTML)
}
})
})
})
}

$.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function(html){
$(html)[operator](this)
return this
}
})
}

Thẻ: zepto JavaScript DOM css selector

Đăng vào ngày 13 tháng 7 lúc 12:02