负责任的JavaScript开发第1部分

这些数字告诉我们,JavaScript代码的增长对Web项目的性能有不利影响。 如果这种情况继续下去,那么很快,在加载中间页面时,将至少转移400 Kb的JS代码。 而这仅仅是传输的数据量。 像其他文本资源一样,JavaScript代码几乎总是以压缩形式传输。 从服务器向客户端传输代码时,压缩通常可能是唯一正确完成的事情。

图片

不幸的是,虽然减少某些资源的传输时间对我们所谓的“性能”有很大贡献,但压缩并不会影响浏览器在完全加载脚本之后解析和处理脚本所花费的时间。 。

如果服务器将400​​ KB的压缩JS代码发送给客户端,则浏览器在解压缩接收到的数据后需要处理的实际代码量将位于兆字节区域。 不同设备处理此类工作的能力取决于设备本身。 关于这一点的文章很多 ,但是我们只能确定地说,解析时间所需要的时间甚至在不同设备之间也有很大差异。

例如,看一下我的这个简单项目。 在页面加载期间,大约23 Kb的未压缩JS代码被传输到客户端。 在2017年中发布的MacBook Pro上运行的Chrome浏览器在大约25毫秒内处理了相当少量的代码。 但是,在诺基亚2 Android智能手机上,类似的数字扩展为190毫秒。 这并不是说它很小,而是在任何情况下页面都足够迅速地变得交互式。

现在,一个重要的问题。 您如何看待,简单的诺基亚2智能手机如何管理平均的现代网页? 实际上-太糟糕了。 浏览网页,即使是在快速的Internet连接上,也迫使用户保持耐心,因为只有在等待时间很长的情况下,才有可能处理加载有JavaScript代码的页面。


查看包含大量JS代码的页面时,诺基亚2的性能概述,该代码的处理会阻塞主线程

尽管近年来用于浏览网页的设备和用于传输数据的网络有了显着改进,但研究表明,所有这些改进都被页面中包含的大量JS代码“吞噬”了。 我们需要负责任地使用JavaScript。 责任始于对我们正在创造的东西以及我们如何做的理解。

“站点”和“应用程序”的意识形态比较


尽管每个人在直觉上都清楚其含义,但奇怪的事情是用不准确的术语发生的,我们用这些术语来命名事物。 有时我们会重载“ bee”一词的含义,称其为“ bees”和“ waspes”,即使蜜蜂和黄蜂之间的区别非常明显。 考虑到这种差异,可能导致“蜜蜂”和“黄蜂”的行为有所不同。 例如,我们将要摧毁大黄蜂的巢,但是如果我们谈论的是蜜蜂,昆虫的实用性和脆弱性要大得多,那么位于错误地方的巢可能会被决定不破坏,而是将其转移到某个地方。

在使用术语“网站”和“ Web应用程序”的方式中,可以观察到类似的自由。 这些概念之间的差异远不如真正的黄蜂和蜜蜂之间的差异明显,但如果将这些概念结合在一起,则可能导致非常不愉快的后果。 当我们允许自己做某事时,麻烦就开始了,这取决于项目是“仅仅是网站”还是“功能完善的Web应用程序”。 如果要为公司创建一个信息站点,则很可能您将不会依赖功能强大的框架来管理DOM更改或在客户端上实现路由。 我至少希望如此。 使用不适合解决某些问题的工具不仅会伤害使用该网站的人员,而且还可能严重影响开发过程。

开发Web应用程序时,一切看起来都不同。 我们安装了软件包,并附带了数百个(即使不是数千个)项目依赖项。 而且,我们甚至不确定其中一些的安全性 。 我们为捆绑器编写复杂的配置。 在如此无所不在的疯狂开发环境中工作时,您需要知识和注意力,以确保所收集的内容很快,并且该项目将在应该工作的地方运行。 如有疑问,请在项目的根目录中运行npm ls --prod命令 ,并查看是否可以命名使用此命令显示的所有内容的目的。 即使可以做到这一点,也不适用于第三方脚本。 我确定您的项目中也会使用几个这样的脚本。

我们忘记了网站和Web应用程序都占据着相同的“生态位”。 两者都受环境的影响,环境由各种网络和设备组成。 如果我们决定将我们正在开发的应用程序称为“应用程序”,则此类限制将不会消失;如果将“网站”称为“应用程序”,则用户的设备将不会神奇地变得更快。

找到谁使用我们创建的内容是我们的责任,我们必须考虑到以下事实:不同用户连接到Internet的条件可能与我们所依赖的条件不同。 在创建事物时,我们必须了解创建目标的目的,然后,我们应该开发有助于实现该目标的内容-即使开发并不是一件令人兴奋的任务

这意味着需要重新评估我们对JavaScript的依赖性,以及对JavaScript的使用,尤其是对HTML和CSS的损害,如何导致我们使用非理性模式,从而损害Web项目的性能和可访问性。

不要让框架强加非理性的模式


当我与依赖框架的团队合作以帮助他们提高生产力时,我目睹了代码库中奇怪事物的发现。 其中许多发现有一个共同点,那就是它们的书写方式通常会导致站点的可用性和性能出现问题。 例如,考虑以下React组件:

