Zepto 源码剖析 1 - 进入 Zepto

挑选 Zepto 的来由

Zepto is a minimalist JavaScript library for modern browsers with a largely jQuery-compatible API. If you use jQuery, you already know how to use Zepto.

Zepto 是一个用于当代浏览器的与 jQuery 大致兼容的 JavaScript 库。假如你运用过 jQuery 的话,你已晓得怎样运用 Zepto 了。

Zepto.js 官方网对其举行的形貌简而言之即为一个大致兼容 jQuery 的 JavaScript 库,因而是不是挑选剖析 Zepto 如许的库前置题目即为,关于此情此景下的前端入门者(2017 年的前端入行者,Like Me)是不是另有剖析 jQuery 源码的必要?

我心中的答案,就是有也没有,先说没有必要的一方面:

  • jQuery 包括大批针对“两套规范”的兼容完成,最显著的例子等于对 XHR 的处置惩罚体式格局上,因而关于类似的片断,寻觅解决方案已没有太大的意义:
var xhrSuccessStatus = {

        // File protocol always yields status code 0, assume 200
        0: 200,

        // Support: IE <=9 only
        // #1450: sometimes IE returns 1223 when it should be 204
        1223: 204
    },
  • jQuery 处置惩罚逻辑的基本对象基于 DOM,因而同时包括基本库 Sizzle.js 用以完成地道的跨平台挑选器完成,但由于 Document.querySelectorAll() 的兼容完成已掩盖一切当代浏览器,类似如许的推断也没有了太多的自创代价:
if ( support.qsa &&
    !nonnativeSelectorCache[ selector + " " ] &&
    (!rbuggyQSA || !rbuggyQSA.test( selector )) &&
    
    // Support: IE 8 only
    // Exclude object elements
    (nodeType !== 1 || context.nodeName.toLowerCase() !== "object") )

-再说有必要的一方面,依然拿 querySelectorAll 举例,Zepto 这一 jQuery 兼容库中将其封装为了以下的 qsa 函数:

  zepto.qsa = function(element, selector) {
  //\ ...
      return element.getElementById && isSimple && maybeID
      ? (found = element.getElementById(nameOnly))
      //\ ...
      : slice.call(
          isSimple && !maybeID && element.getElementsByClassName
            ? maybeClass
              ? element.getElementsByClassName(nameOnly)
              : element.getElementsByTagName(selector)
            : element.querySelectorAll(selector)
        );
  };

即在挪用该函数时,依据范例先做出推断,假如目标为可辨认的范例,那末采纳响应的要领举行挑选,终究降级到运用 querySelectorAll() 这一函数举行挑选。采纳该要领的目标即为了尽量的进步挑选器机能,参考 jsperf getElementById vs querySelector 的运转效果便可略知一二:

