JavaScript >> Javascript 文檔 >  >> Tags >> map

關於 JavaScript 導入地圖你需要知道的一切

當 ES 模塊首次在 ECMAScript 2015 中引入作為 JavaScript 中模塊系統標準化的一種方式時,它是通過在 import 語句中強制指定相對路徑或絕對路徑來實現的。

import dayjs from "https://cdn.skypack.dev/[email protected]"; // 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 運行時或相關構建工具映射到特定(和版本化)文件。用戶只需要在 import 語句中應用裸模塊說明符(通常是包名),關於模塊解析的問題就會自動處理。

由於開發人員已經熟悉這種從 npm 導入包的方式 ,需要一個構建步驟來確保以這種方式編寫的代碼可以在瀏覽器中運行。導入地圖解決了這個問題。本質上,它允許將導入說明符映射到相對或絕對 URL,這有助於在不應用構建步驟的情況下控制模塊的解析。

導入地圖的工作原理

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

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

通過 <script type="importmap"> 指定導入映射 HTML 文檔中的標籤。此腳本標記必須放在第一個 <script type="module"> 之前 文檔中的標籤(最好在 <head> ) 以便在執行模塊解析之前對其進行解析。此外,目前每個文檔只允許一個導入映射,但我們計劃在未來取消此限制。

在 script 標籤內,一個 JSON 對像用於為文檔中的腳本所需的模塊指定所有必要的映射。一個典型的導入圖的結構如下圖所示:

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

imports 上面的對象,每個屬性對應一個映射。映射的左側是導入說明符的名稱,而右側是說明符應映射到的相對或絕對 URL。在映射中指定相對 URL 時,請確保它們始終以 / 開頭 , ../ , 或 ./ .請注意,導入映射中存在包並不一定意味著它將由瀏覽器加載。任何未被頁面上的腳本使用的模塊都不會被瀏覽器加載,即使它存在於導入映射中。

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

您還可以在外部文件中指定映射,然後使用 src 鏈接到文件的屬性(如上所示)。如果您決定使用此方法,請確保發送文件時使用其 Content-Type 標頭設置為 application/importmap+json .請注意,出於性能原因,建議使用內聯方法,這也是本文其餘部分的示例展示方式。

指定映射後,您可以在導入語句中使用導入說明符,如下所示:

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

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

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

需要注意的是,導入映射中的映射不會影響像 src 這樣的地方的 URL <script> 的屬性 標籤。因此,如果您使用類似 <script src="/app.js"> ,瀏覽器將嘗試下載文字 app.js 該路徑下的文件,無論導入映射中的內容是什麼。

將說明符映射到整個包

除了將說明符映射到模塊之外,您還可以將一個說明符映射到包含多個模塊的包。這是通過使用以斜杠結尾的說明符鍵和路徑來完成的。

<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>

動態構建導入地圖

映射也可以基於任意條件在腳本中動態構建,並且此功能可用於基於特徵檢測有條件地導入模塊。下面的例子在lazyload下選擇正確的文件導入 說明符基於是否為 IntersectionObserver 支持API。

<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>

如果您想使用這種方法,請確保在創建和插入導入映射腳本標記之前執行此操作(如上所述),因為修改已經存在的導入映射對像不會有任何效果。

通過映射散列提高腳本的可緩存性

實現靜態文件長期緩存的常用技術是在文件名中使用文件內容的哈希值,以便文件保留在瀏覽器緩存中,直到文件內容髮生更改。發生這種情況時,文件將獲得一個新名稱,以便最新更新立即反映在應用程序中。

使用捆綁腳本的傳統方式,如果更新了多個模塊所依賴的依賴項,則此技術可能會失敗。這將導致所有依賴於該依賴項的文件都被更新,這會迫使瀏覽器重新下載它們,即使只更改了一個字符的代碼。

導入映射通過允許通過重新映射技術單獨更新每個依賴項來解決此問題。假設您需要從名為 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'

當需要更新文件時,只需要更新導入映射。由於對其導出的引用不會更改,因此它們將保持緩存在瀏覽器中,同時由於更新的哈希值再次下載更新的腳本。

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

使用同一模塊的多個版本

使用導入映射很容易要求同一個包的多個版本。您需要做的就是在映射中使用不同的導入說明符,如下所示:

    <script type="importmap">
      {
        "imports": {
          "lodash@3/": "https://unpkg.com/[email protected]/",
          "lodash@4/": "https://unpkg.com/[email protected]/"
        }
      }
    </script>

