在浏览器中打包 TypeScript 系列2:在浏览器中打包 TypeScript

news2024/12/28 18:30:02

原文地址

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

第 1 部分:ES 模块和导入映射import maps

打包和转译( Bundling & Transpiling )

毫无疑问,打包和转译对于 Web 开发至关重要。在深入讨论该主题之前,让我们重申一下什么是打包和转译。

打包是将所有源文件和依赖项合并到单个文件中的过程。

转译是分析每个源文件并修改它以便所有浏览器都可以执行它的过程。这一步允许我们使用最新的 JS/TS 功能,而不必担心是否所有浏览器都支持。

Babel 是最著名的转译工具。

Webpack、Rollup 都是打包工具。

目前,需要先预先进行转译和打包,然后在浏览器中导入的打包后的文件。稍后在内容,我将展示如何在浏览器中完全进行打包和转译。

ESBuild

ESBuild是一个可以转译和打包 JavaScript/TypeScript 项目的单个可执行文件。

ESBuild 并不是唯一可以在浏览器中运行的工具。 SWC 还有一个 WASM 选项。 Rollup 也有浏览器版本。

为什么要在浏览器中打包 ?

您可能正在构建一项服务,用户可以在其中构建代码或自定义现有组件。

用例#1:代码编辑器

假设您正在构建一个在线代码编辑器。用户可以编写 TypeScript 代码并执行它。您有两个选择:A)将 TypeScript 代码发送到服务器,对其进行编译并以浏览器可以执行的 JS 代码进行响应;或 B) 在浏览器中编译 TypeScript 代码并执行它。如果可能,选项 (B) 会更容易,并且无需维护服务器池来执行不受信任的代码。

用例 #2:自定义 Web 组件

我的个人用例是 Bolik.net 服务。 Bolik 允许用户创建自定义 Web 组件。收到用户配置后,我编译 Web 组件源并生成用户可以在网站上导入的单个包。

目前 Bolik 还没有在浏览器中构建 Web 组件。不过,我将在这篇博文中表明这是可能的。

浏览器中的操作方法

与直接在计算机上运行相比,在浏览器中捆绑和打包 略有不同。虽然 WASM 可以在浏览器中运行二进制文件,但仍然存在一个主要区别:文件系统。所有工具都假设它们可以从本地文件系统读取文件。这种情况在浏览器中会并不满足。让我们看看如何规避它。

在浏览器中转译

转译(编译)代码是我们能做的最简单的步骤。 ESBuild 让它变得非常简单。我们只需要导入 ESBuild 库,初始化 WASM 模块,然后就可以转译 TypeScript 了。

const { default: esbuild } = await import("https://esm.sh/esbuild-wasm@0.18.11/");
await esbuild.initialize({
  wasmURL: "https://esm.sh/esbuild-wasm@0.18.11/esbuild.wasm",
});

const res = await esbuild.transform(`
let a: number = 2;
`, {
  loader: "ts",
});
// Prints: let a = 2;
console.log(res.code);

就像这样,我们将 TypeScript 代码转换为 JavaScript。

ESBuild wasm 模块约为 10MB,因此初始化设置确实需要一些时间。

在浏览器中打包

现在让我们尝试打包 。

const { default: esbuild } = await import("https://esm.sh/esbuild-wasm@0.18.11/");
await esbuild.initialize({
  wasmURL: "https://esm.sh/esbuild-wasm@0.18.11/esbuild.wasm",
});

const res = await esbuild.build({
  bundle: true,
  write: false,
  entryPoints: ['main.ts'],
});

这将失败,因为浏览器无权访问文件系统,因此无法读取 maint.ts 。那我们怎样才能让它发挥作用呢?

幸运的是,ESBuild 支持插件。好的,但是编写插件对我们有什么帮助呢?浏览器无法访问本地文件系统,但可以发送HTTP请求!我们的插件可以从服务器获取文件,而不是读取本地文件。

function withBrowserResolver() {
  return {
    name: "browser-resolver",
    async setup(build) {
      // Intercept import paths starting with "https://" or "http://" so
      // esbuild doesn't attempt to map them to a file system location.
      build.onResolve({ filter: /^http[s]{0,1}:\/\// }, (args) => ({
        path: args.path,
        namespace: "http-url",
      }));

      // We also want to intercept all import paths inside downloaded
      // files and resolve them against the original URL. All of these
      // files will be in the "http-url" namespace. Make sure to keep
      // the newly resolved URL in the "http-url" namespace so imports
      // inside it will also be resolved as URLs recursively.
      build.onResolve({ filter: /.*/, namespace: "http-url" }, (args) => ({
        path: new URL(args.path, args.importer).toString(),
        namespace: "http-url",
      }));

      // When a URL is loaded, we want to actually download the content
      // from the internet.
      build.onLoad({ filter: /.*/, namespace: "http-url" }, async (args) => {
        const url = new URL(args.path);
        const res = await fetch(url);
        if (!res.ok) {
          throw new Error(`Failed to fetch ${url}: status=${res.statusText}`);
        }

        const body = await res.text();
        return {
          contents: body,
          // ESBuild can't get extension from a URL so it falls back to js loader.
          loader: resolveLoader(url),
        };
      });
    },
  };
}