测试挪用Ops / SecOps 差异
document.getElementById(“foo”)27,869,670fastest
document.querySelector(“#foo”)11,206,84562% slower
document.querySelector(“[id=foo]”)11,281,57659% slower

另一个例子,关于 jQuery 对象实质的题目,以简化的 Zepto 为例,其具有以下多种的初始化体式格局:

$()
$(selector, [context])  ⇒ collection
$(<Zepto collection>)  ⇒ same collection
$(<DOM nodes>)  ⇒ collection
$(htmlString)  ⇒ collection
$(htmlString, attributes)  ⇒ collection v1.0+
Zepto(function($){ ... }) 

而其挪用天生的对象仅仅形似一个数组,但却怎样完成能够简朴的操纵 DOM 元素:

// 构造一个空的 Zepto(Z) 对象
> $()

[selector: ""]
-> selector: ""
-> length: 0
-> __proto__: Object

// 挑选并着色
> $("p:last-child").css('background-color', 'aliceblue')

答案便存在于 Zepto 的模块构造当中,$.fn 包括暴露在外的东西函数,当一个 Z 对象建立时将其设定为原型没,便可猎取个中的东西函数:

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

如许设想头脑与完成体式格局的例子在 jQuery/Zepto 中触目皆是。面临有照样没有必要浏览 jQuery 源码的题目,比起 jQuery 中写满浏览器峥嵘汗青的长篇巨著,Zepto 将其简化为了:

  • 主体部份 1000 行的代码范围
  • 高度的 jQuery API 兼容
  • 模块化的组合体式格局,默许编译体式格局只包括当代浏览器支撑

最大限制的降低了浏览本钱,因而该题目标答案,能够回复为去粗取精,经由历程剖析一种 jQuery 20% 代码量的最简中心完成(Zepto),明白其 80% 的设想头脑

环境搭建

Zepto 剖析环境的搭建仅须要一个通例页面原始代码

  • 一个通例页面:翻开 Zepto 的首页即可,在开发人员东西中即可运用 Zepto
  • 原始代码:本篇剖析的 Zepto 代码参照 Commit 3172e9,进入该代码分支中即可。

热身部份,先从 Zepto 的模块构造最先:

模块构造

Zepto 中心部份包括 zepto module 等五个默许编译入临盆代码的模块,而且给出了扩大插件的要领,那末第一个题目就是,Zepto 是怎样引入并供应模块构造的

  • 进入 package.json 发明 Zepto 项目供应了三个公用的命令行进口,供 coffee-script 运用,现实进口为 make
    "scripts": {
        "test": "coffee make test",
        "dist": "coffee make dist",
        "start": "coffee test/server.coffee"
    },
  • 进入 make 文件,直接找到它天生 dist 的历程:
//\ Line 33
target.dist = ->
  target.build()
  target.minify()
  target.compress()

target.build = ->
  cd __dirname
  mkdir '-p', 'dist'

  //\ 这里标清楚明了 5 个默许的编译模块,能够经由历程环境变量转变编译目标,且这些模块称号即为对应的文件名
  modules = (env['MODULES'] || 'zepto event ajax form ie').split(' ')
  module_files = ( "src/#{module}.js" for module in modules )
  
  //\ 声明许可证,放在 dist 头部,将目标源码文件中的解释删除,将 3 行以上的空行转换为两个空行写入
  intro = "/* Zepto #{describe_version()} - #{modules.join(' ')} - zeptojs.com/license */\n"
  dist = cat(module_files).replace(/^\/[\/*].*$/mg, '').replace(/\n{3,}/g, "\n\n")
  
  //\ 推断是不是将 AMD Layout 写入,假如是,则将上文代码填入 AMD 代码段中,报答体积
  dist = cat('src/amd_layout.js').replace(/YIELD/, -> dist.trim()) unless env['NOAMD']
  (intro + dist).to(zepto_js)
  report_size(zepto_js)
  • 几点分外的补充:

    • make 中的 #!/usr/bin/env coffee 是类 Unix 操纵系统中示意文本文件解析器的声明 Shebang),类似于 HTML 文档的 DOCTYPE,该文件写法可显著看出 Linux Shell 剧本的意味,不过采纳了 shelljs 这一运转在 Nodejs 上的库举行了跨平台。
    • make 中的 compress 历程顶用到了 gzip 举行紧缩: inp.pipe(gzip).pipe(out),该项紧缩关于用户是通明的,用户浏览器能够经由历程 Content-Encoding HTTP 字段获知该文件已被紧缩过并供应预解压操纵,详见 MDN – HTTP 协定中的数据紧缩
    • make 剧本中另有许多的自创的地方,比方 102 行 git 相干操纵,以及 108 行最先挪用 uglify.js 举行代码紧缩的历程等,不管用 gruntwebpack 构造流水线也只是类似的工序,供应多个出口。
  • 关于 src/amd_layout 这一没被列入模块列表中的文件,其作用即为兼容 AMD 规范
//\ src/amd_layout
//\ 作用于全局的 IIFE
(function(global, factory) {

  //\ AMD 兼容写法
  if (typeof define === 'function' && define.amd)
    define(function() { return factory(global) })
  else
    factory(global)
}(window, function(window) {

  //\ 假如包括 AMD 编译,就将上文代码完全写入该函数
  YIELD
  return Zepto
}))

此模块疏忽 AMD 相干逻辑会获得一个 JavaScript 库中常见的马上实行表达式(IIFE)构造,运用该构造的目标即为构造块级作用域,防备库中的变量对全局举行污染,是一种异常经常使用的包装要领,详见 MDN – IIFE

(function(global, factory) {
  //\ ...
}(
  //\ ...
))

进入 Zepto 主模块

剖析完 Zepto 模块构造,最先进入 Zepto 主模块,主模块框架以下:

//\ src/zepto.js

//\ 将 Zepto 定义为一个 IIFE
var Zepto = (function() {
  //\... Zepto 内部变量定义部份 6-47 行

  //\... Zepto 内部对象 zepto / 大众函数定义部份 48-167 行

  //\... zepto/Z/$ 关联构造
  zepto.Z =
  zepto.isZ =
  zepto.init =
  $ =
  extend =
  $.extend =
  
  //\... 与 DOM 的桥梁 - querySelectorAll 完成
  zepto.qsa =
  
  //\ $ 对象对外的东西函数 279-404 行
  
  //\ 定义 $.fn,经由历程原型链给予要领 
  $.fn = {
    constructor: zepto.Z,
    //\ ...
  }
  
  //\ 一些关于 $.fn 的其他函数完成 852-936 行
  
  //\ 继承处置惩罚 zepto/Z/$ 的关联,将 Z 的原型指向 $.fn
  zepto.Z.prototype = Z.prototype = $.fn

  zepto.uniq = uniq
  zepto.deserializeValue = deserializeValue
  
  //\ 将内置对象 zepto 挂载到 $ 对象
  $.zepto = zepto

  //\ 将 $ 作为 IIFE 返回值返回
  return $
})()

//\ 将 window.Zepto 指向 Zepto IIFE 的返回值 '$'
window.Zepto = Zepto

//\ 假如 '$' 还未被占用,就将其也初始为 Zepto IIFE 的返回值 '$'
window.$ === undefined && (window.$ = Zepto)

其中心设想头脑即体现在 zepto/Z/$ 这三个组件之间的关联上,处置惩罚他们的关联也恰是本篇的目标地点。

    原文作者:cyw0ng
    原文地址: https://segmentfault.com/a/1190000017055782
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