1号店内部架构分析 1号店案例_1号店案例html源码


适合人群

本文适合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后,$是在全局上注册了。我们看看官方是如何处理:


1号店内部架构分析 1号店案例_1号店内部架构分析_02


缩略代码,即为上图。我们可以看到,引入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. 点头像关注,转发给有需要的朋友