function resolveLoader(url) {
  if (url.pathname.endsWith(".ts")) {
    return "ts";
  }

  if (url.pathname.endsWith(".tsx")) {
    return "tsx";
  }

  return undefined;
}

现在我们可以使用它了

const { default: esbuild } = await import("https://esm.sh/esbuild-wasm@0.18.11/");
await esbuild.initialize({
  wasmURL: "https://esm.sh/esbuild-wasm@0.18.11/esbuild.wasm",
});

const res = await esbuild.build({
  bundle: true,
  write: false,
  entryPoints: [`${location.protocol}//${location.host}/bundler-demo/main.ts`],
  loader: {},
  plugins: [withBrowserResolver()],
});

console.log(res.outputFiles[0].text);

它是如何工作的?这需要解释一下。

  • 首先我们定义了一个 ESBuild 插件来支持通过 HTTP 请求获取文件。

  • 该插件会覆盖以 http:// 或 https:// 开头的每个 ES 模块导入。

  • 如果导入的模块想要加载更多文件,那么我们的插件也将能够导入这些依赖项。

  • 然后我们要求 ESBuild 打包 我们的项目。

  • 请注意,我们的入口点现在是一个 URL。我在此网站上托管了几个由 ESBuild 获取和捆绑的演示文件。

  • 在浏览器中执行

我们通常不想只是打包 代码。我们想要对其进行执行,看看它是否有效以及它是如何工作的。幸运的是,我们也可以做到这一点。正如我在第 1 部分中提到的,我们可以通过导入字符串来执行构建的模块:

// 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}!`);

您可以尝试在线 ESBuild。如下图所示:

在这里插入图片描述

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

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

相关文章

Java动态代理、反射

文章目录 动态代理调用者--->代理--->对象为什么需要代理代理的详细实现过程代码详情 反射反射概念反射中常用的方法所有代码 动态代理 调用者—>代理—>对象 动态代理就是无侵入式的给代码增加新的功能,通过接口保证后面的对象和代理需要实现同一个接…

Kubernetes教程—查看 Pod 和节点

