如何不基于构建工具优雅的实现模块导入?

更新日期: 2022-08-16阅读: 1.1k标签: 模块作者: ConardLi
原文来自:code秘密花园;大家好,我是 ConardLi。

当 ES Module 最开始作为一种新的 JavaScript 模块化方案在 ES6 中被引入的候,其实是通过在 import 语句中强制指定相对路径或绝对路径来实现的。

import dayjs from "https://cdn.skypack.dev/dayjs@1.10.7"; // ES modules
console.log(dayjs("2022-08-12").format("YYYY-MM-DD"));

这和其他常见的模块化系统(例如 CommonJS)的工作方式略有不同,并且在使用像 webpack 这样的模块打包工具的时候会使用更简单的语法

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

import dayjs from 'dayjs'; // webpack

在这些系统里,模块导入语句通过 Node.js 运行时或相关构建工具映射到特定(版本)的文件。用户只需要在 import 语句中直接编写模块说明符(通常是包名),模块就可以自动处理。

由于开发人员已经熟悉了这种从 npm 导入包的方式,因此必须要先经过一个的构建步骤才能确保以这种方式编写的代码可以在浏览器中运行。

Import maps 就可以解决这个问题,它可以将模块说明符(包名)自动映射到它的相对或绝对路径。从而让我们不使用构建工具也能使用简洁的模块导入语法。

如何使用 Import maps

我们可以通过 html 中的 <script type="importmap"> 标签来指定一个 Import maps。

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

为了成功的在模块解析之前对其进行解析。这个 script 标签必须放在文档中第一个 <script type="module"> 标签之前(最好放在 <head>中),另外,目前每个 HTML 只允许编写一个 Import maps 。

<script type="module">
import dayjs from 'dayjs';

console.log(dayjs("2022-08-12").format("YYYY-MM-DD"));
</script>

在 script 标签内,我们可以通过一个 JSON 对象来为文档中的脚本所需导入的模块指定所有必要的映射。一个典型的 importmap 结构如下所示:

<script type="importmap">
{
"imports": {
"react": "https://cdn.jsdelivr.net/npm/react/umd/react.production.min.js",
"react-dom": "https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.production.min.js"
"square": "./modules/square.js",
"lodash": "/node_modules/lodash-es/lodash.js"
}
}
</script>

在上面的 import 对象中,每个属性对应一个映射。映射的左侧是导入说明符的名称(一般是包名),而右侧是说明符需要映射到的相对或绝对路径。在映射中指定相对路径时,必须要确保它们始终以 /、../或 ./ 开头。

另外,importmap 中声明的包并不一定意味着它一定会被浏览器加载。页面上的脚本没有使用到的任何模块都不会被浏览器加载,即便你在 importmap 中声明了它。

编写好 importmap 之后,你就可以在后面的脚本中直接使用 ES Module 语法了。

<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 属性链接到这个文件(Content-Type Header 必须要设置为 application/importmap+json 才能正常加载)。

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

不过尽量不要使用这种方式,因为它的性能比直接内联编写要差。

映射整个包

除了将一个说明符映射到模块之外,你还可以将一个说明符映射到包含多个模块的包:

<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('ConardLi'));
console.log(toLower('ConardLi'));
</script>

动态映射

你也可以基于一些条件在 script 中添加一个动态映射,比如,在下面的示例中我们通过判断是否存在 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>

使用同一模块的不同版本

使用 importmap 我们可以将不同的版本的模块映射到不同的包名中:

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

另外你还可以通过 scopes 来实现同一个包不同模块的更细粒度的版本控制:

<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 下的模块会使用 3.10.1 版本,而其他模块会使用 4.17.21 版本。

兼容性


这项技术目前在 Chrome 和 Edge 浏览器 89 及更高版本提供了全面支持,但 Firefox、Safari 和一些移动浏览器还没有支持。我们可以通过下面的代码来判断浏览器的支持情况:

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

对于没有提供支持的浏览器,我们可以使用下面这个 polyfill:https://github.com/guybedford/es-module-shims

另外官方也推荐了一些其他 importmap 相关的 polyfill 和工具:


https://github.com/WICG/import-maps#community-polyfills-and-tooling

参考

  • https://github.com/WICG/import-maps
  • https://www.honeybadger.io/blog/import-maps/

链接: https://www.fly63.com/article/detial/12001

ES6模块功能:export和import的加载方式

ES6之前已经出现了js模块加载的方案,最主要的是CommonJS和AMD规范。commonjs主要应用于服务器,实现同步加载,如nodejs。AMD规范应用于浏览器,如requirejs,为异步加载。

Node的https模块_创建HTTPS服务器

Node的https模块:HTTPS服务器使用HTTPS协议,需要证书授权,SSL安全加密后传输,使用443端口

如何让 node 运行 es6 模块文件,及其原理

最新版的 node 支持最新版 ECMAScript 几乎所有特性,但有一个特性却一直到现在都还没有支持,那就是从 ES2015 开始定义的模块化机制。而现在我们很多项目都是用 es6 的模块化规范来写代码的,包括 node 项目

module、export、require、import的使用

module每个文件就是一个模块。文件内定义的变量、函数等等都是在自己的作用域内,都是自身所私有的,对其它文件不可见。在module中有一个属性exports,即:module.exports。它是该模块对外的输出值,是一个对象。

Node.js - 模块系统

模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript 代码、JSON 或者编译过的C/C++ 扩展。Node.js 提供了 exports 和 require 两个对象

ES模块基础用法及常见使用问题

ES6中引入了模块(Modules)的概念,相信大家都已经挺熟悉的了,在日常的工作中应该也都有使用。本文会简单介绍一下ES模块的优点、基本用法以及常见问题。

ES6 export 和 export default的区别

ES6中 export 和 export default 与 import使用的区别,使用 react native 代码详解,现在流行的前端框架,angular+ 主要使用 export 导出模块,react native 中使用 export default 导出模块,如今编辑器非常强大,安装插件会自动弹出模块名称,知道其导出怎么使用就可以了

export和export default的区别

export与export default均可用于导出常量、函数、文件、模块;你可以在其它文件或模块中通过import+(常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便能够对其进行使用;

关于export和export default你不知道的事

网上有很多关于export和export default的文章,他们大部门都是只讲了用法,但是没有提到性能,打包等关键的东西。大家应该应该能理解import * from xxx会把文件中export default的内容都打包到文件中,而import {func} from xxx只会把文件中的func导入

最全的前端模块化方案

模块化主要是用来抽离公共代码,隔离作用域,避免变量冲突等。将一个复杂的系统分解为多个模块以方便编码。会讲述以下内容:CommonJS、AMD 及 核心原理实现、CMD 及 核心原理实现

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!