...">

浏览器中的ES6模块:它们准备好了吗?

您是否听说过在浏览器中使用ES6模块?实际上,这些是普通的ES6模块。它们仅适用于针对浏览器的代码。



如果有人不知道-这就是它的样子。

有一个页面index.html

<!DOCTYPE html>
<html>
  <body>
    <div id="app"></div>
  
    <script type="module" src="index.mjs"></script>
    <!--     "module" -->
  </body>
</html>

有一个文件index.mjs表示连接到页面的模块:

import { doSomething } from './utils.mjs';

doSomething('Make me a sandwich');

该模块doSomething又从模块导入功能utils.mjs

export const doSomething = message => {
  console.log('No.');
};

ES6模块已经存在了很多年。也许您正在考虑尝试使用它们。我在自己的项目中使用了大约一个月。同时,我得出的结论是……

悬念!

关于浏览器对模块的支持


在谈论我的理解之前,建议您先看看浏览器对模块的支持。我将在2020年初编写此材料,因此模块的支持水平达到了令人印象深刻的90%。


根据caniuse.com对浏览器模块的支持,

我对这90%的服务感到非常满意(我没有考虑10%用户的需求),尽管您可能希望承担更多责任。但是,即使考虑到这一点,如果您的项目不是为IE,UC或Opera Mini设计的,则这种支持水平意味着几乎100%的目标受众将能够毫无问题地使用您的项目。

的确,浏览器对模块的支持仅仅是开始。在这里,我想找到在我学习模块的最初三个问题的答案:

  • 在浏览器中使用模块是否有任何优势?
  • 缺点呢?
  • 我确定我有第三个问题,但现在我已经不记得了。

让我们弄清楚...

在浏览器中使用模块的优点是什么?


这是纯JavaScript!没有组装项目管道,没有400行Webpack配置文件,没有Babel,没有插件和预设,没有其他45个npm模块。只有您和您的代码。

编写代码将完全按照创建时的形式在浏览器中执行,这令人耳目一新。这可能需要更多的努力,但是结果非常令人愉快。这就是用手动变速箱驾驶汽车的方法。

所以。ES6模块的优点是纯JavaScript,这是因为缺少程序集,项目配置以及对依赖项的拒绝已变得不必要。还有什么?还有别的事吗?

不,仅此而已。

在浏览器中使用模块的缺点是什么?


modules模块和捆绑器的比较


在考虑是否在浏览器中使用ES6模块时,您实际上必须在使用模块和打包器(例如Webpack,Parcel或Rollup)之间进行选择。

您可以(可能)同时使用它们,但是实际上,如果您打算通过捆绑程序运行代码,则没有理由使用设计<script type="module">来加载捆绑文件。

因此,基于某人正在考虑使用ES6模块而不是捆绑器的假设,这是他必须放弃的:

  • 最小化完成的代码。
  • 尖端的JavaScript功能。
  • 与JavaScript语法不同的语法(React,TypeScript等)。
  • Npm模块。
  • 快取

更详细地考虑此列表的前三段。

▍文件大小


我的项目(使用模块)的大小为34 Kb。由于我不使用项目构建步骤,因此通过网络传输的代码包含非常长的变量名;其中有很多注释。此外,它代表了一堆小文件,就数据压缩而言,这不是很好。

如果我使用Parcel将所有这些放入一个包中,那么结果将是18 Kb。我的Casual Casio计算器报告说,这大约是不使用捆绑软件的项目代码的一半。部分原因是由于Parcel会压缩文件,而且还因为使用gzip可以更好地压缩使用这种方法的材料。

数据压缩引起我们对另一个问题的关注:文件的组织方式(从开发便利性的角度而言)直接传输到通过网络传输文件的方式。而且,在开发过程中文件的组织方式不一定与您希望在站点中看到文件的方式相对应。例如,在一个项目上工作时,可能有150个模块(文件)有意义。传输到浏览器的相同资料可以合理地组织成12个捆绑包。

我将解释这个想法。我并不是说使用模块意味着不能捆绑和缩小文件(无法做到这一点是一种错觉)。我只是说,两者都没有意义。

是的,这是一个有趣的观察。在我的应用程序中,直到我撰写本节为止,都使​​用了模块(我强调!)。我安装了Parcel以计算正确的装配体尺寸。现在,我认为没有理由返回普通模块。好吧,这没意思!

▍生活无trans


多年来,我已经习惯了使用最新的JavaScript语法结构以及出色的Babel工具,该工具将其转换为所有浏览器都能理解的代码。我已经习惯了如此之多,以至于我很少至少考虑过对浏览器的支持(当然,除了DOM和CSS)。

当我第一次尝试时type=«module»,我将自己做所有事情。我的目标是使用新的浏览器,所以我认为我可以使用惯用的现代JavaScript。

但是现实并不是那么乐观。尝试快速回答以下问题,不要四处寻找。边缘支持吗flatMap()? Safari是否支持破坏性分配(对象和数组)? Chrome在最后一个函数参数后是否支持逗号? Firefox是否支持幂运算?

