JavaScript 导入映射(import map) 及 import.meta介绍

import.meta - JavaScript | MDN

import.meta 是一个宿主环境创建的可扩展的 null 原型对象,其所有属性均可写、可配置、可枚举。规范没有在对象上明确定义任何属性,但是宿主环境通常会定义以下属性:

  • url
  • resolve()

描述

import.meta 语法由关键字 import、一个点符号和 meta 标识符组成。因为 import 是保留字而不是标识符,其并不是属性访问器而是特殊的表达式语法。

import.meta 元属性在 JavaScript 模块中可用;在模块之外(包括在模块中直接调用 eval())使用 import.meta 是语法错误。

import.meta.url

- 示例(传递查询参数):

在 import 声明中使用查询参数允许为特定模块传递参数,可作为应用程序内从 window.location(或在 Node.js 中从 process.env)读取参数的补充方式。例如下面的 HTML:

<script type="module">
  import "./index.mjs?someURLInfo=5";
</script>

index.mjs 模块可以通过 import.meta 获取 someURLInfo 参数:

// index.mjs
new URL(import.meta.url).searchParams.get("someURLInfo"); // 5

import.meta.resolve()

import.meta.resolve() allows a script to access the module specifier resolution algorithm for a name, like this:

// Script at https://example.com/main.js

const helperPath = import.meta.resolve("./lib/helper.js");
console.log(helperPath); // "https://example.com/lib/helper.js"

Note that import.meta.resolve() only performs resolution and does not attempt to load or import the resulting path. Therefore, its return value is the same regardless of whether the returned path corresponds to a file that exists, and regardless of whether that file contains valid code for a module. This allows import.meta.resolve() to be a synchronous operation.

 

导入映射(import map)

以下转载自:https://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247514444&idx=1&sn=c25877d092150b1df5d37408abd438c4&chksm=f992bf9dcee5368b681ebff5101de7c509d9dd5f86a249d0da488c3a3bca488bc116b77efa0b&scene=126&sessionid=0

关于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>
2. 在 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.js 和 module3.js。其中,module2.js 和 module3.js 依赖于 module1.js,也就是说,需要先加载 module1.js,再加载 module2.js 和 module3.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(imp);
</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上的截图:

图片

image.png

目前已经得到了多个主流浏览器的支持,但还没有被所有浏览器兼容。特别国内常用的QQ浏览器、安卓手机常用的UC浏览器等。

然而,对于一些旧版本的浏览器或者不支持导入映射的浏览器,可以通过使用 polyfill 或者额外的加载器库(如 SystemJS)来实现导入映射的功能。

总之,虽然导入映射已经得到了多个主流浏览器的支持,但在开发过程中需要考虑到不同浏览器的兼容性问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值