目标 了解 Kubernetes Pod。了解 Kubernetes 节点。对已部署的应用故障排除。 Kubernetes Pod 在模块 2 中创建 Deployment 时, Kubernetes 创建了一个 Pod 来托管你的应用实例。Pod 是 Kubernetes 抽象出来的, 表示一组一个或多个应用容器(如 Docker…

Nexus2迁移升级到Nexus3

与 Nexus 2.x 相比,Nexus 3.x 为我们提供了更多实用的新特性。SonaType 官方建议我们,使用最新版本 Nexus 2.x 升级到最新版本 Nexus 3.x,并在 Nexus 升级兼容性 一文中为我们提供了各个版本 Nexus 升级到最新版本 Nexus 3.x 的流程&#xff…

opencv如何调用YOLOv5(无pytorch)

目录 一、前言 二.正文 2.1定义颜色 2.2目标检测主代码详解 2.3读取视频or图片进行检测 注意:opencv-python 本文使用的版本为4.5.2.52 一、前言 YOLO系列是one-stage且是基于深度学习的回归方法,而R-CNN、Fast-RCNN、Faster-RCNN等是two-stage且…

情人节特别定制:多种语言编写动态爱心网页(附完整代码)

写在前面案例1:HTML Three.js库案例2:HTML CSS JavaScript案例3:Python环境 Flask框架结语 写在前面 随着七夕节的临近,许多人都在寻找独特而令人难忘的方式来表达爱意。在这个数字时代,结合创意和技术&#xff0…

maven 从官网下载指定版本

1. 进入官网下载页面 Maven – Download Apache Maven 点击下图所示链接 2. 进入文件页,选择需要的版本 3. 选binaries 4. 选文件,下载即可

十亿次实验,用概率解读周易大衍筮法的奥秘

还记得封神电影里的文王占卜吗? 也就是著名的大衍筮法。 《易传》曰:大衍之数五十,其用四十有九。分而为二以象两,挂一以象三, 揲之以四以象四时,归奇于扐以象闰,五岁再闰,故再扐而…

苹果电脑怎么录屏?步骤详解,看到就是赚到

苹果电脑作为一款受欢迎的高性能设备,不仅在日常工作中发挥着重要作用,还可以用于创造内容,如录制屏幕内容。录屏功能能够帮助用户将屏幕上的活动记录成视频,方便分享、演示或存档。可是您知道苹果电脑怎么录屏吗?通过…

Lnton羚通云算力平台【PyTorch】教程:torch.nn.SiLU

torch.nn.SiLU 原型 CLASS torch.nn.SiLU(inplaceFalse) torch.nn.SiLU 是 PyTorch 深度学习框架中的一个激活函数,它代表 Sigmoid-Weighted Linear Unit(SiLU),也称为 Swish 激活函数。SiLU 激活函数在深度学习中被广泛使用&…

Unittest+Selenium模块驱动自动化测试实战

UnittestSelenium自动化测试框架使用模块驱动测试模型将冗余的代码封装成类,且基于PageObject的自动化设计模式,通过分层的方式将页面对象、操作、业务分开处理。 1、首先创建自动化测试框架的文件模块架构,创建common、base、testcase、rep…

Kafka单节点部署

🎈 作者:互联网-小啊宇 🎈 简介: CSDN 运维领域创作者、阿里云专家博主。目前从事 Kubernetes运维相关工作,擅长Linux系统运维、开源监控软件维护、Kubernetes容器技术、CI/CD持续集成、自动化运维、开源软件部署维护…

1.0的星火2.0必将燎原——图文声影PPT全测试

一、前言 大家好,勇哥又来分享AI模型了,前几天讯飞发布的星火大模型2.0迅速的进入了我们圈子里,为了有更多更好的模型分享给大家,分享星火大模型2.0是必须做的,我做一个传递着,希望大家也星火相传啊。 我…

iPhone备忘录删除了怎么恢复?3个妙招教你快速复原

【为了清理手机内存,一键清空了备忘录,突然想起有很多重要的笔记还存在里面,有什么办法能还原回来吗?】 备忘录是一个非常实用的工具,能够帮助大家记录各种各样的信息,并提醒大家按时完成任务,…

19.Helm

文章目录 Helm简介三个概念版本部署HelmHelm命令Helm 自定义模板基于原有的软件包进行修改自建软件包软件包升级软件包升级和创建ingress回滚版本 仓库关联部署harbor安装 push 插件 总结 Helm 简介 Helm本质就是让K8s的应用管理(Deployment、Service等&#xff09…

前端打开弹窗时将链接转化为二维码

qrcodejs2 1.安装qrcodejs2 2.在使用页面中引入 import QRCode from "qrcodejs2";3.在组件中注册(Vue2项目) components: {QRCode,}, 4.在data中定义qrcode,以及方法中使用 showCode(row) {this.dialogVisible true;this.$nextTick(() > { 需要n…

Docker+Jmeter+InfluxDB+Grafana 搭建性能监控平台

当今互联网发展迅速,应用程序的性能监控显得越来越重要。 DockerJmeterInfluxDBGrafana 是一种常用的性能监控平台,可以帮助开发者快速搭建一套可靠的监控体系。在本文中,我们将介绍如何使用这些工具搭建性能监控平台,以便开发人…

R语言处理缺失数据(1)-mice

#清空 rm(listls()) gc()###生成模拟数据### #生成100个随机数 library(magrittr) set.seed(1) asd<-rnorm(100, mean 60, sd 10) %>% round #平均60&#xff0c;标准差10 #将10个数随机替换为NA NA_positions <- sample(1:100, 10) asd[NA_positions] <- NA #转…

哈夫曼树介绍及Java实现

哈夫曼树 1. 介绍1.1 哈夫曼树1.2 路径、路径长度、结点的权、结点的带权路径长度1.3 树的带权路径长度WPL 2. 哈夫曼树构建步骤3. 代码实现 1. 介绍 1.1 哈夫曼树 哈夫曼树-最优二叉树&#xff1a;树的带权路径长度最小的二叉树&#xff1b;权值均为叶子结点&#xff1b;权值…

云计算与边缘计算:加速数字化转型的关键驱动力

云计算和边缘计算技术正以惊人的速度改变着企业的业务和基础架构。这些先进的技术为企业带来了灵活性、可扩展性和成本效益的优势&#xff0c;重新定义了业务运作的方式。 云计算是通过互联网将计算资源提供给用户的一种服务模式。通过云计算&#xff0c;企业可以将应用程序、…

Vulkan基础

目录 一、Vulkan开发理论基础知识 接口设计理念 Host&Device 基础设施——元数据和设备 基础设施——交换链 ​编辑交换链 SwapChain​编辑 渲染管线 Pipeline RenderPass CommandBuffer 二、Vulkan DescriptorSet 创建DescriptorPool 运行时绑定DescriptorSet 三…