乐趣区

关于前端:typemodule-你了解但-typeimportmap-你知道吗

新出了一个系列:Vue2 与 Vue3 技巧小册

有幻想,有干货,微信搜寻【大迁世界】关注这个在凌晨还在刷碗的刷碗智。
本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。

当 ES 模块第一次在 ECMAScript 2015 中被引入,作为在 JavaScript 中标准化模块零碎的一种形式时,它是通过在 import 语句中指定绝对或绝对路径来实现的。

import dayjs from "https://cdn.skypack.dev/dayjs@1.10.7"; // ES modules

console.log(dayjs("2019-01-25").format("YYYY-MM-DDTHH:mm:ssZ[Z]"));

这与模块在其余通用模块零碎中的工作形式略有不同,例如 CommonJS,以及在应用 webpack 这样的模块捆绑器时,应用的是更简略的语法。

const dayjs = require('dayjs') // CommonJS

import dayjs from 'dayjs'; // webpack

在这些零碎中,通过 Node.js 运行时或相干的构建工具,导入指定器被映射到一个特定(和版本)的文件。用户只须要在导入语句中利用袒露的模块指定符(通常是包名),围绕模块解析的问题就会被主动解决。

因为开发者曾经相熟了这种从 npm 导入包的形式,所以须要一个构建步骤来确保以这种形式编写的代码可能在浏览器中运行。这个问题由 import maps 解决了。从实质上讲,它容许将导入指定器映射到绝对或相对的 URL 上,这有助于管制模块的解析,而不须要利用构建步骤。

import maps 是怎么工作的

<script type="importmap">
{
  "imports": {"dayjs": "https://cdn.skypack.dev/dayjs@1.10.7",}
}
</script>
<script type="module">
  import dayjs from 'dayjs';

  console.log(dayjs('2019-01-25').format('YYYY-MM-DDTHH:mm:ssZ[Z]'));
</script>

import map 是通过 HTML document 中的 <script type="importmap">标签指定的。这个 script 标签必须放在 document 中的中第一个 <script type="module">标签之前(最好是在 <head> 中),以便在进行模块解析之前对它进行解析。此外,目前每个 document 只容许有一个 import map,将来可能会勾销这一限度。

script 标签内,一个 JSON 对象被用来指定 document 中 script 所需的所有必要的模块映射。一个典型的 import map 的构造如下所示。

<script type="importmap">
{
  "imports": {
    "react": "https://cdn.skypack.dev/react@17.0.1",
    "react-dom": "https://cdn.skypack.dev/react-dom",
    "square": "./modules/square.js",
    "lodash": "/node_modules/lodash-es/lodash.js"
  }
}
</script>

在下面的 imports 对象中,每个属性都对应着一个映射。映射的右边是 import 指定器的名称,而左边是指定器应该映射到的绝对或相对 URL。

当在映射中指定绝对 URL 时,确保它们总是以 /././结尾。请留神,在 import map 中呈现包并不意味着它肯定会被浏览器加载。任何没有被页面上的 script 应用的模块都不会被浏览器加载,即便它存在于 import map 中。

<script type="importmap" src="importmap.json"></script>

你也能够在一个内部文件中指定你的映射,而后应用 src 属性链接到该文件(如上所示)。如果决定应用这种办法,请确保在发送文件时将其 Content-Type 标头设置为application/importmap+json

留神,出于性能方面的思考,举荐应用内联形式,本文的其余部分的事例,也会应用内联形式。

一旦指定了映射,就能够在 import 语句中应用 import 说明符,如下所示

<script type="module">
  import {cloneDeep} from 'lodash';

  const objects = [{a: 1}, {b: 2}];

  const deep = cloneDeep(objects);
  console.log(deep[0] === objects[0]);
</script>

须要留神的是,导入映射中的映射不会影响诸如 <script> 标签的 src 属性之类的地位。因而,如你的应用 <script src="/app.js"> 之类的内容,浏览器将试图在该门路上下载一个字面上的 app.js 文件,而不论 import map 中的内容如何。

将指定者映射到整个包中

除了将一个指定器映射到一个模块,你也能够将一个指定器映射到一个蕴含多个模块的包。这是通过应用指定器键和以尾部斜线结尾的门路来实现的。

<script type="importmap">
{
  "imports": {"lodash/": "/node_modules/lodash-es/"}
}
</script>

这种办法容许咱们导入指定门路中的任何模块,而不是整个主模块,这会导致所有组件模块由浏览器下载。

<script type="module">
  import toUpper from 'lodash/toUpper.js';
  import toLower from 'lodash/toLower.js';

  console.log(toUpper('hello'));
  console.log(toLower('HELLO'));
</script>

动静地构建 import map

映射也能够基于任意条件在 script 中动静结构,这种能力能够用来依据特色检测有条件地导入模块。上面的例子依据 IntersectionObserver API 是否被反对,在 lazyload 指定器下抉择正确的文件进行导入。

<script>
  const importMap = {
    imports: {
      lazyload: 'IntersectionObserver' in window
        ? './lazyload.js'
        : './lazyload-fallback.js',
    },
  };

  const im = document.createElement('script');
  im.type = 'importmap';
  im.textContent = JSON.stringify(importMap);
  document.currentScript.after(im);
</script>

如果你想应用这种办法,请确保在创立和插入 import map 脚本标签之前进行(如上所述),因为批改一个曾经存在的导入地图对象不会有任何成果。

