type=“module“ 你了解,但 type=“importmap“ 你知道吗

news2024/11/20 20:31:58

新出了一个系列: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/blog/import-maps/

交流

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

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/408820.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

css的rotate3d实现炫酷的圆环转动动画

1.实现效果 2.实现原理 2.1 rotate3d rotate3d&#xff1a;rotate3d() CSS 函数定义一个变换&#xff0c;它将元素围绕固定轴移动而不使其变形。运动量由指定的角度定义; 如果为正&#xff0c;运动将为顺时针&#xff0c;如果为负&#xff0c;则为逆时针。 语法&#xff1a; …

【面试题】redux及中间件相关面试题解析

1、什么是Redux&#xff1f; Redux就是一个js容器&#xff0c;用于全局的状态管理 2、为什么在React项目中要使用Redux&#xff1f; 因为React本质上就是一个UI库&#xff0c;它是单向数据流的&#xff0c;就是说数据只能从父组件通过props流向子组件&#xff0c;但如果子组…

Vue 无感刷新token

关于无感刷新的理解: 实现token无感刷新对于前端来说是一项非常常用的技术,其本质是为了优化用户体验,当token过期时不需要用户跳回登录页重新登录,而是当token失效时,进行拦截,发送刷新token的请求,获取最新的token进行覆盖,让用户感受不到token已经过期 刷新token的一些方案…

零基础JavaScript学习【第六期】

博主有话说:这个礼拜有些忙,离上一次更新已经过了亿天,这几天较忙所以更新会有点慢,谢谢大家的支持。(っ•̀ω•́)っ✎⁾⁾ 我爱学习 个人空间:GUIDM的个人空间 专栏内容:零基础JavaScript学习 基础还是关键。 欢迎大家的一键三连。 前情回顾:第一期https://blog.cs…

uni-app - 最详细 H5 网页接入微信支付功能,提供从详细的示例代码与注释(移动端网页实现微信支付能力,微信公众号前端支付 JSAPI / JS-SDK 详细示例源码)官方最新超级详细教程

前言 关于 uni-app 项目中接入微信支付的文章鱼龙混杂,各种 JSAPI / JS-SDK 乱代码、过时、没注释、不讲流程原理,非常难用。 本文实现了 uni-app H5 移动端网页项目,实现微信支付功能,详细讲解接入流程及超详细示例代码, 从 0-1 完全站在小白的角度,让您无需太多知识也…

【React】React入门--生命周期

&#x1f380;个人主页&#xff1a;努力学习前端知识的小羊 感谢你们的支持&#xff1a;收藏&#x1f384; 点赞&#x1f36c; 加关注&#x1fa90; 文章目录React生命周期初始化阶段运行中阶段销毁阶段老生命周期的问题新生命周期代替性能优化的方案重要的勾子即将废弃的勾子R…

【ElementUI样式优化1】el-table 修改斑马格样式、修改滚动条样式、添加表头边框、删除表格边框划线

重要的不是过去&#xff0c;而是你怎末看待过去&#xff0c;而我们对过去的看法&#xff0c;是可以改变的。 效果预览&#xff1a; &#xff08;1&#xff09;删除表格外框&#xff0c;内框。 &#xff08;2&#xff09;添加表头边框&#xff0c;修改表头文字大小、颜色 &…

猿创征文 |【高级篇】Java 进阶之JVM实战

文章目录⚡前言一、面试题解析二、JVM 理论详解⛅JVM的位置❄️JVM的体系结构⏳类加载器三、JVM 双亲委派机制四、Native 关键字五、PC寄存器 与 方法区六、栈与堆七、三种JVM、新生区、老年区、永久区⛵小结⚡前言 JVM 是 Java 实现 跨平台的基础&#xff0c;所有的Java 程序…

[遇到的问题-已解决]Cannot resolve plugin org.apache.maven.plugins:maven-compiler-plugin:3.1

如上图所示&#xff0c;这是我解决好的&#xff0c;刚开始的时候爆红有这些&#xff1a; 我按照在网上查找的方法&#xff0c;一一试了。 首先&#xff0c;maven 安装的路径和和本地仓库的目录必须要保持一致 打开setting-Build,Excution,Deployment-Build Tools-Maven&#…

vue框架介绍

