负责任地编写JavaScript代码(一)

原文地址:alistapart.com/article/res…
原文做者:Jeremy Wagner
译者:刘辉
声明:本翻译仅作学习交流使用,转载请注明来源javascript

从统计数据上看,JavaScript是性能的关键。以如今的趋势,中等大小的页面很快就会至少发送 400 KB 的 JavaScript,而这仅仅是传输时的大小,而且仍是压缩后的。php

不幸的是,虽然减小资源传输时间是整个性能的重要组成部分,可是压缩并不会影响浏览器处理整个脚本所需的时间。若是服务器发送了 400 KB 的压缩 JavaScript,则解压缩后浏览器须要处理的实际大小超过 1 MB。如何应对这些繁重的工做负载,取决于设备自己。 关于各类设备如何处理大批量JavaScript的文章不少,但事实是,在不一样的设备之间,即便是微不足道的处理时间也会有相差很大差距。css

以个人这个一次性项目为例,该项目提供约 23 KB 的未压缩 JavaScript。 在 2017 年中的 MacBook Pro 上,Chrome 耗时约 25 毫秒。可是,在诺基亚 2 Android手机上,该数字迅速增长到 190 毫秒。这不是很短的时间,可是在任何一种状况下,页面的交互速度都至关快。html

如今有个大问题:你如何看待诺基亚 2 在这些普通页面上的表现呢?它有些卡,即便是网络链接很快,浏览网页也是一种耐心的练习,由于 JavaScript 驱动的网页会花费很长的时间。java

图1

图 1. 在一个页面上浏览诺基亚 2 Android 手机的性能时间表概述,其中过多的 JavaScript 阻塞了主线程。react

尽管设备和网络都在不断进步,可是 JavaScript 的不断膨胀吞噬了这些收益。咱们须要负责任地使用 JavaScript。首先要了解咱们正在构建的内容以及构建方式。git

「网站」和「应用」

怪异的命名可能让咱们不能准确的认识事物的本质。蜜蜂和黄蜂差别很大,可是有时候咱们会把蜜蜂说成黄蜂。然而蜜蜂是益虫,黄蜂不是。github

网站和 WEB 应用程序的区别并不像黄夹克和蜜蜂之间的区别那么明显,可是若是把一个网站和一个功能齐全的 WEB 应用程序搞混,开发者和使用者都会很是痛苦。 若是你要为企业建立一个信息网站,则不太可能使用重量级的框架来管理DOM的变化或者使用客户端路由。使用不合适的工具不但会给用户形成损失,还会下降效率。web

当咱们构建一个 WEB 应用程序时,必需要注意:咱们正在安装的模块可能会带来数百(甚至数千)个依赖,其中一些甚至不肯定是否是安全的。 咱们还要编写复杂的配置来打包。在这种疯狂却无处不在的开发环境中,咱们须要摸清它们来确保构建的内容是快速且可访问的。 若是你对此不够了解,请在项目的根目录中运行 npm ls --prod,看看是否能识别该列表中的全部内容。即便这样,也不能保证第三方脚本彻底没有问题,我相信您的网站中至少有一些这样的脚本。chrome

咱们很容易忘记,网站和 WEB 应用程序所处的环境是同样的。二者都承受着来自各类各样的网络和设备的相同的环境压力。当咱们决定构建「应用程序」时,这些限制不会忽然消失,用户的手机也不会得到神奇的新功能。

咱们有责任评估谁在使用咱们的产品,并认识到他们访问互联网的条件可能与咱们预想的条件不一样。咱们须要知道咱们要实现的目标,而后才能够构建出能够达到目标的产品,即便构建起来并不使人兴奋

这意味着须要从新评估对 JavaScript 的依赖,以及使用 JavaScript 的方式。排斥 HTML 和 CSS 会让咱们走向不可持续的开发方式,从而损害性能和可访问性。

不要让框架迫使您陷入不可持续的模式

在团队合做中,我发现了一些奇怪的代码,这些团队依赖于框架来帮助他们提升生产力。这些奇怪代码的共同特征是会致使可访问性和性能变差。如下面的 React 组件为例:

import React, { Component } from "react";
import { validateEmail } from "helpers/validation";

class SignupForm extends Component {
  constructor (props) {
    super(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 the email checks out, submit
    if (validateEmail(this.state.email)) {
      // ...
    }
  }

  render () {
    return (
      
        Enter your email:
        
        Sign Up
      
    );
  }
}

复制代码

这里有一些值得注意的可访问性问题:

  1. 不使用 form 元素的表单不是表单。确实,你能够经过在父 div 中指定 role="form" 来对此进行说明,可是若是您要构建表单(确定看起来像一个表单),请使用具备适当操做和方法属性的 form 元素。 action 属性相当重要,由于它能够确保表单在缺乏 JavaScript 的状况下仍然能够执行某些操做,固然,只要组件是由服务器呈现的。

  2. span 元素不能替代 label 元素,label 元素可以提供可访问性而 span 元素不能。

  3. 若是咱们打算在提交表单以前在客户端作某事,那么咱们应该将绑定到 button 元素的 onClick 的逻辑移到 form 元素的 onSubmit 上。

  4. 顺便说一下,全部支持 HTML5 的浏览器,包括 IE10,都提供表单验证控件,为何还要使用 JavaScript 来验证电子邮件地址?这里可使用浏览器原生的验证功能,但请注意,要使其与屏幕阅读器配合使用,还须要一点技巧