例如,我必须寻找这些问题的答案,进行跨浏览器测试。但是我已经使用了很久了。在任何足够大的应用中,这很可能导致生产错误。

这也意味着自数组方法slice()(所谓的可选序列运算符)问世以来,我将无法使用最出色的JavaScript创新。我使用了神奇的设计prop?.value大约一个月(从创建React App工具开始支持它们开始,没有任何其他设置)。但是如果没有他们,我已经很不方便了。

▍缓存负担


对我而言,缓存是最大的绊脚石。实际上,事实证明,解决缓存问题非常有趣,这是我决定编写此材料的主要原因。

可以肯定的是,使用捆绑器处理物料时,每个生成的文件都有一个唯一的名称- index.38fd9e.js。具有此名称的文件的内容永不(永不)更改。因此,它可以被浏览器无限期地缓存。如果一次下载了这样的文件,则无需再次下载。

这是一个了不起的系统-除非您尝试找到有关如何清除缓存的问题的答案。

使用类似的设计加载模块<script type="module" src="index.mjs"></script>不使用文件名中的哈希。在这种情况下,应该如何通知浏览器index.mjs从缓存或网络下载的位置?

应当注意,几乎可以使缓存工作可以接受,但是需要一些努力。这是您需要的:

  • 您必须将所有答案的标题设置cache-control为一个值no-cache令人难以置信的是,不缓存并不意味着“不缓存”。这意味着该文件必须被缓存,但是该文件不应“用于在未经源服务器成功验证的情况下满足后续请求”。
  • ETag. — () , , , . , 38fd9e .
  • CDN- , . ETag . . CDN- . ( ETag) . (, , Firebase).
  • -, , « » . , , , , .

结果,当访问者重新访问该站点时,浏览器将说:“我需要下载文件index.mjs。我看到此文件已存在于我的缓存中,其ETag为38fd9e。我会从服务器请求此文件,但是我告诉他只有在他的ETag不是的情况下才将其发送给我38fd9e。”服务工作者将拦截此请求,忽略ETag并将index.mjs其从其缓存中返回(该文件在上次加载页面时进入缓存)。然后,服务工作者将请求重定向到服务器。服务器将返回一条消息,指出文件未更改,或者将文件存储在高速缓存中。

我认为,所有这些都是非常麻烦的。

现在,如果您有兴趣,请查看服务工作者代码:

self.addEventListener('fetch', e => {
  e.respondWith(
    (async () => {
      //       
      const cacheResponse = await caches.match(e.request);
      
      //   (),   
      // (ETag-    )
      fetch(e.request).then(fetchResponse => {
        caches
          .open(CACHE_NAME)
          .then(cache => cache.put(e.request, fetchResponse));
      });
      
      if (cacheResponse) return cacheResponse;
      
      return fetch(e.request);
    })()
  );
});

我很懒,所以我没有学习,也没有使用最新的FetchEvent.navigationPreload属性。事实是,到那时,我花了更多的时间进行缓存,而不是编写应用程序(为您提供信息,我分别在这些问题上花费了10和11个小时)。

是的,我想指出的是, “进口卡” 提案旨在解决上述一些问题。它允许您组织类似s index.jsindex.38fd9e.mj映射的内容。但是无论如何要生成散列,都需要某种组装流水线,必须将导入卡嵌入HTML文件中。这意味着这里需要一个捆绑器...实际上,在这种情况下,浏览器中不再需要这些模块。

结果,尽管理解所有这些都很有趣,但是可以将其与我整年骑单轮车到处行驶的方式进行比较。我不会再这样做了。

但是可能不是每个人都使用捆绑器吗?


我写这篇材料的前提是,每个人都使用结构import/export编写模块代码require,然后使用Webpack,Grunt,Gulp或类似的代码将代码收集到生产包中。

是否有不使用捆绑软件的开发人员?是否有人将他们的JavaScript代码放在多个文件中,然后将它们发送到生产环境而没有捆绑在一起?也许这些人之一是你?如果是这样,我想了解您的全部生活。

摘要


我努力根据有效开发的主要原则做出所有严肃的决定,例如在模块和捆绑器之间进行选择。这就是生产力和质量。

不幸的是,浏览器中模块的使用对任何一个都不起作用。

如果明天我要开始一个新项目,那么脑海中就不会有关于是否运行旧版的create-react-app的问题。所有初始设置将花费大约30秒,尽管40 Kb的初始项目大小有点大,但对于大多数站点而言,这不会发挥任何作用。

这是另一种情况。假设我需要将一些HTML / CSS和JavaScript放在一起进行某种实验,而这样的实验将是一个项目,其中包含的文件要多于几个文件。如果在进行此项目时,我不打算花时间设置系统来构建它,那么我可能会在浏览器中使用模块。然后-如果我不在乎此项目的性能。

我想知道Webpack及其相关工具如何与浏览器中的ES6模块一起使用。我相信,将来,当“进口卡”的提案得到足够的支持时,捆扎机可能会将它们用作提取当今使用的难看的哈希的机制。

亲爱的读者们!您是否在浏览器中使用过ES6模块?


All Articles