在浏览器中打包 TypeScript 系列1:ES 模块和导入映射Import map

news2024/9/28 11:11:53

原文地址

这是“在浏览器中打包 TypeScript 系列”的第 1 部分。

第 2 部分:在浏览器中打包 TypeScript

JS打包简史

让我们绕个小弯,看看在使用 ES 模块之前是如何使用 JS 的。 (年份为近似值)

1. 黑暗时代(2010年之前)

只要有 JavaScript,就可以通过脚本标签引入 JavaScript。您只需导入一堆会污染全局名称空间的脚本,并希望所有脚本都已加载。

2、黎明曙光(2010-2018)

随着 2009 年 Node.js 的首次发布,事情开始变得更加光明,JS 社区也变得更加活跃。大约在同一时间,我们看到了几个标志性项目,例如 RequireJs 和 browserify。

RequireJS 是一个在浏览器中异步导入模块的库。 Browserify 是类似 Node.js (CommonJS) 模块的 JS 打包器。 Browserify 带来了很多实用工具,比如 uglify、reactify、sassify、babelify、likify-this-post 等。

下边是过去定义模块的稍微简化的方法:

if(typeof define === "function" && define.amd) {
  // RequireJS module
  define("jquery", [], function() {
    return jQuery;
  });
} else if (typeof module === "object" && typeof module.exports === "object") {
  // CommonJS module (Node.js / Browserify)
  module.exports = jQuery;
} else {
  // Export to global namespace
  window.jQuery = jQuery;
}

是的,这是很多步骤,每个库都必须提供一个支持不同导出机制的包。

3.王者时代(2018-2022)

Webpack 于 2014 年发布,但花了一些时间才获得关注。但一旦它成功了,它就成为了 JS 项目的唯一构建工具。 Webpack 引入了加载器的概念。

loader 是一个 Webpack 插件,支持导入非 JS 文件。例如,如果你想导入 CSS,你需要一个 css-loader。

Webpack 允许您自定义很多东西,最终它成为问题和投诉的主要来源。人们可以弄清楚如何使用它正确配置构建管道。然后,随着每个主要版本的 API 发生变化,因此每个加载的内容都需要由其作者进行更新,这反过来又修改了加载器的 API,因此陷入了无底的兔子洞……

React 团队尝试通过 create-react-app 项目“改善”这种情况。很多人似乎都喜欢这个项目。我不是他们中的一员。 create-react-app 将 Webpack 隐藏在干净的界面后面。他们的 webpack 配置文件几乎有 800 行长!这对我来说太复杂了,我总是更喜欢手动编写 webpack 配置文件来仅配置我需要的东西。

Webpack 解决了很多问题,例如打包、使用插件、导入 CSS 等。但随着时间的推移,它的缓慢性和错误配置加载器的挫败感已经变得太多了。幸运的是,每年都有新项目不断出现,让生活变得更加简单。

4. 未来已来(2022年-)

顺便说一句,现在是 2023 年!这本质上意味着未来已经开始。

事实证明,Node.js 速度慢,不像本机代码那么快。所以出现了 ESBuild(用 Go 编写)和 swc(用 Rust 编写)可以让我们不需要等待 JS 构建完成几秒钟。这可以在几毫秒内完成。

Vite 和 Turbo 等项目使用混合方法。他们为工作选择合适的工具。

需要说明的是我没有写过关于Rollup的内容

ES 模块

我们非常习惯使用 JS 打包工具、转译器等来构建我们的 UI 项目。但如今我们真的需要这些工具带来的额外复杂性吗?我们可以只导入我们编写的代码吗?

答案是:“这取决于”。

It Depends book

首先让我们弄清楚如何导入代码。现在所有浏览器都支持 ES 模块。 JavaScript 中管理模块的标准机制。

如果您想了解详细信息,我强烈推荐这个精彩的解释。出于本博文的目的,我将仅讨论使用 ES 模块。

让我们深入研究代码!让我们从定义一个模块开始:

// name.js
export const name = 'James Bond';

这很简单。我们有一个名为 name.js 的文件,它导出一个名为 name 的变量。这个文件本身并不是很有用。实际的好处来自于我们可以导入这个文件。只要导入的 URL 相同,浏览器就会缓存该模块并重用它。

