如何减少浏览器理解需要的东西?

64 阅读6分钟

有一天,我正在举办一个关于单页应用程序性能优化的研讨会。对于这个研讨会,我需要一个可以逐步优化的例子。我决定不使用一个框架,因为我不知道与会者的经验和背景。另外,我不想把注意力放在框架的细节上,而是把注意力放在建立在平台上的概念上,这些概念普遍适用于SPA。

编写现代JavaScript已经有相当长的时间了(尤其是在Node上!),我在我的演示应用程序中使用了每一个新的语言功能。import/export,async/await,fetch, 类、箭头函数、模板字符串和字面意思。基本上所有与老式ES5语法相冲突的东西。

在我写完前几行后,我自然而然地想到要把所有的东西转成所有浏览器都能理解的东西。然后我停了一会儿,问自己。我真的必须这样做吗?

浏览器不需要到达那里,它们已经在这里了!"

所以在安装Babel和Webpack或者Browserify之前,我意识到了一些东西:就像我所有书上的每一个浏览器都知道如何解释CSS Grid一样,我所有书上的每一个浏览器都实现了大量的ESnext语法特性。那些在旧平台上会导致语法错误的功能。

我从类或箭头函数等功能中知道。但即使是像async/await - 我以前很少使用 -在所有主要平台上都可以使用。这真是太神奇了!这就是生活在未来!不需要转译,不需要有一个构建脚本。代码,部署,运行

请看下面的代码。这是由glitch.com提供的标准 client.js文件,但经过重构,使用了现代的JavaScript而不是jQuery和旧的语法。

const $ = (sel) => document.querySelector(sel);

export async function init() {
  console.log('hello world :o');
  const res = await fetch('/dreams');
  const dreams = await res.json();
  $('#dreams').innerHTML = dreams.map(dream => `<li>${dream}</li>`).join('');

  $('form').addEventListener('submit', async (ev) => {
    ev.preventDefault();
    const dream = $('input').value;
    const res = await fetch(`/dreams?dream=${dream}`, { method: 'POST' });
    if(res.ok) {
      $('#dreams').innerHTML = $('#dreams').innerHTML + `<li>${dream}</li>`;
      $('input').value = '';
      $('input').focus();
    }
  });
}

看看我们现在可以使用的所有美好事物。

  • async/await- 以同步的方式写异步代码。为你的承诺提供句法糖!
  • fetch- 像XHR一样,但实际上很容易使用!
  • 箭头函数- 匿名函数,更容易编写,更容易范围化
  • 模板字符串--不再有奇怪的字符串连接法
  • 模块--从其他JavaScript文件中导出和导入,原生的。

但是等等,你可能会说,那些不支持所有这些新功能的浏览器怎么办?是的,那些怎么办?

削足适履 - 5年前#

我很喜欢Chris HeilmannScriptConf上说的那句话:"屏蔽旧的浏览器是不行的,但100%支持它们是浪费时间"。这是对渐进式增强的美妙呼吁。创建一个坚实的基础,在功能准备好的时候进行增强。你可以为每一个功能单独做这个。或者,你可以更积极一些,在你支持哪些浏览器和不支持哪些浏览器方面做一个明确的切割。

英国广播公司(BBC)称其为*切中要害*。有一套严格的规则,浏览器必须通过才能获得完整的体验。

if('querySelector' in document
  && 'localStorage' in window
  && 'addEventListener' in window) {
  // bootstrap the javascript application
}

如果一个浏览器不符合一个标准,它就不会得到任何JavaScript,而只能忍受普通的、但仍然可用的、只有HTML的体验。这些规则是一个时代的标志。这篇文章发表于五年多以前。它区分了 "HTML4 "浏览器(它没有统一的API,很可能需要jQuery)和 "HTML5 "浏览器。

我认为我们现在面临着ES5浏览器和现代ESnext浏览器之间的类似区别。而且我认为我们可以再一次做一个简单的切割。为非ESnext浏览器提供一个坚实的,但却被削弱的体验,在现代浏览器上获得完整的功能体验。

现在就开始吧![#]

BBC文章中的规则是检查平台中的可用功能。这一次有点不同,因为我们很可能连功能检查都没有。语法的更新完全不同,有些浏览器可能会在整个脚本被解析和执行之前抛出语法错误。

但是,有一种方法可以在现代平台上安全地激活功能丰富的增强功能,而根本不会引起任何错误。而且,它就在平台本身中。

伴随着所有的现代语法功能,有一个(不那么)小但重要的规范。模块。不仅是在从其他文件导入和导出方法的意义上的模块,而且还有一个在HTML中可用的script 类型。

<script type="module">
import { init } from './client.js';
init();
</script>

在撰写本文时,这种script 类型在所有现代浏览器中都是可用的(在Firefox的一个标志后面),并为我们的事业提供了一个美妙的行为。能够理解这种类型的浏览器将按照定义加载模块。那些不知道该怎么做的浏览器会简单地忽略整个模块。完美!但火狐呢?

*但是Firefox怎么办?*我听到你说。火狐支持我前面展示的所有优点,但模块仍然在一个标志后面。然而,正如历史所显示的,这是一个可以很快改变的东西。其他每个浏览器都有ES模块。而且,当标志被激活时,ES模块在火狐浏览器中工作得相当好。因此,火狐在所有即将发布的版本中采用ES模块的道路是很好的。我们只是需要等待一下。

现在就使用#

我们在常青浏览器上达到了一个点,我们可以定义另一个检查点,即浏览器功能的新现状。就像我们当年区分非HTML5浏览器和HTML5浏览器一样,我们可以从现在开始对非ES6和ES6浏览器做一个分水岭。对哪些是支持的,哪些是不支持的达成共识。

在拼凑了所有的东西之后,我决定马上使用这个。我所有的新网站和项目都将尝试在没有转码或捆绑的情况下生存。也许会有一点Rollup来减少文件量,但不会有不来自平台的额外模块加载行为。在我的脚本中,我可以自由地使用我喜欢的、从Babel-times和Node.js中了解的所有语法。