概述 Vue 是一套用于构建用户界面的渐进式框架 对渐进式的理解&#xff1a; 每个框架都不可避免会有自己的一些特点&#xff0c;从而会对使用者有一定的要求&#xff0c;这些要求有强有弱&#xff0c;它的强势程度会影响在业务开发中的使用方式。 vue的定位&#xff1a; 我在做…

【Vue入门必备知识篇03】--- 生命周期 数据共享

前言❤️ 过好自律的生活&#xff0c;美好才会在路上不期而遇 ❤️【Vue入门必备知识篇03】--- 生命周期 & 数据共享一、生命周期 & 数据共享&#xff08;1&#xff09;组件的生命周期1.1 生命周期 & 生命周期函数1.2 组件生命周期函数的分类1.3 生命周期图示&…

记录Chrome插件从V2版本升级到V3版本的过程中遇到的问题

总结一下自己在把Chrome V2版本的插件升级到V3版本的过程中&#xff0c;遇到的一些问题&#xff0c;之前也有发布一章V3版本的manifest.json配置项参数说明&#xff0c;基本也涵盖了下面提到的几个配置项的改动&#xff0c;传送门>> 总结分了两大块&#xff0c;一块是ma…

【Node.js】深度解析常用核心模块-fs模块

✅ 作者简介&#xff1a;一名将要迈入大三的大学生&#xff0c;致力于提高前端开发能力 ✨ 个人主页&#xff1a;前端小白在前进的主页 &#x1f525; 系列专栏 &#xff1a; node.js学习专栏 ⭐️ 个人社区 : 个人交流社区 &#x1f525;前言 在文章&#x1f449;Node.js — 前…

React脚手架工具创建项目的详细介绍

文章目录React脚手架工具脚手架工具解析create-react-app创建React项目目录的结构分析从零编写代码React脚手架工具 脚手架工具解析 如果我们只是开发几个小的demo程序&#xff0c;那么永远不需要考虑一些复杂的问题: 比如目录结构如何组织划分; 比如如何管理文件之间的相互依…

Python lxml库的安装和使用

lxml 是 Python 的第三方解析库&#xff0c;完全使用 Python 语言编写&#xff0c;它对 Xpath 表达式提供了良好的支持&#xff0c;因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 安装lxml库 lxml 属于 Python 第三方库&#xff0c;因此需要…

全网多种方式解决Unchecked runtime.lastError: The message port closed before a response was received的错误

文章目录1. 文章引言2. 分析问题3. 解决问题4. 解决该错误的其他方法1. 文章引言 今天启动项目后访问Knife4j接口文档&#xff0c;却报出下图错误&#xff1a; 在报出Knife4j文档请求异常错误时&#xff0c;赶紧打开控制台&#xff0c;如下所示&#xff1a; 即Unchecked runti…

CSS过渡动画

css中实现动画有两种方式&#xff1a;transition过渡动画、 animation自定义动画。 transition 是 css3 新增的⼀个功能&#xff0c;可以实现元素不同状态间的平滑过渡&#xff08;当元素从⼀个状态进⼊到另⼀个状态时&#xff09;&#xff0c;经常⽤来制作⼀些动画效果。 之…

前端项目面试核心问题(持续更新)

本文有配套视频教程 项目面核心问题回答思路 说说你最近的项目 记叙文的六要素&#xff1a;时间、人物、地点、起因、经过、结果&#xff1b;时间&#xff1a;研发周期&#xff1b;人物&#xff1a;团队成员、分工、我负责哪几个模块&#xff1b;起因&#xff1a;项目背景、…

Vue项目实战——【基于 Vue3.x + Vant UI】实现一个多功能记账本(开发导航栏及公共部分)

基于 Vue3.x Vant UI 的多功能记账本&#xff08;三&#xff09; 文章目录基于 Vue3.x Vant UI 的多功能记账本&#xff08;三&#xff09;项目演示开发导航栏1、底部导航栏2、测试底部导航栏3、公共头部写到最后&#xff08;附源码&#xff09;系列内容参考链接基于 Vue3.x …

element-UI组件之日期时间选择器与时间格式转化

element-UI组件之日期时间选择器与时间格式转化日期选择器与时间选择器的一般使用日期时间选择器选择日期时间点选择日期范围日期选择器月份范围选择器禁选日期用time.getTime()进行日期(时间)格式的转换时间选择器el-time-select选择固定时间点el-time-picker选择任意时间点禁…