又新學到了一個 JavaScript 知識點 Import Maps

關於import maps不知道大家有沒有聽過,我之前只是知道JavaScript增加的新特性,具體什麼作用也沒有進行學習研究過,週末學習中看到有人使用它,於是也嘗試瞭解一下~,後來發現確實還是挺好用的,所以寫一篇文章給大家分享一下。

我主要從以下幾個方面來簡單介紹一下import maps

import maps 是什麼

import map直譯過來是 導入映射,它與模塊的使用有關,一般我們在項目中導入模塊,會調用require方法,或者使用import語句或方法,引入的模塊通常使用 npm 之類的包管理器進行管理。但是import map提供了一種支持,讓我們可以直接在頁面上管理模塊,不需要通過打包構建。

import maps已經成爲了一個 Web 標準,並且在 2021 年 7 月正式通過了 W3C 的標準化流程;但是由於這個特性比較新,很多瀏覽器不支持,後面我們詳細聊聊兼容情況。

接下來看一下import map怎麼工作的...

import maps 怎麼使用

import maps中,可以使用一個 JavaScript 對象來定義模塊標識符與對應 URL 的映射關係,例如:

<script type="importmap">
  {
    "imports"{
      "lodash""https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.js",
      "react""/node_modules/react/index.js"
    }
  }
</script>

在上述示例中,定義了 lodash 模塊的 URL 爲 https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.js,而 react 模塊的 URL 則爲相對路徑 /node_modules/react/index.js

通過importmap,可以在模塊中使用字符串形式的模塊名稱來導入其他模塊,而不必關心實際模塊資源的 URL,例如:

<script type="module">
  import _ from "lodash";
  import React from "react";
</script>

這樣,JavaScript 引擎會自動根據導入映射中定義的映射關係來加載模塊資源,並將其綁定到相應的模塊變量上。

總之import maps使用其實非常的簡單,是通過在html文檔的script標籤中,使用json對象來配置所有需要再當前 html 文檔中需要引入的模板。

如果這個json映射表內容比較多,我們還可以將它放在其他文件中,然後通過src屬性去鏈接它,例如:

<script scr="xxx_importmaps.json"></script>

上面一起簡單的認識了import maps 相信不少人和我一樣,都在想一個問題,這種實現方式有什麼優勢呢,或者說究竟能解決哪些問題, 帶着疑問我們進入下一節

import maps有何優勢?

換句話說,我們需要先知道import maps可以用來做什麼?

動態加載模塊

import maps 支持動態加載模塊,可以在需要的時候才進行加載,避免了一次性加載所有模塊的開銷,提高了應用程序的性能和用戶體驗。

這裏舉例說明一下,例如在 Vue2的項目中,文章頁面支持播放視頻,但是文章中沒有視頻時,我可以不用加載播放器, 項目中動態加載 player.min.js

  1. 首先在index.html文件中添加import maps
<head>
  <script type="importmap">
    {
      "imports"{
        "player""https://example.com/path/to/player.min.js"
      }
    }
  </script>
</head>
  1. 在 Vue 組件中動態加載 player.min.js 文件

可以在需要使用播放器的 Vue 組件中使用動態加載 player.min.js 文件的代碼,例如:

export default {
  data() {
    return {
      player: null,
    };
  },
  methods: {
    loadPlayer() {
      if (this.player) {
        return Promise.resolve(this.player);
      }

      return import('player').then((Player) ={
        this.player = new Player.default();
        return this.player;
      });
    },
    play() {
      this.loadPlayer().then((player) ={
        player.play();
      });
    },
  },
};

減少網絡請求

使用 import maps 可以減少網絡請求的主要原因是,可以將多個模塊合併成一個請求,從而減少了網絡請求的次數,提高了頁面的加載速度。

如果一個頁面需要引入多個模塊,如果不使用import maps,每個模塊都需要通過一個獨立的請求進行加載。而使用 import maps,可以將多個模塊合併成一個請求,從而減少了網絡請求的次數。例如:

<script type="importmap">
   {
  'lodash''https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js',
  'vue''https://cdn.jsdelivr.net/npm/vue@3.0.0/dist/vue.esm-browser.js',
  'axios''https://cdn.jsdelivr.net/npm/axios@0.21.1/dist/axios.min.js'
   };
</script>
// 定義一個映射表

// 加載模塊
Promise.all([
  import('lodash'),
  import('vue'),
  import('axios')
]).then(([ _, { createApp }, axios ]) ={
  const app = createApp({ /* ... */ });
  axios.get(/* ... */);
});

