前言
最近看了下 zepto 的源码,阅读目的:
- 让浮躁的心情归于平淡,远离焦虑
- 学习组织代码的方式、代码结构
- 巩固原型链知识
- 给自己正反馈,激励前行
实现
我没有全部实现 zepto 的所有功能,只是实现简单的、比较核心的部分
因为是学习使用,参考 zepto 源码自行实现 zue 实现 zepto $ 函数功能(简化部分细节,主要是加深对 zepto 结构的了解)
实现 $ ,根据传递 selector 返回对应实例(支持 html 标签、ID、Class 选择器,支持传递 HTML 片段)
先看下主体结构
// 自执行函数,返回 Zue 即 $ 函数
const Zue = (function(){
let $, Z, zue = {}
Z = function(dom, selector) {
}
zue.Z = function(dom, selector) {
return new Z(dom, selector)
}
// 根据 HTML 片段,创建 dom 数组
zue.fragment = function(html) {
}
// 在 element 下面更具选择器查找对应 dom 元素,返回 dom 数组
zue.qsa = function(element, selector)
zue.init = function(selector) {
let dom
// 通过正则表达式判断是调用 zue.fragmeng 或 zue.qsa 来生成 dom 数组;
return zue.Z(dom, selector)
}
// 入口函数,调用 zue.init() 返回 Z 对象实例
$ = function(selector) {
return zue.init()
}
// 通过类似方法,定义 $ 静态方法
$.map = function(elements, cb) {
// 具体实现
}
// 定义 Z 对象原型方法,所有 Z 实例共享
$.fn = {
// 定义各种方法例如 forEach、reduce 等等
}
// Z 原型指向 $.fn, 让 Z 实例共享 $.fn 上方法
zue.Z.prototype = Z.prototype = $.fn
return $;
})()
// 给 window.Zue 赋值,如果 window.$ 没有值,使用 $ 别名
window.Zue = Zue
window.$ === undefined && (window.$ = Zue)
以上就是 zepto 大概结构,解释一下代码
- $(selector) 返回的是 Z 对象实例
- Z.prototype 指向 .fn 定义了常见的方法这个在 zepto 文档上可以看到
- .isEmptyObject $.isNumeric ,还有常见帮助函数
- $ 是函数 Zepto 的别名
- zepto.init 主要用来识别 selector ,判断调用 zue.qsa 或 zue.fragment 来返回 dom 数组
- zue.qsa 和 zue.fragment 应该核心的实现部分,因为我是考虑简单的实现来理解大致结构,所以源码中简化了很多操作,实际的代码考虑很多,边界处理等等,感兴趣可以自己阅读
- Z 构造函数,用来将 dom 数组复制到实例上,同时设置 selector 和 length 值
下面给下具体的实现
/**
* $ 是函数,内部执行 zue.init 返回 Z 对象实例
* zue.init 根据传入参数 selector ,调用 zue.qsa(dom 查找) 或者 zepro.fragment() 返回 dom 对象数组
* 执行 zue.Z() 内容实用 new Z() 返回对象实例
* Z 构造函数,初始化 len、selector、将 dom 数组绑定在对象实例上
*/
// 自执行函数,返回 Zue 即 $ 函数
const Zue = (function () {
let $,
Z,
zue = {},
fragmentRE = /^\s*<(\w+|!)[^>]*>/
const isString = (str) =>
Object.prototype.toString.call(str) === "[object String]"
Z = function (dom, selector) {
this.length = dom.length || 0
dom.length &&
dom.forEach((item, index) => {
this[index] = item
})
this.selector = selector
}
zue.Z = function (dom, selector) {
return new Z(dom, selector)
}
// 更具 HTML 片段,创建 dom 数组
zue.fragment = function (html) {
let dom
// 创建 div 容器
let container = document.createElement("div")
// 将 HTML 片段赋值给 innerHTML
container.innerHTML = "" + html
// 遍历 container childNodes,返回对应 dom 数组
dom = Array.prototype.slice.call(container.childNodes).map((item) => {
return container.removeChild(item)
})
return dom
}
/**
*
* 只处理 class、id、html 标签选择器情况
* @param element
* @param selector
*/
zue.qsa = function (element, selector) {
return element.querySelectorAll(selector)
}
zue.init = function (selector, context) {
let dom = []
/**
* selector 情况判定
* 1. 为空
* 2. String
* */
// 1. 为空
if (!selector) return zue.Z()
// 2. String
else if (isString(selector)) {
// 去前后空格
selector = selector.trim()
// 判断是否为合法 html fragment
if (selector[0] === "<" && fragmentRE.test(selector)) {
// 根据 html fragment 创建 dom
dom = zue.fragment(selector)
selector = null
}
// 否则认为是选择器
else {
dom = zue.qsa(document, selector)
}
}
return zue.Z(dom, selector)
}
$ = function (selector, context) {
return zue.init(selector, context)
}
// 定义 $ 静态方法
$.map = function (elements, cb) {
let arryElements = Array.from(elements)
return arryElements.map(cb)
}
// 定义 Z 对象原型方法,所有 Z 实例共享
$.fn = {
constructor: zue.Z,
forEach: [].forEach,
reduce: [].reduce,
push: [].push,
sort: [].sort,
}
// Z 原型指向 $.fn, 让 Z 实例共享 $.fn 上方法
zue.Z.prototype = Z.prototype = $.fn
return $
})()
// 给 window.Zue 赋值,如果 window.$ 没有值,使用 $ 别名
window.Zue = Zue
window.$ === undefined && (window.$ = Zue)