适合人群
本文适合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,他的效率就远远比不上"直接操作js,算好结果再同步dom"。虚拟dom的出现,让我们的前端,直接从mvc结构转换成mvvm结构,三大框架都是数据驱动,而jQuery依然是操作dom的写法,不得不被淘汰。这好比诺基亚想要在其他方面拯救自己,如果不抛弃塞班更换流行系统,将有心无力,最后被无情的抛弃。
手写mini_jquery
基本页面
我们新建一个html,写好jQuery的常见写法:
zQuery源码 复制代码
再新建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:
点击我重新赋值
点击我变成红色
点击我变成隐藏
点击我改变样式选择器
样式选择器
样式选择器
样式选择器
样式选择器
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:
点击我触发链式变化:
我是子内容
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. 点赞+评论(收藏)
2. 点头像关注,转发给有需要的朋友