在上面的代碼中,在加載模塊時,我們使用了 Promise.all 方法將多個模塊的導入操作合併到一個 Promise 對象中,這樣瀏覽器就可以將多個模塊的請求合併成一個請求,從而減少了網絡請求的次數。需要注意的是,在使用 import maps 減少網絡請求時,我們需要避免將過多的模塊合併到一個請求中,否則可能會導致請求過大,影響頁面的加載速度。通常建議將多個模塊合併到一個請求中時,選擇具有相似功能或關聯度高的模塊進行合併,同時需要進行適當的測試和優化,以確保頁面的加載速度和性能。

模塊依賴關係管理

通過在import maps中定義模塊之間的依賴關係,可以更加方便地進行依賴管理,並且可以在不同的環境中(例如開發環境和生產環境)使用不同的導入映射,從而更好地控制依賴關係。假設我們有一個頁面需要加載三個模塊:module1.jsmodule2.jsmodule3.js。其中,module2.jsmodule3.js 依賴於 module1.js,也就是說,需要先加載 module1.js,再加載 module2.jsmodule3.js。在不使用 Import Maps 的情況下,我們需要手動管理這些模塊的加載順序,代碼如下:

// 加載模塊 1
const script1 = document.createElement('script');
script1.src = 'module1.js';
script1.onload = () ={
  // 加載模塊 2
  const script2 = document.createElement('script');
  script2.src = 'module2.js';
  script2.onload = () ={
    // 加載模塊 3
    const script3 = document.createElement('script');
    script3.src = 'module3.js';
    document.head.appendChild(script3);
  };
  document.head.appendChild(script2);
};
document.head.appendChild(script1);

上面的代碼中,我們通過創建 script 標籤來加載模塊,並使用 onload 事件來控制模塊的加載順序,保證模塊的依賴關係正確。這種方式需要手動管理模塊的加載順序,代碼比較冗長,且容易出錯。

使用 Import Maps 可以簡化這個過程,代碼如下:

<script type="importmap">
   {
    'module1''module1.js',
    'module2'{ deps: ['module1'], url: 'module2.js' },
    'module3'{ deps: ['module1'], url: 'module3.js' },
  };
</script>
// 加載模塊
Promise.all([
  import(moduleMap['module1'].url),
  import(moduleMap['module2'].url),
  import(moduleMap['module3'].url),
]).then(([module1, module2, module3]) ={
  // ...
});

其中每個模塊都定義了其依賴關係和對應的 URL。然後在加載模塊時,我們只需要按照模塊依賴關係的順序,依次導入每個模塊即可,不需要手動管理模塊的加載順序。這種方式代碼比較簡潔,且可以更好地管理模塊的依賴關係。

其他用法

動態構建 import map

import maps可以根據條件任意的動態配置,這個能力就相當於支持了動態加載模塊,可以在需要的時候才進行加載,避免一次性加載所有的模塊,提高應用程序的性能和用戶體驗。這裏舉例說明一下,例如我的文章頁面支持播放視頻,但是文章中沒有視頻,我可以不用加載播放器:

<script>
  const importMap = {
    imports: {
      lazyload: "player.min.js",
    },
  };
  const imp = document.createElement('script');
  imp.type = 'importmap';
  imp.textContent = JSON.stringify(importMap);
  document.currentScript.after(im);
</script>

這種方式需要確保創建和插入 import map 腳本標籤之前進行(如上所述),因爲修改一個已經存在的import map對象不會有任何效果。

import maps 引入同一個包的不同版本

通過import maps引入同一個包的不同版本是一件非常簡單的事情,你只需要把不同版本的標識符在映射中寫出來就行。就像下面這樣。

<script type="importmap">
      {
        "imports"{
          "vue@2/""https://cdn.jsdelivr.net/npm/vue@2.0.0/dist/vue.esm-browser.js",
          "vue@3/""https://cdn.jsdelivr.net/npm/vue@3.0.0/dist/vue.esm-browser.js"
        }
      }
</script>

import maps can i use

最後說一下關於import maps的兼容性問題,首先上一張can i use上的截圖:

目前已經得到了多個主流瀏覽器的支持,但還沒有被所有瀏覽器兼容。特別國內常用的QQ瀏覽器、安卓手機常用的UC瀏覽器等。

然而,對於一些舊版本的瀏覽器或者不支持導入映射的瀏覽器,可以通過使用 polyfill 或者額外的加載器庫(如 SystemJS)來實現導入映射的功能。

總之,雖然導入映射已經得到了多個主流瀏覽器的支持,但在開發過程中需要考慮到不同瀏覽器的兼容性問題。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/yXg9Aa20MTjnFdF47QQlyA