通过对哈希值的映射来进步脚本的可缓存性

实现动态文件长期缓存的常见技术是在文件名中应用文件内容的哈希值,这样文件就会始终在浏览器的缓存中,直到文件内容发生变化。当这种状况产生时,文件将失去一个新的名字,以便最新的更新立刻反映在应用程序中。

在传统的 bundling scripts, 的形式下,如果一个被多个模块依赖的依赖关系被更新,这种技术就会呈现问题。这将导致所有依赖该依赖的文件被更新,迫使浏览器从新下载它们,即便只有一个字符的代码被扭转。

import map 为这个问题提供了一个解决方案,它容许通过重映射技术独自更新每个依赖关系。假如你须要从一个名为 post.bundle.8cb615d12a121f6693aa.js 的文件中导入一个办法:

<script type="importmap">
  {
    "imports": {"post.js": "./static/dist/post.bundle.8cb615d12a121f6693aa.js",}
  }
</script>

而不是这样写:

import {something} from './static/dist/post.bundle.8cb615d12a121f6693aa.js'

能够这么写:

import {something} from 'post.js'

当更新文件的时候,只有 import map 须要更新。因为对其导出的援用没有更改,它们将放弃在浏览器中的缓存,同时因为更新的哈希值,更新的脚本将再次被下载。

<script type="importmap">
  {
    "imports": {"post.js": "./static/dist/post.bundle.6e2bf7368547b6a85160.js",}
  }
</script>

应用同一模块的多个版本

在 import map 中很容易实现一个包对应多个版本,所须要做的就是在映射中应用不同的导入指定符,如下图所示:

    <script type="importmap">
      {
        "imports": {
          "lodash@3/": "https://unpkg.com/lodash-es@3.10.1/",
          "lodash@4/": "https://unpkg.com/lodash-es@4.17.21/"
        }
      }
    </script>

通过应用作用域,也能够用同一个导入指定符来指代同一个包的不同版本。这容许咱们在一个给定的作用域内扭转导入指定符的含意。

<script type="importmap">
  {
    "imports": {"lodash/": "https://unpkg.com/lodash-es@4.17.21/"},
    "scopes": {
      "/static/js": {"lodash/": "https://unpkg.com/lodash-es@3.10.1/"}
    }
  }
</script>

有了这种映射,在 /static/js 门路下的任何模块,在导入语句中援用 lodash/ 指定器时,将应用https://unpkg.com/lodash-es@3.10.1/,而其余模块将应用https://unpkg.com/lodash-es@4.17.21/

应用带有 import map 的 NPM 包

正如在本文中所展现的,任何应用 ES Modules 的 NPM 包的生产版本都能够通过 ESM、Unpkg 和 Skypack 等 CDN 在 import map 中应用。

即便 NPM 上的包不是为 ES 模块零碎和本地浏览器导入行为设计的,像 Skypack 和 ESM 这样的服务也能够将它们转化为可在导入地图中应用的包。能够应用 Skypack 主页上的搜寻栏来寻找浏览器优化的 NPM 包,这些包能够立刻应用,而无需摆弄构建步骤。

检测 import map 反对

只有反对 HTMLScriptElement.supports() 办法,就能够在浏览器中检测 import map 的反对:

if (HTMLScriptElement.supports && HTMLScriptElement.supports('importmap')) {// import maps is supported}

反对旧的浏览器

Import map 使得在浏览器中应用裸模块指定器成为可能,而无需依赖目前在 JavaScript 生态系统中普遍存在的简单的构建零碎,但目前网络浏览器中并不广泛支持它。

在整顿本文时,Chrome 和 Edge 浏览器的 89 版及当前的版本提供了全面反对,但 Firefox、Safari 和一些挪动浏览器不反对这项技术。为了在这些浏览器中保留对 import map 的应用,必须采纳一个适合的 polyfill。

一个能够应用的 polyfill 的例子是 ES Module Shims polyfill,它为任何反对 ES 模块基线的浏览器 (约 94% 的浏览器) 增加了 import map 和其余新模块个性的反对。咱们所须要做的就是在 import map 脚本之前在 HTML 文件中蕴含 es-module-shim 脚本

<script async src="https://unpkg.com/es-module-shims@1.3.0/dist/es-module-shims.js"></script>

在包含 polyfill 之后,可能会在你的控制台中失去一个JavaScript TypeError。这个谬误能够被平安地疏忽,因为它不会产生任何面向用户的结果。

总结

import map 提供了一种更理智的形式来在浏览器中应用 ES 模块,而不局限于从绝对或相对的 URL 中导入。这使得咱们能够很容易地挪动代码,而不须要调整 import 语句,并使个别模块的更新更加无缝,而不影响依赖这些模块的脚本的缓存能力。总的来说,import map 为 ES 模块在服务器和浏览器中的应用形式带来了平等性。

代码部署后可能存在的 BUG 没法实时晓得,预先为了解决这些 BUG,花了大量的工夫进行 log 调试,这边顺便给大家举荐一个好用的 BUG 监控工具 Fundebug。


作者:romaopedro199 译者:前端小智 起源:dev 原文:https://www.honeybadger.io/bl…

交换

有幻想,有干货,微信搜寻 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。

本文 GitHub https://github.com/qq44924588… 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。

退出移动版