// main.js
import { name } from './name.js';
console.log(`Hi from JS ${name}!`);
<!-- index.html -->
<html>
  <head>
    <title>Sample page</title>
    <script type="module">
      import { name } from './name.js';
      console.log(`Hi from HTML ${name}!`);
    </script>
    <script type="module" src="./main.js"></script>
  </head>
</html>

我们的 ES 模块可以有一段代码,该代码将在模块第一次导入时执行。浏览器控制台将打印两条信息:

Hi from JS James Bond!
Hi from HTML James Bond!

在上面的示例中,我们导入了 name.js 模块两次:从 main.js 和 index.html 导入。浏览器构建一个模块导入 URL(例如 http://localhost/name.js )并缓存它。由于这种缓存机制, name.js 被下载并执行一次。 URL 中的查询参数将使模块变得唯一。

导入依赖项

如果您的项目很小并且没有外部依赖项,那么直接使用 ES 模块是一个很好的起点。

让我们看看如果添加单个依赖项会发生什么。 Lodash 提供了我经常使用的方便的 debounce 实现。将此导入片段粘贴到浏览器控制台中:

const { default: debounce } = await import('https://unpkg.com/lodash-es@4.17.21/debounce.js');

单个函数导入可获取 14 个文件!这就是问题开始出现的地方。幸运的是,unpkg.com 不是我们唯一的选择。

await import('https://esm.sh/lodash-es@4.17.21/debounce.js');

Esm.sh 将模块打包到单个文件中。此导入将请求数量减少到 2。如果我们指定直接导入 URL,我们可以将请求计数减少到 1。

Import map

现在我们已经了解了基础知识,让我们来谈谈外部依赖项。现有的打包程序要么导入相对文件,要么从 node_modules 目录导入依赖项。如果未找到文件或依赖项,则构建过程中会失败。

然而,在浏览器中,我们没有构建阶段。网络已经通过 URL 解决了这个问题。让我们回到主模块并假设我们从 http://localhost 下载它。

// main.js
import { name } from './name.js';
// OR import { name } from 'http://localhost/name.js';
console.log(`Hi from JS ${name}!`);

浏览器构建一个模块 URL http://localhost/main.js 并相对 http://localhost/name.js 导入 name.js 。

从 ES 模块导入时,必须使用相对路径或完整 URL。

那么第三方依赖怎么样?

遗憾的是,这里没有灵丹妙药。您可以供应商依赖项并自行托管它们,也可以从 CDN 提供商(如 esm.sh、jspm、unpkg 或 skypack)导入它们。

我们可以通过相对路径或完整的 URL 导入。这是否意味着每次我们想要导入 lodash 或其他库时,我们都需要使用 https://esm.sh/lodash-es@4.17.21 怪物?不!这正是Import map 要解决的问题。

<script type="importmap">
{
  "imports": {
    "lodash-es": "https://esm.sh/v124/lodash-es@4.17.21"
  }
}
</script>

如果您在任何导入之前包含上述importmap,那么您可以自由使用 lodash-es 。

Import maps不仅允许您为导入添加别名,还允许您覆盖依赖项的导入。

请注意,Import maps仅供应用程序使用。Import maps不能嵌套。如果您正在开发一个库,那么您将需要使用不同的机制来管理依赖项。 (查看 Deno 的推荐)

动态导入

模块可以静态和动态导入。静态导入必须位于文件的开头。它们始终被解析,并且导入路径中不能有任何变量。另一方面,动态导入允许我们在导入路径中选择任何策略。静态导入是一个语句,而动态导入是一个返回 Promise 的函数。

// main.js
const { name } = await import('./name.js');
console.log(`Hi from JS ${name}!`);

从字符串导入

事实证明,我们甚至可以从字符串导入模块。 (请随意将下面的代码片段粘贴到您的浏览器控制台中)

// Define our module
const code = `export const name = 'James Bond';`;
// Create a URL object
const blob = new Blob([code], { type: "text/javascript" });
const url = URL.createObjectURL(blob);
// Import
const module = import(url);
URL.revokeObjectURL(url); // Garbage collect

// Use imported module
const { name } = await module;
console.log(`Hi from JS ${name}!`);

TypeScript

在我们开始使用 TypeScript 之前,ES 模块在浏览器中工作得很好。遗憾的是,浏览器目前无法执行 TypeScript。幸运的是,这个问题并不像听起来那么复杂。当浏览器导入模块时,它会向服务器发送一个简单的 GET HTTP 请求。然而,服务器可以即时转译 TypeScript 代码,并以浏览器可以解析的 JavaScript 文件进行响应。

// Transpile our TS file
const body = await Deno.readTextFile(filePath);
const res = await esbuild.transform(
  body,
  {
    loader: "ts",
  },
);
// Now we can respond with a JS file

我将在下一篇博客文章中详细介绍 ESBuild。

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

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

相关文章

SPSS--如何使用分层分析以及分层分析案例分享

分层分析&#xff1a;将资料按某个或某些需要控制的变量的不同分类进行分层&#xff0c;然后再估计暴露因子与某结局变量之间关系的一种资料分析方法。 分层分析的最重要的用途是评估和控制混杂因子所致的混杂偏倚。通过按混杂因子分层&#xff0c;可使每层内的两个比较组在所控…

七夕福利来袭:多种表白代码/语录超级赠送,不信你没女朋友

前言 马上七夕了~又是牛郎织女相会的一天&#xff01; 不管什么时候&#xff0c; 这都是一个特别的日子&#xff0c; 在这个充满幸福的日子里&#xff0c; 我要把最美好的祝福&#xff0c; 送给心里有我的每一个人&#xff1b; 祝愿大家&#xff1a; 一生平安&#xff0c…

兼具传统和新锐基因的极氪,是怎么做用户运营的?|新能源车专题研究

主笔&#xff1a;浣芳黛 出品&#xff1a;增长黑盒研究组 近几个月来&#xff0c;新能源车势头强劲&#xff0c;众多车企纷纷传出连月增长和再创新高的捷报&#xff0c;在当下整体经济复苏缓慢的映衬下&#xff0c;显得格外耀眼。 于是&#xff0c;增长黑盒近期针对新能源车企展…

Ganache的安装与设置连接

文章目录 前言1. 安装Ganache2. 安装cpolar3. 创建公网地址4. 公网访问连接5. 固定公网地址 前言 Ganache 是DApp的测试网络&#xff0c;提供图形化界面&#xff0c;log日志等&#xff1b;智能合约部署时需要连接测试网络。 Ganache 是一个运行在本地测试的网络,通过结合cpol…

docker 06(docker compose)

一、服务编排 二、docker compose

【Mybatis源码分析】解析语句标签_Select|Update|Insert|Delete

解析语句标签 Select|Update|Insert|Delete 一、前言二、语句标签的源码分析三、sql 标签的解析四、总结 一、前言 在阐述解析语句标签之前&#xff0c;得先知道我们的语句标签内容最后被封装到Configuration哪&#xff1f;&#xff08;都应该知道 Mybatis 通过的是 XMLConfig…

骨传导耳机是如何让我们听到声音的?为什么要选择骨传导耳机?

骨传导耳机的工作原理就是通过人的颅骨、骨迷路、螺旋器、听觉中枢来传递声波&#xff0c;不需要接触到人的外耳道和内耳膜&#xff0c;省去了许多声波传递的步骤&#xff0c;相对于入耳式耳机会更加的保护耳朵。 说简单一点&#xff0c;平时我们自己挠头发或者通过上次碰撞牙…

交换机生成树STP

生成树协议&#xff08;spanning-tree-protocol,stp&#xff09;&#xff1a;在具有物理环路的交换机网络上生成没有回路的逻辑网络的方法&#xff0c;生成树协议使用生成树算法&#xff0c;在一个具有冗余路径的容错网络中计算出一个无环路的路径&#xff0c;使一部分端口处于…

Blazor Session设置

文章目录 前言SessionProtectedSessionStorage 类信息加密使用场景 代码部分Nuget扩展安装源码使用&#xff0c; 相关资料 前言 微软官方封装了一些浏览器的操作&#xff0c;其中就有Session的操作的封装 ProtectedSessionStorage 微软文档 因为我们知道&#xff0c;依赖注入…

每个.NET开发都应掌握的C#委托事件知识点

上篇文章讲述了C#接口的知识点&#xff0c;本文将介绍C#委托事件知识点。C#作为.NET开发的核心语言之一&#xff0c;提供了丰富的特性来支持面向对象编程和事件驱动的模型。其中&#xff0c;委托和事件是C#中不可或缺的关键概念&#xff0c;每个.NET开发者都应该深入理解它们的…

什么是原码、反码和补码

什么是原码、反码和补码 文章目录 什么是原码、反码和补码1、机器数2、原码3、反码4、补码5、总结 1、机器数 前言 一个数在计算机中的表示形式是二进制的话&#xff0c;这个数其实就叫机器数。 机器数通常是带有符号的&#xff08;指有正数和负数之分&#xff09;&#xff0c;…

【后端速成 Vue】第一个 Vue 程序

1、为什么要学习 Vue&#xff1f; 为什么使用 Vue? 回想之前&#xff0c;前后端交互的时候&#xff0c;前端收到后端响应的数据&#xff0c;接着将数据渲染到页面上&#xff0c;之前使用的是 JavaScript 或者 基于 JavaScript 的 Jquery&#xff0c;但是这两个用起来还是不太…

C++新经典08--范围for、new内存动态分配与nullptr

范围for语句 C语言部分学习过了for语句&#xff0c;在C11中for语句的能力被进一步扩展&#xff0c;引入了范围for语句&#xff0c;用于遍历一个序列。看看如下范例&#xff1a; int v[]{12,13,14,16,18};//数组ⅴ中每个元素依次放入x并打印x值。相当于把ⅴ的每个元素值复制到x…

第11步---MySQL的优化

第11步---MySQL的优化 1.概念 原先写功能。后来对平静进行优化 设计 查询语句 索引 存储 2.查看执行效率 -- 查看当前会话sql得执行类型得统计信息SHOW session STATUS like Com%上面展示得信息就是统计了当前会话得执行得操作得次数。 -- 查看全局得 SHOW GLOBAL STATU…

【C++入门到精通】C++入门 —— deque(STL)

阅读导航 前言一、deque简介1. 概念2. 特点 二、deque使用1. 基本操作&#xff08;增、删、查、改&#xff09;2. 底层结构 三、deque的缺陷四、 为什么选择deque作为stack和queue的底层默认容器总结温馨提示 前言 文章绑定了VS平台下std::deque的源码&#xff0c;大家可以下载…

SOLIDWORKS中一些不常用却很实用的功能介绍

1.过滤 FeatureManager 设计树 我们可以在FeatureManager 设计树过滤器中搜索特定的零件特征和装配体零部件。 2.添加文件夹和子文件夹 在零件或装配体文件中&#xff0c;您可添加文件夹到 FeatureManager 设计树内。 您可重新命名新的文件夹并将额外项目拖动到新的文件夹中。…

(一)Dubbo源码解析:增强SPI

〇、前言 在Dubbo的架构设计中&#xff0c;如何可以通过“类插拔”的方式&#xff0c;对其功能进行灵活的扩展或者削弱&#xff0c;那么&#xff0c;SPI起到了极其关键的作用。本篇文章作为分析Dubbo源码的第一篇文章&#xff0c;我们先暂时放下“服务注册发布流程”、“服务启…

Linux:shell脚本:基础使用(6)《正则表达式-awk工具》

简介 awk是行处理器: 相比较屏幕处理的优点&#xff0c;在处理庞大文件时不会出现内存溢出或是处理缓慢的问题&#xff0c;通常用来格式化文本信息 awk处理过程: 依次对每一行进行处理&#xff0c;然后输出 1&#xff09;awk命令会逐行读取文件的内容进行处理 2&#xff09;a…

攻防世界-disabled_button

原题解题思路 看页面源码 把这个删了就行

ESP32-C3 手动启用 Secure Boot V2 与 Flash 加密流程

ESP-IDF 中 flash 加密可以在 bootloader 阶段自动启用&#xff0c;但是这需要设备自加密后重启一次&#xff0c;为了节省这次重启的步骤&#xff0c;你可以选择通过一些脚本工具在外部启用 flash 加密。 本篇文档用于介绍 ESP32-C3 手动启用 Secure Boot V2 与 Flash 加密的操…