适合人群
本文适合0.5~3年的前端开发人员,以及想了解jQuery是什么的小伙伴们。
前言
谈谈个人对jQuery的看法,无兴趣可直接看源码分析。
如果你是一个五年以上的开发人员,相信你一定认识了解jQuery。这好比你十年前就已经有手机,那你肯定认识了解诺基亚。
当今的jQuery的确是没落了,逊色于三大框架,他的确落寞了。落寞到什么层度?
落寞到,你面试时你提起他,面试官会觉得你没有跟上时代;
落寞到,你写项目的用上jQuery,别人会觉得你的项目非常的low;
落寞到,掘金都懒得给他一个专题了(要是十年前肯定是个特殊的专区)。
其实不然,jQuery还是有很多可以学习且可以借鉴的地方,这也是笔者考虑再三,把他补上源码篇之一的原因。如果你是追求短期面试高薪,明显jQuery跟本文都不适合你。如果你注重基础,不凡了解一下本文。
如果你入门前端时,已经是三大框架的时代,那可以简单的理解为:jQuery是一个JavaScript函数库,让你的项目相比原生的js, “写的少,做的多”。是的,document.getelementbyid(‘id’).innerhtml = “你好” 用 $(‘#id’).html(“你好”) 即可实现,这对比我们原生js的来写项目,是带来多大的方便。他有很多优势,具体的优势,下边分析完源码会有汇总。但是不得不先提一下,他的缺陷是什么?淘汰的根本是什么?
笔者的观点是:虚拟dom的出现是淘汰jQuery的根本。jQuery的简写,还是用法是无脑操作dom。当多次需要渲染同一个dom时,你直接操作的dom(因为渲染了多次),他的效率就远远比不上”直接操作js,算好结果再同步dom”(因为只渲染了最终的一次)。虚拟dom的出现,让我们的前端,直接从mvc结构转换成mvvm结构,三大框架都是数据驱动,而jQuery依然是操作dom的写法,不得不给淘汰。这好比诺基亚想要在其他方面拯救自己,如果不抛弃塞班更换流行系统,将有心无力,最后给无情的抛弃。
手写mini_jquery
基本页面
我们新建一个html,写好jQuery的常见写法:
<html>
<head>
<title>zQuery源码</title>
<meta name="keywords" content="shopInfo.shopName?if_exists" />
<meta name="description" content="shopInfo.shopName?if_exists" />
<meta http-equiv="X-UA-Compatible" content="IE=8">
</meta>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
</head>
<body>
<script src="src/zQuery.js"></script>
</body>
</html>
再新建zQuery.js引入。
全局注册
用过jquery的小伙伴们,都知道我们的?全局哪里都可以使用。引入jQuery后,$是在全局上注册了。我们看看官方是如何处理:
缩略代码,即为上图。我们可以看到,引入jQuery后即调用了,实际上就是利用理解调用函数 (function(){})(),将jQuery暴露在全局中。
那么有没有人思考过?为什么截图中,会有一个module的判断呢?其实那是后续有了node环境,也有部分人需要在node用到jQuery,node是没有dom页面的,添加的判断。我们的案例暂时不需要考虑这些。我们手写一个注册:
(function (window) {
var zQuery = function () {
return new Object();
}
window.$ = zQuery;
})(window);
获取对象
即完成引入注册,全局注册。但是返回的对象到底是个啥?我们都知道$(“”)返回的是个dom对象,可以根据id获取,也可以根据标签名称,根据类名等等。
- 首先他要根据()里的值,获取dom对象,
- 支持id, class, 标签的获取。
我们根据两个要点,我们给他定义一个初始化方法,且新建时候立即调用,修改一下他的返回值:
(function (window) {
var zQuery = function () {
return new zQuery.fn.init(selector);
}
window.$ = zQuery;
zQuery.fn = {
init: function (selector) {
this.dom = [];
const childNodes = document.childNodes;
var rs = null;
if (typeof (selector) != 'undefined') {
if (selector.substr(0, 1) == "#") {//id选择器
rs = document.getElementById(selector.slice(1));
this.dom[0] = rs;
} else if (selector.substr(0, 1) == ".") { //样式选择器
rs = document.getElementsByClassName(selector.slice(1));
for (var i = 0; i < rs.length; i++) {
this.dom[i] = rs[i];
}
} else {//标签选择器
rs = document.getElementssByTagName();
for (var i = 0; i < rs.length; i++) {
this.dom[i] = rs[i];
}
}
}
return this;
},
}
})(window);
这样,即可完成$()的对象获取。
操作对象
再拿到我们的dom对象之后,接下来就是我们如何改变他们的问题。常见的是:
- html改变文本值
- css改变颜色
- hide/show隐藏或者显示
以上方法,用原生js实现的话,很简单吧?我们嵌入zQuery.fn中,来实现代码:
zQuery.fn = {
...,
html: function (value) {
if (this.dom.length == 1) {
this.dom[0].innerHTML = value;
} else if (this.length > 1) {
for (var i = 0; i < this.dom.length; i++) {
this.dom[i].style[attr] = value;
}
}
},
css: function (attr, value) {
if (this.dom.length == 1) {
this.dom[0].style[attr] = value;
} else if (this.dom.length > 1) {
for (var i = 0; i < this.dom.length; i++) {
this.dom[i].style[attr] = value;
}
}
},
show: function (attr, value) {
if (this.dom.length == 1) {
this.dom[0].style.display = 'block';
} else if (this.dom.length > 1) {
for (var i = 0; i < this.dom.length; i++) {
this.dom[i].style.display = 'block';
}
}
},
hide: function (attr, value) {
if (this.dom.length == 1) {
this.dom[0].style.display = 'none';
} else if (this.dom.length > 1) {
for (var i = 0; i < this.dom.length; i++) {
this.dom[i].style.display = 'none';
}
}
},
}
到此,即可完成我们的dom操作。案例如下:
html:
<div id="htmlId">点击我重新赋值</div>
<div id="cssId" style="color: black;">点击我变成红色</div>
<div id="showId">点击我变成隐藏</div>
<div id="changeId">点击我改变样式选择器</div>
<div class="cssSelector">样式选择器</div>
<div class="cssSelector">样式选择器</div>
<div class="cssSelector">样式选择器</div>
<div class="cssSelector">样式选择器</div>
js:
btnList.addEventListener('click', function (e) {
var id = e.target.id;
switch (id) {
case 'htmlId':
$('#htmlId').html("html赋值成功");
break;
case 'cssId':
$('#cssId').css("color", "red");
break;
case 'showId':
$("#showId").hide();
break;
case 'changeId':
$(".cssSelector").css("color", "#0099dd");
break;
}
}
});
完成链式
jquery的优势之一,就是方便支持链式。那么我们如何来实现他呢?
其实也很好理解,我们只需要将我们找到的dom对象,接下去再寻找下一层dom对象,直到找到结果为止。 我们把第一次的dom对象保存,第二次拿去dom对象,基于第一次的条件去下获取,然后重新初始化即可。 且这个过程,所有的层次关系,多级的dom,内置方法是一致的,我们需要完成原型链的继承。
三个步骤:
-
1.拿到上次的dom
-
2.基于上次的dom,获取下一次dom。
-
3.完成原型链继承
第一个步骤,我们可以新建一个全局的myDocument表示保存的dom结构。 zQuery.fn = { init: function (selector, myDocument) { this.dom = this.dom ? this.dom : []; this.myDocument = myDocument ? myDocument : document; const childNodes = this.myDocument.childNodes; var rs = null; if (typeof (selector) != 'undefined') { if (selector.substr(0, 1) == "#") {//id选择器 rs = this.myDocument.getElementById(selector.slice(1)); this.dom[0] = rs; console.log("rs===" + rs.innerText + rs.innerHTML); } else if (selector.substr(0, 1) == ".") { //样式选择器 rs = this.myDocument.getElementsByClassName(selector.slice(1)); for (var i = 0; i < rs.length; i++) { this.dom[i] = rs[i]; } } } return this; }, ... } 基于上次的dom,获取下一次dom。 zQuery.fn = { ... find: function (selector) { if (this.dom.length == 1) { this.init(selector, this.dom[0]); } else if (this.dom.length > 1) { for (var i = 0; i < this.dom.length; i++) { this.init(selector, this.dom[i]); } } return this; }, } //完成原型链继承 zQuery.fn.init.prototype = zQuery.fn;
实现案例测试:
html:
<div id="findId"> 点击我触发链式变化:<div class="f_div">我是子内容</div> </div>
js:
$("#findId").find('.f_div').css("color", "red");
网络请求
最后简单的实现jQuery的$.ajax神器。
ajax: function ({ url, dataType, success }) {
var xhr = new XMLHttpRequest();
xhr.open(dataType, url);
xhr.send(null);
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
success(xhr.responseText);
};
}
},
$().ajax({
url: 'http://....,
dataType: 'GET',
success: function (res) {
alert("接口返回数据:" + res);
}
})
这样一个简版的jQuery以及实现了。看完简版,我们来分析一下jq的优势吧。
jQuery的优势
jQuery虽然在dom的处理上,成为要害。但是他曾经的火热,说明身上还是很多优点的。我们分析一下:
- 简洁的api
无论上在选择器上,还是在网络请求上,jQuery都用了几个字母,就实现了我们的长篇代码。链式操作使页面更加简洁。
- dom操作的封装
jQuery封装了大量常用的DOM操作,使开发者在编写DOM操作相关程序的时候能够得心应手。jQuery轻松地完成各种原本非常复杂的操作,让JavaScript新手也能写出出色的程序。jQuery在操作dom上本身是有优势的,只是我们需要每次都去操作他,不像单页面架构,只更新自己需要更新的部分
- 小且不污
jQuery本身的引入非常小,最低差不多15K就解决,而且作者的封装,值暴露了$,不会污染全局的变量。
- 浏览器兼容性
单页的兼容,哪个支持ie10?没记错的话,官方都是声明支持IE11以上吧。
但是jQuery却不需要考虑这样的问题,能够支持IE 6.0+、FF 2+、Safari 2.0+和Opera 9.0+下
- 丰富的插件支持
在笔者的眼中,jQuery目前为止的生态库,是比其他框架要多,且引入即用。特别是一些门户需要的动画效果的,游戏的效果。他的生态圈比较历史渊源。
- 自定义插件
jQuery自定义插件,$.fn.extend即可扩展,十分方便。
框架建议
技能上允许时,不要一味的追求框架。三大框架固然是个不错的框架,但是jQuery还是有适合自己存在的地方,例如笔者写门户时,除了服务端渲染外,剩下的喜欢用简单的jQuery去实现。不单单时候门户,jQuery的劣势是什么?就是dom操作无法减少。但是我们本身的需求,就是不需要操作dom,或者少之又少,那就没有缺陷了。所以没有太多交互效果的网站中,依然还是可以使用jQuery。
框架源码
可到github下载完整实例:github.com/zhuangweizh…
(function (window) {
var zQuery = function (selector) {
return new zQuery.fn.init(selector);
}
zQuery.fn = {
init: function (selector, myDocument) {
this.dom = this.dom ? this.dom : [];
this.myDocument = myDocument ? myDocument : document;
const childNodes = this.myDocument.childNodes;
var rs = null;
if (typeof (selector) != 'undefined') {
if (selector.substr(0, 1) == "#") {//id选择器
rs = this.myDocument.getElementById(selector.slice(1));
this.dom[0] = rs;
console.log("rs===" + rs.innerText + rs.innerHTML);
} else if (selector.substr(0, 1) == ".") { //样式选择器
rs = this.myDocument.getElementsByClassName(selector.slice(1));
for (var i = 0; i < rs.length; i++) {
this.dom[i] = rs[i];
}
}
}
return this;
},
find: function (selector) {
if (this.dom.length == 1) {
this.init(selector, this.dom[0]);
} else if (this.dom.length > 1) {
for (var i = 0; i < this.dom.length; i++) {
this.init(selector, this.dom[i]);
}
}
return this;
},
html: function (value) {
if (this.dom.length == 1) {
this.dom[0].innerHTML = value;
} else if (this.length > 1) {
for (var i = 0; i < this.dom.length; i++) {
this.dom[i].style[attr] = value;
}
}
},
css: function (attr, value) {
if (this.dom.length == 1) {
this.dom[0].style[attr] = value;
} else if (this.dom.length > 1) {
for (var i = 0; i < this.dom.length; i++) {
this.dom[i].style[attr] = value;
}
}
},
show: function (attr, value) {
if (this.dom.length == 1) {
this.dom[0].style.display = 'block';
} else if (this.dom.length > 1) {
for (var i = 0; i < this.dom.length; i++) {
this.dom[i].style.display = 'block';
}
}
},
hide: function (attr, value) {
if (this.dom.length == 1) {
this.dom[0].style.display = 'none';
} else if (this.dom.length > 1) {
for (var i = 0; i < this.dom.length; i++) {
this.dom[i].style.display = 'none';
}
}
},
//异常请求
ajax: function ({ url, dataType, success }) {
var xhr = new XMLHttpRequest();
xhr.open(dataType, url);
xhr.send(null);
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
success(xhr.responseText);
};
}
},
}
zQuery.fn.init.prototype = zQuery.fn;
window.$ = zQuery;
})(window);
博客进度
| 序号 | 博客主题 | 相关链接 | |—–|——|————|- | 1 | 手写vue_mini源码解析 | juejin.cn/post/684790… | | 2 | 手写react_mini源码解析 | juejin.cn/post/685457… | | 3 | 手写webpack_mini源码解析 | juejin.cn/post/685457… | | 4 | 手写jquery_mini源码解析(即本文) | juejin.cn/post/685457… | | 5 | 手写vuex_mini源码解析 | juejin.cn/post/685705… | | 6 | 手写vue_router源码解析 | 预计8月 | | 7 | 手写diff算法源码解析 | 预计8月 | | 8 | 手写promise源码解析 | 预计8月 | | 9 | 手写原生js源码解析(手动实现常见api) | 预计8月 | | 10 | 手写react_redux,fiberd源码解析等 | 待定,本计划先出该文,整理有些难度 | | 11 | 手写koa2_mini | 预计9月,前端优先 |
期间除了除了源码篇,可能会写一两篇优化篇,基础篇等。有兴趣的欢迎持续关注。
下一篇将写vuex的实现。
今天的文章源码篇(四):手写jQuery版mini源码分析jQuery的优势。附送简版jQuery源码分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/19722.html