import React, { Component } from "react"; import { validateEmail } from "helpers/validation"; class SignupForm extends Component {  constructor (props) {    this.handleSubmit = this.handleSubmit.bind(this);    this.updateEmail = this.updateEmail.bind(this);    this.state.email = "";  }  updateEmail (event) {    this.setState({      email: event.target.value    });  }  handleSubmit () {    //      -      if (validateEmail(this.state.email)) {      // ...    }  }  render () {    return (      <div>        <span class="email-label">Enter your email:</span>        <input type="text" id="email" onChange={this.updateEmail} />        <button onClick={this.handleSubmit}>Sign Up</button>      </div>    );  } } 

在这里,您可以找到有关项目可访问性的几个显着问题:

  1. 不使用<form>元素的表单不再是表单。 实际上,您可以通过简单地指定父<div>元素的role =“ form”来解决此问题,但是,如果您创建一个表单,并且我们看到的绝对看起来像是表单,请使用<form>元素,并进行相应设置属性actionmethodaction属性在这里起着至关重要的作用,因为即使JavaScript不可用(自然而然,如果组件是在服务器上呈现的),它也允许表单至少执行某些操作。
  2. <span>不能替代<label> ,它提供了一些与<span>没有的项目的可用性有关的功能。
  3. 没有type="submit"属性的<button>元素只是一个按钮,单击该按钮将调用绑定到该按钮的事件处理程序。 如果我们想在提交表单之前对数据做一些事情,我们需要为按钮分配type="submit"属性,并将代码从onClick事件处理程序移至onSubmit表单事件处理程序。
  4. 顺便说一句,为什么要使用JavaScript来验证电子邮件地址,而HTML5却可以使用我们的控件来支持在几乎所有浏览器中(最多IE10)检查输入数据? 在这里,我们看到了一个机会,无法利用浏览器中已经存在的功能并应用适当的元素类型以及所需的属性。 但是,使用这种结构时,请记住,设置它们与屏幕阅读器的正常交互将需要一些努力

考虑到上述情况,我们重构了组件代码:

 import React, { Component } from "react"; class SignupForm extends Component { constructor (props) {   this.handleSubmit = this.handleSubmit.bind(this); } handleSubmit (event) {   //    ,         XHR   // (      ,     ,     JS).   event.preventDefault();   //  … } render () {   return (     <form method="POST" action="/signup" onSubmit={this.handleSubmit}>       <label for="email" class="email-label">Enter your email:</label>       <input type="email" id="email" required />       <button type="submit">Sign Up</button>     </form>   ); } } 

现在,该组件呈现的事实不仅变得更易于访问,而且使用更少的JS代码实现与以前相同的功能。 在一个被JavaScript淹没的世界中,删除几行代码应被视为积极的事情。 浏览器给我们带来了很多机会 ,我们需要努力尽可能多地利用这些机会。

我不想在这里说页面的可访问性问题仅在使用某些框架时出现。 我的意思是,由于过于依赖JavaScript,因此开发人员将完全错过许多重要的HTML和CSS功能。 这些知识上的差距通常会导致错误,而且,我们甚至都不怀疑这些错误。 框架是提高开发人员生产率的有用工具,但是,不断研究基本Web技术的功能对于创建方便,可用的产品极为重要,而与开发中使用的辅助工具无关。

依靠网络平台的强大功能,为您的项目带来光明的未来


由于我们在谈论框架,因此应注意,Web平台本身也是一个巨大的框架。 如上一部分所示,如果我们可以依靠已建立的模式来使用标记和浏览器功能,那么我们将处于一个更好的位置。 这些标准功能的替代方法是再次发明它们。 不用说,这样的“发明”充满了巨大的困难。 但是,如果我们安装的所有JavaScript软件包的作者都能以自己的方式解决此类问题,该怎么办?

Page单页应用


开发人员可以轻松负担的缺点之一是使用单页应用程序(SPA)模型,即使在不适合使用该模型的项目中也是如此。 当然,这样的项目受益于以下事实:由于客户端执行的路由,用户认为它们更具生产力。 但是使用SPA模型的缺点是什么? 浏览器的内置页面导航功能尽管建立在同步模型上,但却为项目带来了很多优势。 其中之一是,通过执行复杂的规范来进行访问历史的管理。 没有JavaScript的用户, 无论是否自己禁用它 ,都不会失去使用该项目的能力。 为了在关闭JavaScript的浏览器中提供一页应用程序,突然发现需要特别注意服务器渲染。


比较在慢速通信通道上加载实验应用程序的不同选项。 左侧的应用程序呈现完全取决于JavaScript。 右侧的应用程序在服务器上呈现,但随后在客户端上使用hydrate()方法将组件连接到服务器上已创建的标记

在这里,您可以看到在客户端上呈现的应用程序显示了几秒钟的空白屏幕,然后显示了完成的界面。

在服务器上呈现并在客户端上可操作的应用程序可以快速显示该界面的主要元素,但是您可以在与完全在客户端上呈现的应用程序相同的时间之后使用它。

如果位于客户端的路由器无法通知用户正在查看的页面上发生了什么更改,则应用程序的可用性也会受到影响。 这可能迫使用户依靠辅助技术来找出页面上到底发生了什么变化,结果,用户在网站上的工作要复杂得多。

此外,您可以立即遇到我们的老对手-系统负担过重。 一些客户端路由器非常小。 但是,如果您在React上创建一个项目,使用兼容的路由器 ,并可能使用一个来管理应用程序的状态,则意味着您必须接受它将包含一定数量的服务代码,而您无法从中获得任何东西。 即,在这种情况下,该代码约为135 Kb。 仔细分析正在创建的项目,以及客户端路由是否值得增加系统负载。 通常,最好拒绝客户端路由系统。

如果您担心用户的感觉,如果您希望网站对他来说很快,那么您可以依赖link属性rel = prefetch ,该属性使您可以组织来自同一来源的文档的预加载。 用户认为,使用此属性对改善项目的性能具有巨大影响,因为单击这些链接时,链接到使用此属性的页面会立即从缓存中加载。 此外,由于预加载数据的优先级较低,因此不太可能与重要资源争夺带宽。


当您访问网站的主页时,将预加载通过编写/引用的HTML代码。 当用户单击相应的链接时,HTML代码立即从浏览器缓存中加载

预加载页面可能会引起的主要问题是,您需要意识到,这种加载可能会浪费时间和资源。 要解决此问题,例如,您可以使用Google的小型Quicklink脚本来缓解此问题。 它检查当前客户端是否正在使用慢速连接,是否启用了数据保存模式 ,并且默认情况下允许您避免从页面源以外的其他源预加载资料。

为了使该网站在多次访问的用户眼中快速浏览,可以使用服务人员 。 假定您熟悉服务工作者的某些功能,则无论项目是否使用客户端路由系统,都可以使用它们。 通过服务人员执行路由缓存 ,我们获得了许多与早期下载某些链接相同的优点,但是我们拥有处理请求和答案的更多可能性。 无论您是否认为网站是“应用程序”,为网站配备服务工作者都可能是当今最关键的JavaScript用例之一。

▍JavaScript不是为布局而设计的


如果我们安装了旨在解决与页面布局有关的问题的JS软件包,那么是时候让我们非常小心并问自己要使用此软件包实现什么了。 CSS是专门为构建页面布局而创建的,为了有效地使用它,您不需要任何抽象。 现在,他们尝试使用JavaScript构建布局的大多数任务(例如放置元素,对齐元素,调整其大小(如操纵文本),甚至使用JavaScript完全创建布局)都可以使用CSS来完成。 浏览器很好地支持了诸如Flexbox和Grid之类的用于创建布局的现代工具,因此我们不需要基于用于处理布局的框架来开发项目。 顺便说一下,CSS也是一个框架。 事实证明,当我们拥有财产请求的机会时,逐步改善布局以支持新的形成方式并不困难

 /*   , ,   ,        CSS Grid. */ /*  @supports  ,     CSS Grid,     . */ @supports (display: grid) { /*      */ @media (min-width: 40em) {   /*       CSS Grid */ } } 

使用JavaScript解决创建页面布局和自定义页面外观的问题并不是新闻。 这就是我们在2009年所做的事情,当时我们生活在一种自欺欺人的氛围中,当时他说每个网站在IE6以及当时更高级的浏览器中都应该看起来像。 如果今天是2019年,我们将继续开发网站,使它们在所有浏览器中的外观都一样,这意味着我们需要重新考虑我们的目标。 总会有一些需要支持的浏览器,而这些浏览器不具有与大多数现代浏览器相同的功能。 所有平台上项目的完全外部相似性不仅浪费能源,而且还是渐进式改进思想的根本敌人。

底线:我不会成为JavaScript的杀手er


不要误会我的意思,我不属于JavaScript的敌人。 多亏使用了这种语言,我才得以建立事业,并且说实话,JavaScript十多年来给我带来了很多乐趣。 与任何长期的关系一样,我花费在JavaScript上的时间越多,我就越了解它。 它是一种成熟的语言,具有许多功能,并且每年都会变得越来越好。

但是,有时在我看来,我们的JavaScript关系出了点问题。 我批评他。 或者,更确切地说,我批评当前将JavaScript视为网站构建的主要工具的趋势,而JavaScript最初是在不考虑其他任何手段的情况下才采用的。 当我分析另一个看起来像令人困惑的圣诞花环的捆绑包时,对我来说很明显,网络充满了JavaScript。 我们几乎出于任何原因都使用这种语言,即使在某些情况下不需要此语言也是如此。 有时我会考虑这种对JS态度的后果有多严重。

我计划继续写有关JavaScript和Web开发的文章,继续寻找合理使用Web技术的方法。 希望我们在一起可以使现代网络更好。

亲爱的读者们! 您是否认为现代网络确实充满了JavaScript代码?

Source: https://habr.com/ru/post/zh-CN447576/


All Articles