整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

GitHub已完全弃用jQuery,问题是为什么?

GitHub已完全弃用jQuery,问题是为什么?

者 | GitHub 前端工程团队

译者 | 无明

编辑 | 覃云 - 前端之巅

最近,我们将 jQuery 完全从 GitHub.com 的前端代码中移除了,这标志着我们数年来逐步移除 jQuery 这个渐进式的过程终于结束了,这对我们来说是一件里程碑式的事件。这篇文章将介绍过去我们是如何依赖上 jQuery 的,随着时间地推移,我们意识到不再需要它,但到最后我们并没有使用另一个库或框架取代它,而是使用标准的浏览器 API 实现了我们所需要的一切。

早期,jQuery 对我们意义重大

GitHub.com 在 2007 年底开始使用 jQuery 1.2.1,那是谷歌发布 Chrome 浏览器的前一年。当时还没有通过 CSS 选择器来查询 DOM 元素的标准方法,也没有动态渲染元素的样式的标准方法,而 Internet Explorer 的 XMLHttpRequest 接口与其他很多 API 一样,在浏览器之间存在不一致性问题。

jQuery 让 DOM 操作、创建动画和“AJAX”请求变得相当简单,基本上,它让 Web 开发人员能够创建更加现代化的动态 Web 体验。最重要的是,使用 jQuery 为一个浏览器开发的代码也适用于其他浏览器。在 GitHub 的早期阶段,jQuery 让小型的开发团队能够快速进行原型设计并开发出新功能,而无需专门针对每个 Web 浏览器调整代码。

