如何使用导入地图动态导入 JavaScript

作者选择了 Creative Commons以作为 Write for Donations计划的一部分获得捐赠。

介绍

外部库可以为新的 JavaScript 项目增加复杂性. 要安装和使用外部代码库,您需要一个构建工具,可以分析代码并将您导入的库合并到最终格式。

例如,你可能只需要一个库在你的应用程序的一部分,大多数用户可能永远不会需要的应用程序的一部分,比如一个管理页面,但默认情况下,大多数构建系统会将所有代码包装成一个大文件,用户将加载代码,无论他们是否需要执行它。

构建工具是开发经验的重要组成部分,但是一个叫做"导入地图" 的光谱将允许您在没有构建工具的情况下将外部代码导入您的项目,并且只有在运行时需要时才会加载代码. 导入地图不会完全取代执行许多其他有价值动作的构建工具,比如构建样式表和处理图像,但它们会允许您快速并方便地使用本地浏览器功能来拖曳新的JavaScript应用程序.

在此教程中,您会使用导入地图和JavaScript模块来导入代码而无需构建工具. 您将创建一个基本应用程序, 显示一个消息, 您将创建一个导入地图, 它会告诉你的浏览器在哪里 定位外部代码。 接下来,您将把所导入的代码整合到您的JavaScript中,并使用第三方代码而无需在本地下载代码或通过构建步骤运行. 最后,你会了解当前执行导入地图许多方面的工具,并研究所有现代浏览器.

