今天聊一点js的东西。
js和物联网领域的关系不是很大,为什么要关心js呢?其实关系也不小,它主要是负责直接和用户交互的那部分,也就是前端,前端基本都是离不开js的,甚至有些CS架构的程序也用js,比如nodejs
开发的客户端程序。
让我干唠是唠不出来什么的,因为咱没怎么用过js,只是最近在学习vue.js
,看到了一篇GitHub
官方在2018年发布的解释为什么放弃前端框架jQuery
的文章,心血来潮,想读一遍,顺便翻译出来,以帮助理解。然而自己翻译一是太费时间,二是水平不够也翻不好,所以就借助机器翻译,先粗翻,我在润色。
原文链接:https://github.blog/2018-09-06-removing-jquery-from-github-frontend/
以下为译文:
从 GitHub.com 网站前端移除 jQuery
我们最近完成了一项里程碑式的工作,去除了GitHub.com 网站前端代码对 jQuery 的依赖。这标志着一个长达数年的与 jQuery 逐渐剥离的过渡工作的结束,我们现在已经能够完全删除该库。在这篇文章中,我们将解释一下我们最初是如何依赖 jQuery 的,又是如何意识到何时不再需要它的,并指出——我们能够使用标准浏览器 API 实现我们需要的一切(而不是用另一个库或框架替换它)。
为什么 jQuery 在早期有意义
GitHub.com 网站在 2007 年底将 jQuery 1.2.1 作为依赖引入。回顾当时的时间节点,谷歌在一年后才发布了其 Chrome 浏览器第一个版本。那时候还没有通过 CSS 选择器查询 DOM 元素的标准方法,也没有实现动画效果的标准方法,并且 IE 开创的 XMLHttpRequest
接口与许多其他 API 一样,存在浏览器兼容问题。
jQuery 使操作 DOM、实现动画效果和发起“AJAX”请求变得简单。它使 Web 开发人员能够创建更现代、更动态的网站页面,所以脱颖而出。最重要的是,使用 jQuery 在一个浏览器中构建的 JavaScript 功能通常也可以在其他浏览器中使用,它对浏览器的兼容问题处理的比较到位。在 GitHub 的早期,它的大部分功能仍在不断完善,jQuery 这些特性允许小型开发团队快速原型化他们的产品并推出新功能,而无需专门针对每个 Web 浏览器调整代码。
jQuery 简洁的接口还使开发插件扩展变得很简单,构建 GitHub.com 网站前端的 pjax
和 facebox
就是两个基于 jQuery 开发的插件。
我们将永远铭记 John Resig 和 jQuery 贡献者创建和维护这样一个如此有用且在当时必不可少的库。
近些年的Web标准
这些年来,GitHub 成长为一家拥有数百名工程师的公司,并逐渐成立了一个专门的团队来负责把关 JavaScript 代码的规模和质量,这些代码会服务于 Web 浏览器。与此同时,我们也一直在寻找并排除技术债务,有时技术债务会随着依赖项的增多而增长,这些依赖项在一开始的时候给我们带来价值,但是随着时间推移和技术演变,价值会逐渐消失。
回到 jQuery 身上,我们将 jQuery API 与现代浏览器中支持的 Web API 进行了比较,并意识到一些问题:
$(selector)
完全可以使用querySelectorAll()
来替代;css
类名切换可以使用Element.classList
实现;css
现在支持使用样式表而不是JavaScript
定义动画;- 发起
ajax
请求可以使用Fetch Standard
实现; addEventListenner()
接口足够稳定,支持跨平台使用;- 我们可以使用轻量级库轻松封装事件委托模式;
- 随着JavaScript的发展,JQuery提供的语法糖显得越来越鸡肋。
此外,链接语法并不能满足我们未来想要编写代码的方式。例如:
$('.js-widget')
.addClass('is-loading')
.show()
这种语法写起来很简单,但按照我们的标准,并不能很好地传达意图。作者是否期望此页面上有一个或多个 js-widget 元素?另外,如果我们更新页面并不小心遗漏了 js-widget 类名,浏览器是否会报异常通知我们出了问题?默认情况下,当没有匹配到选择器时,jQuery 会默默地跳过整个表达式,对我们来说,这是 bug 而不是功能。
最后,我们开始使用 Flow 注解以在构建时执行静态类型检查,然后我们发现链接语法不适合静态分析,因为几乎每个 jQuery 方法调用返回的结果都是相同的类型。我们之所以选择 Flow 是因为当时@flow weak
弱类型等功能使我们能够逐步有效地开始将类型应用到没有类型的代码库中。( ps: 这一段完全看不懂了)
总而言之,与 jQuery 剥离意味着我们可以更多地依赖 Web 标准,让 MDN Web 文档成为我们前端开发人员事实上的默认文档,在未来维护更具弹性的代码,并最终从我们打包好的程序中移除30kb的依赖,从而加快页面加载速度和 JavaScript 执行速度。
逐步解耦
虽然有一个目标在望,但是我们很清楚,用所有资源重写代码来替换 jQuery 是不可行的。如果冒然行动,如此匆忙的努力可能会导致网站功能出现许多倒退,然后很快将不得不淘汰这些倒退的功能。相反,我们采取循序渐进的方式:
-
设置跟踪每行代码使用 jQuery 调用比率的指标,并监控该指标,以确保它保持不变或下降,而不是上升。
-
我们不鼓励在任何新代码中导入 jQuery。为了方便自动化管理,我们创建了
eslint-plugin-jquery
,如果有人尝试使用 jQuery 功能(例如$.ajax
),它将无法通过 CI 检查。 -
现在旧代码中有很多违反
eslint
规则的行为,所有这些我们都在代码注释中使用特定的eslint-disable
规则进行了注释。对于该代码的读者来说,这些注释将清楚地表明该代码并不代表我们当前的编码实践。 -
我们创建了一个拉取请求机器人,当有人试图添加新的
eslint-disable
规则时,它会在拉取请求上留下评论并通知我们的团队。这样我们就可以尽早参与代码审查并提出替代方案。 -
许多旧代码使用了
pjax
和facebox
这类 jQuery 插件,因此当我们使用vanilla JS
替换它们时保持了它们的接口不变。静态类型检查帮助我们对这些重构更有信心。 -
此处省略一段,实在看不懂,编都编不出来。。。
-
我们维护了一个自定义构建的 jQuery 版本,当我们发现不再使用某个 jQuery 模块时,我们会将其从自定义版本中删除并发布一个更精简的版本。例如,在我们删除了 jQuery 的
CSS
伪选择器(如:visible
或:checkbox
)之后,我们就能够删除Sizzle
模块;当最后一个$.ajax
调用被fetch()
替换时,我们能够删除AJAX
模块。这样做有双重目的:加快JavaScript
执行速度,同时确保新代码不能使用已删除的功能。 -
根据我们的网站分析结果,我们会在可行的情况下尽快放弃对旧版本
Internet Explorer
版本的支持。每当某个 IE 版本的使用率低于某个阈值时,我们就会停止为其提供 JavaScript支持,并专注于测试和支持更现代的浏览器。早期放弃对 IE 8-9 的支持使我们能够使用许多浏览器新的功能,否则这些功能很难被通过打补丁的方式使用。 -
作为我们在 GitHub.com 上构建前端功能的改进方法的一部分,我们专注于尽可能多地使用常规的
HTML
代码,只在有必要时才添加JavaScript
代码来作为一种渐进增强的方式。这样一来,那些使用 JS 增强的 Web 表单和其他 UI 元素通常也可以在浏览器禁用JavaScript
的情况下工作。在某些情况下,我们能够完全删除某些遗留代码,而不必在vanilla JS
中重写它们。
这几年来,通过以上所述和诸如此类的努力,我们逐渐减少了对 jQuery 的依赖,直到不再有一行代码引用它。
自定义标签
近年来引起轰动的一项技术是自定义标签,它是 Web Components
标准非常重要的一个特性,它使开发者能够将HTML页面的功能封装为 custom elements
(自定义标签),这意味着用户无需下载、解析和编译额外的库或者框架代码。
自 2014 年以来,我们根据 Web Components v0
规范创建了一些自定义标签。但是,由于当时的标准仍在不断变化,我们没有投入太多精力。直到 2017 年 Web Components v1
规范发布并在 Chrome
和 Safari
中实现,我们才开始更广泛地采用自定义标签。
在 jQuery 移除期间,我们寻找并提取出来那些适合通过自定义标签实现的对象。例如,我们将 facebox
中用来显示模态对话框的部分转换为 <details-dialog>
。
我们追求渐进式增强的理念也延伸到自定义标签上。这意味着我们在标签中尽可能的保留原内容,只在其基础之上做行为添加。例如,<local-time>
默认显示原始时间戳,被升级为将时间戳转换为本地时区的时间;当 <details-dialog>
嵌套在 <details>
中时,即使没有 JavaScript 也具有交互功能,但通过可用性增强得到了升级。
以下是如何实现 <local-time>
自定义标签的示例:
// The local-time element displays time in the user's current timezone
// and locale.
//
// Example:
// <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 解锁许多可能性,但这也使得为它打补丁变得更加困难。因为现在对它进行打补丁 会导致性能损失,即使对于操作与 Web 组件无关的 DOM 部分的代码也是如此,所以目前我们在生产环境中使用它是不可行的。
Polyfills (特指JavaScript中的补丁代码)
以下是帮助我们过渡到可以使用标准浏览器功能的补丁列表。我们仅在绝对必要时提供这些补丁,也就是说,它们只作为单独的 JavaScript 包提供给那些过时的浏览器使用以弥补兼容性的不足。
github/eventlistener-polyfill
github/fetch
github/form-data-entries
iamdustan/smoothscroll
javan/details-element-polyfill
jonathantneal/closest
kumarharsh/custom-event-polyfill
marvinhagemeister/request-idle-polyfill
mathiasbynens/Array.from
mathiasbynens/String.prototype.codePointAt
mathiasbynens/String.prototype.endsWith
mathiasbynens/String.prototype.startsWith
medikoo/es6-symbol
nicjansma/usertiming.js
rubennorte/es6-object-assign
stefanpenner/es6-promise
webcomponents/template
webcomponents/URL
webcomponents/webcomponentsjs
WebReflection/url-search-params
yola/classlist-polyfill
译文完!
结语:
本来就是件心血来潮的事,大周末的,寻思着两个小时也就弄完了,结果连看娃带吃饭,一天都没弄完,还是太天真。因为没能一鼓作气,难免半道泄气,期间多次强打精神总算翻译完了。
因为水平有限,再加上有些技术概念事先并不了解,有些地方还是比较难理解的,好在此篇并不着重讲具体技术,而是类似综述,于我本人而言,还是学到了一些方法论和“自以为新潮”的概念的,比如 Github 的工程师采用逐步解耦的方式慢慢与 jQuery 进行剥离的思想,放在其他软件开发领域也是有很强的指导性的,再比如新的 Web API
标准中支持自定义标签、Shadow DOM
这类之前都没有听过的概念等等。
至于译文中难免存在错误的或者纰漏,欢迎批评指正!
本来都打算发布了,结果在查一个概念的时候发现之前有人已经翻译过了,就取长补短了一下。