基于 jQuery 简单的接口所构建的扩展库也成为 GitHub.com 前端的基础构建块:pjax(https://github.com/defunkt/jquery-pjax)和 facebox(https://github.com/defunkt/facebox)。

我们将永远不会忘记 John Resig 和 jQuery 贡献者创建和维护的这样一个有用的基本库。

后来的 Web 标准

多年来,GitHub 成长为一家拥有数百名工程师的公司,并逐渐成立了一个专门的团队,负责 JavaScript 代码的规模和质量。我们一直在排除技术债务,有时技术债务会随着依赖项的增多而增长,这些依赖项在一开始会为我们带来一定的价值,但这些价值也随着时间的推移而下降。

我们可以将 jQuery 与现代浏览器支持的 Web 标准的快速演化进行比较:

  • $(selector) 模式可以使用 querySelectorAll() 来替换;
  • 现在可以使用 Element.classList 来实现 CSS 类名切换;
  • CSS 现在支持在样式表中而不是在 JavaScript 中定义可视动画;
  • 现在可以使用 Fetch Standard 执行 $.ajax 请求;
  • addEventListener() 接口已经足够稳定,可以跨平台使用;
  • 我们可以使用轻量级的库来封装事件委托模式;
  • 随着 JavaScript 语言的发展,jQuery 提供的一些语法糖已经变得多余。

另外,链式语法不能满足我们想要的编写代码的方式。例如:

$('.js-widget')
 .addClass('is-loading')
 .show()

这种语法写起来很简单,但是根据我们的标准,它并不能很好地传达我们的意图。作者是否期望在当前页面上有一个或多个 js-widget 元素?另外,如果我们更新页面标记并意外遗漏了 js-widget 类名,浏览器是否会抛出异常会告诉我们出了什么问题?默认情况下,当没有任何内容与选择器匹配时,jQuery 会跳过整个表达式,但对我们来说,这是一个 bug。

最后,我们开始使用 Flow 来注解类型,以便在构建时执行静态类型检查,并且我们发现,链式语法不适合做静态分析,因为几乎所有 jQuery 方法返回的结果都是相同的类型。我们当时之所以选择 Flow,是因为 @flow weak 模式等功能可以让我们逐步将类型应用于无类型的代码库上。

总而言之,移除 jQuery 意味着我们可以更多地依赖 Web 标准,让 MDN Web 文档成为前端开发人员事实上的默认文档,在将来可以维护更具弹性的代码,并且可以将 30KB 的依赖从我们的捆绑包中移除,加快页面的加载速度和 JavaScript 的执行速度。

逐步解耦

虽然定下了最终目标,但我们也知道,分配所有资源一次性移除 jQuery 是不可行的。这种匆匆忙忙的做法可能会导致网站功能出现回归。相反,我们采取了以下的策略:

1. 设定指标,跟踪整一行代码调用 jQuery 的比率,并监控指标走势随时间变化的情况,确保它保持不变或下降,而不是上升。


2. 我们不鼓励在任何新代码中导入 jQuery。为了方便自动化,我们创建了 eslint-plugin-jquery(https://github.com/dgraham/eslint-plugin-jquery),如果有人试图使用 jQuery 功能,例如 $.ajax,CI 检查将会失败。

3. 旧代码中存在大量违反 eslint 规则的情况,我们在代码注释中使用特定的 eslint-disable 规则进行了注解。看到这些代码的读者,他们都该知道,这些代码不符合我们当前的编码实践。

4. 我们创建了一个拉请求机器人,当有人试图添加新的 eslint-disable 规则时,会对拉取请求留下评论。这样我们就可以尽早参与代码评审,并提出替代方案。

5. 很多旧代码使用了 pjax 和 facebox 插件,所以我们在保持它们的接口几乎不变的同时,在内部使用 JS 重新实现它们的逻辑。静态类型检查有助于提升我们进行重构的信心。

6. 很多旧代码与 rails-behavior 发生交互,我们的 Ruby on Rails 适配器几乎是“不显眼的”JS,它们将 AJAX 生命周期处理器附加到某些表单上:

// 旧方法
 $(document).on('ajaxSuccess', 'form.js-widget', function(event, xhr, settings, data) {
 // 将响应数据插入到 DOM 中
 })

7. 我们选择触发假的 ajax* 生命周期事件,并保持这些表单像以前一样异步提交内容,而不是立即重写所有调用,只是会在内部使用 fetch()。

8. 我们自己维护了 jQuery 的一个版本,每当发现我们不再需要 jQuery 的某个模块的时候,就会将它从自定义版本中删除,并发布更轻量的版本。例如,在移除了 jQuery 的 CSS 伪选择器之后(如:visible 或:checkbox)我们就可以移除 Sizzle 模块了,当所有的 $.ajax 调用都被 fetch() 替换时,就可以移除 AJAX 模块。

这样做有两个目的:加快 JavaScript 执行速度,同时确保不会有新代码试图使用已移除的功能。

9. 我们根据网站的分析结果尽快放弃对旧版本 Internet Explorer 的支持。每当某个 IE 版本的使用率低于某个阈值时,我们就会停止向它提供 JavaScript 支持,并专注支持更现代的浏览器。尽早放弃对 IE 8 和 IE 9 的支持对于我们来说意味着可以采用很多原生的浏览器功能,否则的话有些功能很难通过 polyfill 来实现。

10. 作为 GitHub.com 前端功能开发新方法的一部分,我们专注于尽可能多地使用常规 HTML,并且逐步添加 JavaScript 行为作为渐进式增强。因此,那些使用 JS 增强的 Web 表单和其他 UI 元素通常也可以在禁用 JavaScript 的浏览器上正常运行。在某些情况下,我们可以完全删除某些遗留的代码,而不需要使用 JS 重写它们。

经过多年的努力,我们逐渐减少对 jQuery 的依赖,直到没有一行代码引用它为止。

自定义元素

近年来一直在炒作一项新技术,即自定义元素——浏览器原生的组件库,这意味着用户无需下载、解析和编译额外的字节。

从 2014 年开始,我们已经基于 v0 规范创建了一些自定义元素。然而,由于标准仍然在不断变化,我们并没有投入太多精力。直到 2017 年,Web Components v1 规范发布,并且 Chrome 和 Safari 实现了这一规范,我们才开始更广泛地采用自定义元素。

在移除 jQuery 期间,我们也在寻找用于提取自定义元素的模式。例如,我们将用于显示模态对话框的 facebox 转换为<details-dialog>元素(https://github.com/github/details-dialog-element)。

我们的渐进式增强理念也延伸到了自定义元素上。这意味着我们将尽可能多地保留标记内容,然后再标记上添加行为。例如,<local-time>默认显示原始时间戳,它被升级成可以将时间转换为本地时区,而对于<details-dialog>,当它被嵌在 <details>元素中时,可以在不使用 JavaScript 的情况下具备交互性,它被升级成具有辅助增强功能。

以下是实现<local-time>自定义元素的示例:

// local-time 根据用户的当前时区显示时间。
//
// 例如:
// <local-time datetime="2018-09-06T08:22:49Z">Sep 6, 2018</local-time>
//
class LocalTimeElement extends HTMLElement {
 static get observedAttributes() {
 return ['datetime']
 }
 attributeChangedCallback(attrName, oldValue, newValue) {
 if (attrName==='datetime') {
 const date=new Date(newValue)
 this.textContent=date.toLocaleString()
 }
 }
}
if (!window.customElements.get('local-time')) {
 window.LocalTimeElement=LocalTimeElement
 window.customElements.define('local-time', LocalTimeElement)
}

我们很期待 Web 组件的 Shadow DOM。Shadow DOM 的强大功能为 Web 带来了很多可能性,但也让 polyfill 变得更加困难。因为使用 polyfill 会导致性能损失,因此在生产环境中使用它们是不可行的。

英文原文

https://githubengineering.com/removing-jquery-from-github-frontend/

法: $.ajax([settings]);


  1. $.ajax({
  2. type: "POST",
  3. url: "UserLogincheck",
  4. data: {username:$("#username").val(), password:$("#password").val(),verify:$("#verify").val()},
  5. dataType: "json",
  6. success: function(data){
  7. if(data==1){
  8. // 用户名或密码错误
  9. alert("用户名或密码错误");
  10. }
  11. else if(data==2){
  12. alert("验证码错误");
  13. // 验证码错误
  14. }
  15. else if(data==0){
  16. window.location.href="index";
  17. }
  18. //跳转页面
  19. },
  20. error:function(XMLHttpRequest, textStatus, errorThrown){//请求失败时调用此函数
  21. console.log(XMLHttpRequest.status);
  22. console.log(XMLHttpRequest.readyState);
  23. console.log(textStatus);
  24. }
  25. });

php作为后台的处理过程


  1. public function UserLogincheck(){//用户登陆验证(用户名和邮箱均可登陆)
  2. $very=new \Think\Verify();
  3. if($very->check($_POST['verify'])){//验证码正确
  4. $user_admin=I('post.username');
  5. $judge=" (user_name='$user_admin' or user_email='$user_admin' )";
  6. $user_del_status=$this->user->where($judge)->getField('user_del');
  7. if($user_del_status==0){//用户未被注销
  8. $user_password=I('post.password');
  9. $judge .="and user_password='$user_password'";
  10. $res=$this->user->where($judge)->find();
  11. if($res){//记录用户登陆状态
  12. $_SESSION['ADMIN_user_id']=$this->user->where($judge)->getField('user_id');
  13. $_SESSION['ADMIN_user_name']=$data['user_name'];
  14. $this->ajaxReturn(0);//登陆成功
  15. }
  16. $this->ajaxReturn(1);//用户名或密码错误
  17. }
  18. else
  19. $this->ajaxReturn(8);//用户信息被注销
  20. }
  21. else
  22. $this->ajaxReturn(2);//验证码错误
  23. }

ajax参数详解:

data

类型:String

发送到服务器的数据。将自动转换为请求字符串格式。GET 请求中将附加在 URL 后。查看 processData 选项说明以禁止此自动转换。必须为 Key/Value 格式。如果为数组,jQuery 将自动为不同值对应同一个名称。如 {foo:["bar1", "bar2"]} 转换为 '&foo=bar1&foo=bar2'。

dataFilter

类型:Function

给 Ajax 返回的原始数据的进行预处理的函数。提供 data 和 type 两个参数:data 是 Ajax 返回的原始数据,type 是调用 jQuery.ajax 时提供的 dataType 参数。函数返回的值将由 jQuery 进一步处理。

dataType

类型:String

预期服务器返回的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断,比如 XML MIME 类型就被识别为 XML。在 1.4 中,JSON 就会生成一个 JavaScript 对象,而 script 则会执行这个脚本。随后服务器端返回的数据会根据这个值解析后,传递给回调函数。可用值:

"xml": 返回 XML 文档,可用 jQuery 处理。

"html": 返回纯文本 HTML 信息;包含的 script 标签会在插入 dom 时执行。

"script": 返回纯文本 JavaScript 代码。不会自动缓存结果。除非设置了 "cache" 参数。注意:在远程请求时(不在同一个域下),所有 POST 请求都将转为 GET 请求。(因为将使用 DOM 的 script标签来加载)

"json": 返回 JSON 数据 。

"jsonp": JSONP 格式。使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数。

"text": 返回纯文本字符串

error

类型:Function

默认值: 自动判断 (xml 或 html)。请求失败时调用此函数。

有以下三个参数:XMLHttpRequest 对象、错误信息、(可选)捕获的异常对象。

如果发生了错误,错误信息(第二个参数)除了得到 null 之外,还可能是 "timeout", "error", "notmodified" 和 "parsererror"。

这是一个 Ajax 事件。

global

类型:Boolean

是否触发全局 AJAX 事件。默认值: true。设置为 false 将不会触发全局 AJAX 事件,如 ajaxStart 或 ajaxStop 可用于控制不同的 Ajax 事件。

ifModified

类型:Boolean

仅在服务器数据改变时获取新数据。默认值: false。使用 HTTP 包 Last-Modified 头信息判断。在 jQuery 1.4 中,它也会检查服务器指定的 'etag' 来确定数据没有被修改过。

jsonp

类型:String

在一个 jsonp 请求中重写回调函数的名字。这个值用来替代在 "callback=?" 这种 GET 或 POST 请求中 URL 参数里的 "callback" 部分,比如 {jsonp:'onJsonPLoad'} 会导致将 "onJsonPLoad=?" 传给服务器。

jsonpCallback

类型:String

为 jsonp 请求指定一个回调函数名。这个值将用来取代 jQuery 自动生成的随机函数名。这主要用来让 jQuery 生成度独特的函数名,这样管理请求更容易,也能方便地提供回调函数和错误处理。你也可以在想让浏览器缓存 GET 请求的时候,指定这个回调函数名。

password

类型:String

用于响应 HTTP 访问认证请求的密码

processData

类型:Boolean

默认值: true。默认情况下,通过data选项传递进来的数据,如果是一个对象(技术上讲只要不是字符串),都会处理转化成一个查询字符串,以配合默认内容类型 "application/x-www-form-urlencoded"。如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false。

scriptCharset

类型:String

只有当请求时 dataType 为 "jsonp" 或 "script",并且 type 是 "GET" 才会用于强制修改 charset。通常只在本地和远程的内容编码不同时使用。

success

类型:Function

请求成功后的回调函数。

参数:由服务器返回,并根据 dataType 参数进行处理后的数据;描述状态的字符串。

这是一个 Ajax 事件。

traditional

类型:Boolean

如果你想要用传统的方式来序列化数据,那么就设置为 true。请参考工具分类下面的 jQuery.param 方法。

timeout

类型:Number

设置请求超时时间(毫秒)。此设置将覆盖全局设置。

type

类型:String

默认值: "GET")。请求方式 ("POST" 或 "GET"), 默认为 "GET"。注意:其它 HTTP 请求方法,如 PUT 和 DELETE 也可以使用,但仅部分浏览器支持。

url

类型:String

默认值: 当前页地址。发送请求的地址。

username

类型:String

用于响应 HTTP 访问认证请求的用户名。

xhr

类型:Function

需要返回一个 XMLHttpRequest 对象。默认在 IE 下是 ActiveXObject 而其他情况下是 XMLHttpRequest 。用于重写或者提供一个增强的 XMLHttpRequest 对象。这个参数在 jQuery 1.3 以前不可用。

Query介绍

jQuery是对JavaScript的封装,它是免费、开源的JavaScript函数库,jQuery 极大地简化了 JavaScript 编程

jQuery和JavaScript它们的作用一样,都是负责网页行为操作,增加网页和用户的交互效果的,只不过jQuery简化了JavaScript编程,jQuery实现交互效果更简单

jQuery的下载地址:

https://code.jquery.com

jQuery用法

jQuery引入

jQuery的入口函数:

我们知道使用js获取标签元素,需要页面加载完成以后再获取,我们通过使用onload事件来获取标签元素

而jQuery提供了ready函数来解决这个问题,保证获取标签元素没有问题,它的速度比原生的 window.onload 更快

jQuery选择器

jquery选择器就是快速选择标签元素,获取标签的,选择规则和css选择器一样

jQuery选择器的种类:

  1. 标签选择器是根据标签名来选择标签
  2. 类选择器是根据类名来选择标签
  3. id选择器是根据id来选择标签
  4. 层级选择器是根据层级关系来选择标签
  5. 属性选择器是根据属性名来选择标签

判断标签是否选择成功:

可以使用length属性来判断标签是否选择成功, 如果length大于0表示选择成功,否则选择失败

选择集过滤

选择集过滤就是在选择标签的集合里面过滤自己需要的标签

①has(选择器名称)方法,表示选取包含指定选择器的标签

②eq(索引)方法,表示选取指定索引的标签

选择集转移

选择集转移就是以选择的标签为参照,然后获取转移后的标签

$('#box').prev(); 表示选择id是box元素的上一个的同级元素

$('#box').prevAll(); 表示选择id是box元素的上面所有的同级元素

$('#box').next(); 表示选择id是box元素的下一个的同级元素

$('#box').nextAll(); 表示选择id是box元素的下面所有的同级元素

$('#box').parent(); 表示选择id是box元素的父元素

$('#box').children(); 表示选择id是box元素的所有子元素

$('#box').siblings(); 表示选择id是box元素的其它同级元素

$('#box').find('.myClass'); 表示选择id是box元素的class等于myClass的元素

获取和设置元素内容

获取和设置元素的内容使用: html方法

给指定元素追加html内容使用: append方法

获取和设置元素属性

获取和修改标签样式:

使用css方法可以给标签设置样式属性

获取和设置标签属性:

通过prop方法来完成

获取和设置元素的value属性:

通过val方法来完成,更加简单和方便

jQuery事件

常用事件:

click() 鼠标单击

blur() 元素失去焦点

focus() 元素获得焦点

mouseover() 鼠标进入(进入子元素也触发)

mouseout() 鼠标离开(离开子元素也触发)

ready() DOM加载完成

事件代理

事件代理:

事件代理就是使用父元素来代理子元素的事件,好处是减少事件的绑定次数,提高性能

事件代理使用场景:

使用场景当多个相同的子元素绑定同一个事件,可以使用事件代理

事件代理方法:

事件代理是使用delegate方法来完成

JavaScript对象

JavaScript 中的所有事物都是对象:字符串、数值、数组、函数等都可以认为是对象,此外,JavaScript 允许自定义对象,对象可以拥有属性和方法

创建自定义javascript对象有两种方式:

①通过顶级Object类型来实例化一个对象

②通过对象字面量创建一个对象

JSON

(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式

采用完全独立于编程语言的文本格式来存储和表示数据,简洁和清晰的层次结构使

得 JSON 成为理想的数据交换语言

JSON的优点:

易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率

JSON的格式:

①对象格式

②数组格式

对象格式:

对象格式的JSON数据,使用一对大括号({}),大括号里面放入key:value形式的键值对,多个键值对使用逗号分隔

数组格式:

数组格式的JSON数据,使用一对中括号([]),中括号里面的数据使用逗号分隔


JSON本质上是字符串,如果在js中操作JSON数据,可以将JSON字符串转化为JavaScript对象

ajax

一次完整的请求后,浏览器会接受到html,css,js,图片等大量的数据

ajax的介绍

ajax 是 Asynchronous JavaScript and XML的简写,ajax一个前后台配合的技术,它可以让 javascript 发送异步的 http 请求,与后台通信进行数据的获取,ajax 最大的优点是实现局部刷新,ajax可以发送http请求,当获取到后台数据的时候更新页面显示数据实现局部刷新,在这里大家只需要记住,当前端页面想和后台服务器进行数据交互就可以使用ajax了


ajax的实现

jquery将它封装成了一个方法$.ajax()

我们可以直接用这个方法来执行ajax请求


ajax方法的参数说明:

url 请求地址

type 请求方式,默认是'GET',常用的还有'POST'

dataType 设置返回的数据格式,常用的是'json'格式

data 设置发送给服务器的数据,没有参数不需要设置

success 设置请求成功后的回调函数

error 设置请求失败后的回调函数

async 设置是否异步,默认值是'true',表示异步,一般不用写


ajax的简写方式:

$.ajax按照请求方式可以简写成

$.get或者$.post方式


$.get和$.post方法的参数说明:

$.get(url,data,success(data, status, xhr),dataType).error(func) $.post(url,data,success(data, status, xhr),dataType).error(func)

url 请求地址

data 设置发送给服务器的数据,没有参数不需要设置

success 设置请求成功后的回调函数

data 请求的结果数据

status 请求的状态信息, 比如: "success"

xhr 底层发送http请求XMLHttpRequest对象

dataType 设置返回的数据格式

"xml"

"html"

"text"

"json"

error 表示错误异常处理

func 错误异常回调函数