  5. 尽管不是可访问性问题,可是此组件不依赖任何状态或生命周期方法,这意味着能够将其重构为无状态功能组件,这样可让 JavaScript 更少。

了解了这些内容,咱们能够重构此组件:

import React from "react";

const SignupForm = props => {
  const handleSubmit = event => {
    // Needed in case we're sending data to the server XHR-style
    // (but will still work if server-rendered with JS disabled).
    event.preventDefault();

    // Carry on...
  };
  
  return (
    <form method="POST" action="/signup" onSubmit={handleSubmit}> <label for="email" class="email-label">Enter your email:</label> <input type="email" id="email" required /> <button>Sign Up</button> </form> ); }; 复制代码

如今这个组件不只 JavaScript 减小了,并且可访问性也提升了不少。浏览器为咱们提供了不少免费的功能,咱们应该优先使用浏览器原生的功能。

这并非说只有在使用框架时才会出现没法访问的模式,而是对 JavaScript 的惟一偏心最终会在咱们对 HTML 和 CSS 的理解上出现差距。这些知识鸿沟一般会致使咱们甚至可能没有意识到的错误。框架是提升咱们生产力的有用工具,可是不管咱们选择使用哪一种工具,核心网络技术的持续学习对于创造良好的体验都是必不可少的。

依靠 web 平台,您将走得更快,更远

当咱们讨论框架问题时,必须说 web 平台自己就是一个强大的框架。如上一部分所示,咱们能够更好地依靠已经成熟的模式和浏览器自身特性。重复造轮子来替代它们只会让咱们本身更痛苦。

单页应用

开发者最容易掉入的陷阱之一就是盲目采用单页应用「SPA」模型,即便该模型不适合该项目。是的,经过 SPA 的客户端路由,用户确实能够得到更好的体验,可是你会失去什么呢? 浏览器本身的导航功能(尽管是同步的)提供了不少好处。 好比,根据规范来管理历史记录。 没有 JavaScript 的用户(不管是否由他们本身选择)都不会彻底失去访问权限。 要使 SPA 在没有 JavaScript 的状况下仍然可用,服务器端渲染就成了你必须考虑的事情。

图2

图2.慢网络环境下一个示例应用程序加载的比较。左侧的应用彻底取决于 JavaScript 来呈现页面。右侧的应用程序在服务器上呈现响应,但随后使用客户端映射将组件附加到现有的服务器提供的标记上。

若是客户端路由没法让人们知道页面上的内容已更改,则可访问性也会受到损害。这会使那些依靠辅助技术浏览页面的人没法肯定页面上发生了什么改变,解决这个问题是一项艰巨的任务。

而后是咱们的老对手:系统开销。不少客户端路由库很是小,可是当你的项目使用ReactReact Router,甚至再加上一个状态管理库做为基础时,你将接受大约135KB永远没法优化的代码。请仔细考虑这样的构建方式以及客户端路由是否真的有必要。一般状况下,能不用就不用。

若是担忧导航性能,能够用 rel = prefetch 来预加载同源的文档。 预加载的文档在缓存中,跳转时当即可用,所以对改善页面的感知加载性能具备显著做用。因为预加载的优先级较低,所以它们与关键资源抢带宽的可能性也较小。

图3

图3.在初始页面上预加载了 writing/ 的 HTML。 当用户请求 writing/ 时,会当即从浏览器缓存中加载其HTML。

连接预加载的主要缺点是你须要意识到它可能会形成浪费。 Quicklink是Google的一个很小的连接预加载脚本,它经过检查当前客户端是否处于慢网络环境或启用了数据保护程序模式,来判断是否进行预加载,而且默认状况下不进行跨域的预加载。

不管咱们是否使用客户端路由,Service workers 能够极大地提高回头用户的体验。当咱们用 Service workers 预缓存路由时,咱们将得到与连接预加载相同的好处,可是对请求和响应的控制程度更高。不管你是否将你的站点视为「应用程序」,向其添加Service workers都是当今存在的最负责任的 JavaScript 用法之一。

JavaScript 并不是布局难题的解决方案

若是咱们打算经过安装第三方模块来解决布局问题,那应该先想一想,“我到底要作什么?” CSS 旨在完成此工做,而且不须要任何抽象便可有效使用。 现在,JavaScript模块试图解决的大多数布局问题,例如盒子放置,对齐和调整大小,管理文本溢出,甚至整个布局系统,均可以使用 CSS 解决。 像 Flexbox 和 Grid 这样的现代布局引擎获得了很好的支持,所以咱们不须要使用任何布局框架来做为构建项目的基础。CSS 原本就是框架。咱们能够经过feature queries,渐进式的加强布局,这并不困难

/* Your mobile-first, non-CSS grid styles goes here */

/* The @supports rule below is ignored by browsers that don't support CSS grid, _or_ don't support @supports. */
@supports (display: grid) {
  /* Larger screen layout */
  @media (min-width: 40em) {
    /* Your progressively enhanced grid layout styles go here */
  }
}


复制代码

使用 JavaScript 解决方案来解决布局和展现问题并非什么新鲜事。 咱们在 2009 年就是这么干的,网站在每一个浏览器里看起来都应该彻底同样,无论是在 IE6 里仍是在更强大的浏览器里。 若是咱们在 2019 年仍然追求这个,那应从新评估咱们的开发目标。 总会有一些咱们必须支持的浏览器没法完成那些现代浏览器所能完成的工做。 全部平台上的所有视觉均等只是徒劳的追求,渐进加强才是的主要目标。

我不是要杀死JavaScript

没错,我对 JavaScript 没有恶意。它给了我一份职业,并且,若是我对本身说实话,那将是十多年的享受之源。像任何长期的关系同样,我花的时间越多,对它的了解就越多。这是一种成熟的,功能丰富的语言,并且随着时间的流逝,它只会变得愈来愈有能力和更优雅。

可是,JavaScript 让我感到有些矛盾。我对 JavaScript 持批判的态度,或许更准确地说,我对于把 JavaScript 做为构建 WEB 的首要手段的趋势持批判态度。当我拆开一个捆成一团的圣诞树灯同样的东西时,很明显,JavaScript 已经泛滥成灾。

在后续的系列文章中,我将提供更多实用的建议来阻止过分的使用 JavaScript,以便为 WEB 构建的内容对每一个地方的每一个人均可用。一些建议是预防性的,一些则是以毒攻毒的,不管哪一种,都是为了相同的目标。我相信咱们全部人都喜欢 WEB,并但愿经过 WEB 作正确的事,可是我但愿咱们思考如何使它对全部人更具弹性和包容性。


若是你以为这篇内容对你有价值,请点赞,并关注咱们的官网和咱们的微信公众号(WecTeam),每周都有优质文章推送:

WecTeam
相关文章
相关标签/搜索