您還可以使用相同的導入說明符通過使用範圍來引用同一包的不同版本。這允許您在給定範圍內更改導入說明符的含義。

<script type="importmap">
  {
    "imports": {
      "lodash/": "https://unpkg.com/[email protected]/"
    },
    "scopes": {
      "/static/js": {
        "lodash/": "https://unpkg.com/[email protected]/"
      }
    }
  }
</script>

使用此映射,/static/js 中的任何模塊 路徑將使用 https://unpkg.com/[email protected]/ 引用 lodash/ 時的 URL import 中的說明符 語句,而其他模塊將使用 https://unpkg.com/[email protected]/ .

將 NPM 包與導入地圖一起使用

正如我在整篇文章中所展示的那樣,任何使用 ES 模塊的 NPM 包的生產就緒版本都可以通過 ESM、Unpkg 和 Skypack 等 CDN 在您的導入映射中使用。即使 NPM 上的包不是為 ES 模塊系統和本機瀏覽器導入行為而設計的,Skypack 和 ESM 等服務也可以將它們轉換為準備在導入映射中使用。您可以使用 Skypack 主頁上的搜索欄來查找瀏覽器優化的 NPM 包,這些包可以立即使用,而無需擺弄構建步驟。

以編程方式檢測導入地圖支持

只要支持 HTMLScriptElement.supports() 方法,就可以在瀏覽器中檢測導入地圖支持。以下代碼段可用於此目的:

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

支持舊版瀏覽器

導入映射使得在瀏覽器中使用裸模塊說明符成為可能,而無需依賴於當前在 JavaScript 生態系統中流行的複雜構建系統,但目前它在 Web 瀏覽器中並未得到廣泛支持。在撰寫本文時,Chrome 和 Edge 瀏覽器 89 及更高版本提供了全面支持,但 Firefox、Safari 和一些移動瀏覽器不支持該技術。要在此類瀏覽器中保留導入映射的使用,必須使用合適的 polyfill。

可以使用的 polyfill 的一個示例是 ES Module Shims polyfill,它為任何具有 ES 模塊基線支持的瀏覽器(大約 94% 的瀏覽器)添加了對導入映射和其他新模塊功能的支持。您需要做的就是在您的導入地圖腳本之前在您的 HTML 文件中包含 es-module-shim 腳本:

<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>

你仍然可能得到一個 JavaScript TypeError 在包含 polyfill 後,在此類瀏覽器中的控制台中。可以安全地忽略此錯誤,因為它不會產生任何面向用戶的後果。

Uncaught TypeError: Error resolving module specifier “lodash/toUpper.js”. Relative module specifiers must start with “./”, “../” or “/”.

其他與導入地圖相關的 polyfill 和工具可以在其 GitHub 存儲庫中找到。

結論

導入映射提供了一種在瀏覽器中使用 ES 模塊的更明智的方法,而不僅限於從相對或絕對 URL 導入。這使您可以輕鬆地移動代碼而無需調整導入語句,並使單個模塊的更新更加無縫,而不會影響依賴於這些模塊的腳本的可緩存性。總體而言,導入映射為 ES 模塊在服務器和瀏覽器中的使用方式帶來了奇偶性。

您會使用導入映射來替換或補充您當前的構建系統嗎?請在 Twitter 上告訴我您做出決定的原因。

感謝閱讀,祝您編碼愉快!


Tutorial JavaScript 教程
  1. 使用 React 的 Flux 啟發庫

  2. 挑戰:編寫你最糟糕的程序

  3. JavaScript 中重複字符串的三種方法

  4. 如何在 JavaScript 中檢查對像是否為空

  5. 完整的付款結構

  6. 使用 React JS 創建無限滾動! ♾️

  7. 在字符串和 ArrayBuffers 之間轉換

  1. 如何使用 Fuse.js 實現客戶端搜索

  2. JavaScript 參考指南:JS 模塊生態系統

  3. PWA:漸進式 Web 應用程序的提示和技巧大列表

  4. 撲熱息痛.js💊| #95:解釋這段 JavaScript 代碼

  5. 如何在 JavaScript 中遍歷對象

  6. React State:一個簡單易懂的分解

  7. MutationObserver - 如何檢測 iframe 中的 dom 變化

  1. 做難以想像的事——Kinsta 將每個人的性能提升高達 200%

  2. 使用 Pablo 在 JavaScript 中構建 SVG

  3. Javascript 1o1 - 使用 DOM - 2

  4. 使用 NuxtJs 的 Laravel Api 身份驗證(Sanctum)(第 2 部分)