前提条件

  • 联合国 您需要一个运行 [Node.js] (https://nodejs.org/en/about/) 的开发环境 。 这个教程在Node.js版本14.17.1和npm版本6.14.23上进行了测试. 要在 macOS 或 Ubuntu 18.04 上安装此功能,请遵循 [如何在 macOS (https://andsky.com/tech/tutorials/how-to-install-node-js-and-create-a-local-development-environment-on-macos) 上安装节点并创建本地开发环境(https://andsky.com/tech/tutorials/how-to-install-node-js-on-ubuntu-18-04) 或使用 [如何在 Ubuntu 18.04 (https://andsky.com/tech/tutorials/how-to-install-node-js-on-ubuntu-18-04 上安装节点.js] 的 PPA** 部分中的步骤。
  • 联合国 您还需要关于 JavaScript 的基本知识, 您可以在 [How To Code in JavaScript (https://www.digitalocean.com/community/tutorial_series/how-to-code-in-javascript) (JavaScript) (HTML (https://www.digitalocean.com/community/tutorial_series/how-to-build-a-website-with-html) 和 [CSS (https://www.digitalocean.com/community/tutorial_series/how-to-style-html-with-css ) 中找到这些知识. HTML和CSS的好资源是Mozilla开发者网络. .

第1步:创建一个HTML页面并插入JavaScript

在此步骤中,您将创建一个HTML页面,使用JavaScript进行动态活动,并启动本地开发服务器来跟踪您的更改。

要开始,在新目录中,创建一个空白的HTML文档。

在文本编辑器中打开名为index.html的文件:

1nano index.html

在文件内部,添加一个短,空白的HTML页面:

 1[label index.html]
 2<!DOCTYPE html>
 3<html lang="en-US">
 4  <head>
 5    <meta charset="utf-8">
 6    <meta http-equiv="X-UA-Compatible" content="IE=edge">
 7    <title>Hello World</title>
 8    <meta name="viewport" content="width=device-width, initial-scale=1">
 9  </head>
10  <body>
11  </body>
12</html>

本文档有几个标准的 <meta> 标签和一个空的 <body> 元素。

然后添加一个<script>标签. 脚本标签的src属性将是一个新的JavaScript文件,您即将创建的名为hello.js:

 1[label index.html]
 2<!DOCTYPE html>
 3<html lang="en-US">
 4<head>
 5  <meta charset="utf-8">
 6  <meta http-equiv="X-UA-Compatible" content="IE=edge">
 7  <title>Hello World</title>
 8  <meta name="viewport" content="width=device-width, initial-scale=1">
 9  <script defer src="./hello.js"></script>
10</head>
11<body>
12</body>

请注意,您正在向 <script> 标签添加一个 defer 属性,这会延迟脚本标签的执行,直到文档被解析后。

接下来,在同一个目录中创建一个名为hello.js的JavaScript文件,如index.html:

1nano hello.js

在文件中,写一些JavaScript来创建一个新的文本元素,上面写着Hello, World:

1[label hello.js]
2const el = document.createElement('h1');
3const words = "Hello, World!"
4const text = document.createTextNode(words);
5el.appendChild(text);
6
7document.body.appendChild(el);

现在你有你的脚本,你可以在浏览器中打开index.html文件. 在与你的index.html文件相同的目录中,运行npx serve。这将运行本地的 serve包,而不会下载到你的node_modules

1npx serve

该命令会询问您是否要安装一个软件包。键入y以同意:

1Need to install the following packages:
2  serve
3Ok to proceed? (y) y

当你运行命令时,你会看到一些这样的输出:

 1npx: installed 88 in 15.187s
 2
 3   ┌────────────────────────────────────────┐
 4   │                                        │
 5   │   Serving!                             │
 6   │                                        │
 7   │   Local:  http://localhost:5000        │
 8   │                                        │
 9   │   Copied local address to clipboard!   │
10   │                                        │
11   └────────────────────────────────────────┘

当你打开你的网页浏览器到 http://localhost:5000,你会看到你的代码. 您可以离开服务器运行在一个单独的标签或窗口或关闭它与 CTRL+C在预览你的代码后。

Hello, World in a browser

现在您正在浏览器中显示一个基本页面,但您尚未能够利用第三方代码和JavaScript套件。在下一步,您将动态导入代码并导入函数到脚本中,而无需构建工具。

步骤 2 — 使用 ES6 模块编写 Hello World 脚本

在此步骤中,您将编写使用外部包的代码. 您将修改代码以使用 ES 导入导入 JavaScript 代码. 最后,您将使用模块类型将代码加载到您的浏览器中,以便浏览器能够动态加载代码。

首先,打开hello.js:

1nano hello.js

你要从 lodash导入一些代码,以动态地改变你的文本。

然后在文件的顶部,从lodash导入 startCase函数,使用标准ES6的导入语法:

1[label hello.js]
2import startCase from '@lodash/startCase';
3
4const el = document.createElement('h1');
5const words = "hello, world";
6const text = document.createTextNode(words);
7el.appendChild(text);
8
9document.body.appendChild(el);

最后,用words变量作为document.createTextNode中的参数调用startCase:

1[label hello.js]
2import startCase from '@lodash/startCase';
3
4const el = document.createElement('h1');
5const words = "hello, world";
6const text = document.createTextNode(startCase(words));
7el.appendChild(text);
8
9document.body.appendChild(el);

如果你关闭了你的网页服务器,打开一个新的终端窗口或卡,然后运行npx服务器重新启动服务器,然后在网页浏览器中导航到http://localhost:5000查看更改。

当您在浏览器中预览代码时,请 打开开发者控制台

1[secondary_label Output]
2Uncaught SyntaxError: Cannot use import statement outside a module

Module Error

由于代码使用导入语句,您将需要修改index.html内部的<script>标签,以处理现在在多个文件之间分割的JavaScript。

关闭hello.js并打开index.html:

1nano index.html

若要将脚本运行为模块,请在<script>标签上的type属性值更改为module:

 1[label hello.js]
 2<!DOCTYPE html>
 3<html lang="en-US">
 4<head>
 5  <meta charset="utf-8">
 6  <meta http-equiv="X-UA-Compatible" content="IE=edge">
 7  <title>Hello World</title>
 8  <meta name="viewport" content="width=device-width, initial-scale=1">
 9  <script type="module" src="./hello.js"></script>
10</head>
11<body>
12</body>
13</html>

注意,您还删除了推迟属性,JavaScript模块将不会执行,直到页面被解析。

打开浏览器,你仍然会看到一个错误`:

1[secondary_label Output]
2Uncaught TypeError: Failed to resolve module specifier "@lodash/startCase". Relative references must start with either "/", "./", or "../"

Unknown specifier

现在问题不在于导入陈述,问题在于浏览器不知道导入陈述的含义,它正在等待在 Web 服务器上找到代码,因此它正在寻找与当前文件相关的文件,要解决这个问题,你需要一个名为导入地图的新工具。

在此步骤中,您学会了如何修改 JavaScript 代码以使用 ES 导入来加载外部库,您还修改了 HTML script 标签以处理 JavaScript 模块。

在下一步中,您将告诉浏览器如何使用导入地图来找到代码。

步骤 3 — 使用导入地图加载外部代码

在此步骤中,您将学习如何创建导入地图,以告诉您的浏览器在哪里找到外部代码。

导入地图是一个JavaScript对象,其中密钥是导入的名称(‘@lodash/startCase’),值是代码的位置。

index.html中添加一个新的script标签,其中包含importmap类型。在script标签中,创建一个 JavaScript 对象,其中包含一个imports的密钥。

 1[label hello.js]
 2<!DOCTYPE html>
 3<html lang="en-US">
 4<head>
 5  <meta charset="utf-8">
 6  <meta http-equiv="X-UA-Compatible" content="IE=edge">
 7  <title>Hello World</title>
 8  <meta name="viewport" content="width=device-width, initial-scale=1">
 9  <script type="importmap">
10    {
11      "imports": {
12      }
13    }
14  </script>
15  <script type="module" src="./hello.js"></script>
16</head>
17<body>
18</body>
19</html>

请确保您不会在对象中添加任何追踪符号,浏览器不会知道如何处理它们。

现在你有你的基本对象,你可以添加你的代码的位置. 导入地图的结构将是这样的:

1{
2    "imports": {
3        "nameOfImport": "locationOfCode",
4        "nameOfSecondImport": "secondLocation"
5    }
6}

您已经知道您的导入名称@lodash/startCase,但现在您需要找到导入地图指向的位置。

一个很好的资源是 unpkg。 Unpkg 是一个内容交付网络(CDN)为任何包在 npm. 如果你可以 npm安装一个包,你应该能够通过 unpkg 加载它. Unpkg 还包括一个浏览选项,可以帮助你找到你需要的特定文件。

要找到 startCase 代码,请在浏览器中打开 https://unpkg.com/browse/[email protected]/ 请注意 URL 中的 browse 字。这为您提供了图形的方式来浏览目录,但您不应该将路径添加到您的导入地图,因为它是 HTML 页面而不是原始 JavaScript 文件。

另外,请注意,您正在浏览lodash-es,而不是lodash。这就是以 ES 模块形式导出的lodash库,这就是您在这种情况下所需要的。

浏览代码,你会注意到有一个名为 startCase.js的文件。这个代码导入其他函数,并使用它们将每个单词的第一个字母转换为上一个字母:

 1import createCompounder from './_createCompounder.js';
 2import upperFirst from './upperFirst.js';
 3
 4/**
 5 * Converts `string` to
 6 * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage).
 7 *
 8 * @static
 9 * @memberOf _
10 * @since 3.1.0
11 * @category String
12 * @param {string} [string=''] The string to convert.
13 * @returns {string} Returns the start cased string.
14 * @example
15 *
16 * _.startCase('--foo-bar--');
17 * // => 'Foo Bar'
18 *
19 * _.startCase('fooBar');
20 * // => 'Foo Bar'
21 *
22 * _.startCase('__FOO_BAR__');
23 * // => 'FOO BAR'
24 */
25var startCase = createCompounder(function(result, word, index) {
26  return result + (index ? ' ' : '') + upperFirst(word);
27});
28
29export default startCase;

浏览器将遵循导入声明并导入所需的每个文件。

现在你已经有了导入地图的位置,更新文件导入地图以新的URL。在index.html内,添加@lodash/startCase和URL。

 1[label index.html]
 2<!DOCTYPE html>
 3<html lang="en-US">
 4<head>
 5  <meta charset="utf-8">
 6  <meta http-equiv="X-UA-Compatible" content="IE=edge">
 7  <title>Hello World</title>
 8  <meta name="viewport" content="width=device-width, initial-scale=1">
 9  <script type="importmap">
10    {
11      "imports": {
12        "@lodash/startCase": "https://unpkg.com/[email protected]/startCase.js"
13      }
14    }
15  </script>
16  <script type="module" src="./hello.js"></script>
17</head>
18<body>
19</body>
20</html>

保存文件. 更新您的浏览器,你会看到 Hello World

Working

<$>[注] 注: 导入地图尚未得到广泛支持。在最新版本的 Edge 或 Chrome 中打开代码,或查看最新版本的 支持的浏览器

您的浏览器显示Hello World,但现在发生了更有趣的事情。在浏览器中打开 浏览器控制台,选择 Inspect Elements ,然后切换到 ** 网络** 卡。

在打开网络选项卡后,刷新页面,你会看到浏览器正在动态地加载代码。

Network Tab

更重要的是,请注意,所有的代码都被懒惰地加载,这意味着浏览器不会导入任何代码,直到具体需要。例如,即使startCase位于导入地图中,并且导入地图在hello.js脚本之前被定义,但直到hello.js下载并导入代码后,它才被加载。

如果您要在导入地图中添加其他条目,浏览器就不会加载它们,因为它们从未被导入到代码中。

一个主要的问题是,导入地图尚未得到所有浏览器的完全支持,即使它们受支持,一些用户也可能无法使用受支持的浏览器,幸运的是,有不同的项目使用导入地图语法,同时添加完整的浏览器支持。

在此步骤中,您创建了一个导入地图,您还了解了如何导入模块代码,以及该代码将如何懒惰地加载到浏览器中。

步骤 4 – 使用 SystemJS 构建跨浏览器支持

在此步骤中,您将在使用 SystemJS 的所有浏览器中使用导入地图。您将导出代码作为 SystemJS 构建,以及如何设置导入地图类型以使用 SystemJS 格式。

导入地图将从应用程序中删除许多复杂的构建步骤,但尚未得到广泛的支持。

幸运的是,有一个名为SystemJS的项目可以使用创建导入地图来提供跨浏览器支持,并使用各种包构建。

lodash 图书馆方便,因为它是以 ES 格式编译的,但大多数图书馆并非如此。 许多图书馆是以其他格式导出的。 其中最常见的是 Universal Module Definition 或 UMD

一个主要的区别是,与ES导入不同,UMD构建通常将所有代码合并到一个文件中。

若要更新您的项目以使用 SystemJS 和 UMD 构建的 lodash,请先打开hello.js:

1nano hello.js

更改导入声明以直接从lodash导入startCase函数。

1[label hello.js]
2import { startCase } from 'lodash';
3
4const el = document.createElement('h1');
5const words = "hello, world";
6const text = document.createTextNode(startCase(words));
7el.appendChild(text);
8
9document.body.appendChild(el);

保存并关闭文件。

接下来,要将文件构建为 SystemJS 构建,您需要一个简单的构建步骤. 您可以使用另一个构建工具,例如 webpack,但在这个示例中,您将使用 rollup

首先,初始化项目以创建一个 package.json 文件. 添加 -y 旗帜以接受所有默认值:

1npm init -y

运行命令后,您将看到成功的输出:

 1{
 2  "name": "hello",
 3  "version": "1.0.0",
 4  "description": "",
 5  "main": "index.js",
 6  "devDependencies": {},
 7  "scripts": {
 8    "test": "echo \"Error: no test specified\" && exit 1"
 9  },
10  "keywords": [],
11  "author": "",
12  "license": "ISC",
13  "homepage": ""
14}

注意:您的输出可能略有不同,取决于您正在使用的 npm 版本。

接下来,安装rollup作为一个devDepenceny:

1npm install --save-dev rollup

过了一会儿,你会看到一个成功消息:

1+ rollup@2.56.2
2added 1 package from 1 contributor and audited 2 packages in 6.85s
3found 0 vulnerabilities

接下来,创建一个简单的构建配置,打开一个名为rollup.config.js的新文件:

1nano rollup.config.js

然后添加一个配置,将以 SystemJS 格式输出代码:

 1[label rollup.config.js]
 2export default {
 3  external: ["lodash"],
 4  input: ["hello.js"],
 5  output: [
 6    {
 7      dir: "public",
 8      format: "system",
 9      sourcemap: true
10    }
11  ]
12};

外部密钥告诉 rollup 不要在最终构建中包含任何 lodash 代码,当导入时 SystemJS 会动态地加载该代码。

输入是根文件的位置,而输出则告诉 rollup 把最终代码放在哪里,以及它应该使用的格式,在这种情况下是系统

保存并关闭文件。

现在你有一个构建步骤,你需要添加一个任务来运行它。

1nano package.json

脚本对象中,添加一个名为构建的脚本,该脚本将运行rollup -c

 1[label package.json]
 2{
 3  "name": "hello",
 4  "version": "1.0.0",
 5  "description": "",
 6  "main": "hello.js",
 7  "devDependencies": {
 8    "rollup": "^2.56.2"
 9  },
10  "scripts": {
11    "build": "rollup -c"
12  },
13  "keywords": [],
14  "author": "",
15  "license": "ISC",
16  "homepage": ""
17}

保存和关闭文件,然后运行构建命令:

1npm run build

命令将短暂运行,然后你会看到一个成功消息:

1> rollup -c
2
3hello.js → public...
4created public in 21ms

您还会看到一个名为公共的新目录,其中包含内置的文件. 如果您打开公共/hello.js,您将看到您的项目以系统格式编译。

1nano public/hello.js

该文件将看起来像这样. 它类似于hello.js 与周围的System.register 方法。 此外,lodash 位于一个阵列中. 这将告诉 SystemJS 在运行时间内加载外部库。

 1[label public/hello.js]
 2System.register(['lodash'], function () {
 3    'use strict';
 4    var startCase;
 5    return {
 6    	setters: [function (module) {
 7    		startCase = module.startCase;
 8    	}],
 9    	execute: function () {
10
11    		const el = document.createElement('h1');
12    		const words = "hello, world";
13    		const text = document.createTextNode(startCase(words));
14    		el.appendChild(text);
15
16    		document.body.appendChild(el);
17
18    	}
19    };
20});
21//# sourceMappingURL=hello.js.map

保存并关闭文件。

最后一步是更新您的index.html以处理新文件:

打开index.html

1nano index.html

首先,您需要导入 SystemJS 代码,使用一个常规的 <script> 标签,其中有 `src' 属性指向 CDN 发行版。

<script>标签放在导入地图的右下方:

 1[label index.html]
 2<!DOCTYPE html>
 3<html lang="en-US">
 4<head>
 5  <meta charset="utf-8">
 6  <meta http-equiv="X-UA-Compatible" content="IE=edge">
 7  <title>Hello World</title>
 8  <meta name="viewport" content="width=device-width, initial-scale=1">
 9  <script type="importmap">
10    {
11      "imports": {
12    	"@lodash/startCase": "https://unpkg.com/[email protected]/startCase.js
13      }
14    }
15  </script>
16  <script src="https://cdn.jsdelivr.net/npm/systemjs/dist/system.js"></script>
17  <script type="module" src="./hello.js"></script>
18</head>
19<body>
20</body>
21</html>

此格式类似于您在步骤 3 中完成的格式,但有三个更改:

  • 更新导入地图类型。 * 更新引用lodash。 * 添加引用hello.js脚本。

首先,更新类型。由于这不是导入地图的本体浏览器版本,而是systemjs版本,请将类型更改为systemjs-importmap:

 1[label index.html]
 2<!DOCTYPE html>
 3<html lang="en-US">
 4<head>
 5  <meta charset="utf-8">
 6  <meta http-equiv="X-UA-Compatible" content="IE=edge">
 7  <title>Hello World</title>
 8  <meta name="viewport" content="width=device-width, initial-scale=1">
 9  <script type="systemjs-importmap">
10    {
11      "imports": {
12    	"@lodash/startCase": "https://unpkg.com/[email protected]/startCase.js
13      }
14    }
15  </script>
16  <script src="https://cdn.jsdelivr.net/npm/systemjs/dist/system.js"></script>
17  <script type="module" src="./hello.js"></script>
18</head>
19<body>
20</body>
21</html>

接下来,更新参照,将@lodash/startCase更改为lodash。您将导入完整的库,然后更改 UMD 构建的位置,以 unpkg。

然后为你好添加一个新的条目,并将其指向公共目录中的编译版本:

 1[label index.html]
 2...
 3  <meta name="viewport" content="width=device-width, initial-scale=1">
 4  <script type="systemjs-importmap">
 5    {
 6      "imports": {
 7        "hello": "./public/hello.js",
 8        "lodash": "https://unpkg.com/[email protected]/lodash.js"
 9      }
10    }
11  </script>
12...

现在你正在导入systemJS,并更新了导入地图,剩下的只是加载模块。

将模块的script标签上的type属性更改为systemjs-module。然后将src更改为import:hello

 1[label hello.js]
 2...
 3  <script type="systemjs-importmap">
 4    {
 5      "imports": {
 6        "hello": "./public/hello.js",
 7        "lodash": "https://unpkg.com/[email protected]/lodash.js"
 8      }
 9    }
10  </script>
11  <script src="https://cdn.jsdelivr.net/npm/systemjs/dist/system.js"></script>
12  <script type="systemjs-module" src="import:hello"></script>
13</head>
14...

保存并关闭文件。

当你这样做时,浏览器会更新,你会看到 Hello World

与本地导入地图不同,这将在任何浏览器中工作. 以下是FireFox的结果:

Hello in firefox

如果你看看网络选项卡,你会看到,与导入地图一样,代码按需要懒惰地加载:

Firefox network tab

在此步骤中,您在使用 SystemJS 的浏览器中使用了导入地图,您更改了脚本以使用 lodash 的 UMD 构建,创建了一个 rollup 构建以在系统格式中输出代码,并更改了导入地图和模块类型以与 SystemJS 工作

结论

在本教程中,您使用了导入地图来动态加载JavaScript代码. 您渲染了一个应用程序,在没有任何构建步骤的情况下动态加载外部库. 然后,您创建了一个构建过程,以在SystemJS格式中生成代码,以便您可以在所有浏览器中使用导入地图。

导入地图为您提供了开始将大型项目分解为较小的独立片段的机会,称为 microfrontends.您也不需要像本教程中所学的那样限制自己对静态定义的地图;您还可以创建 动态导入地图可以从其他脚本中加载。

新的功能正在进行中,您可以在 官方规格上跟踪它们。

Published At
Categories with 技术
comments powered by Disqus