如何将 BigInteger.min.js 加载到浏览器以便 Data.BigInt 找到它?

How to load BigInteger.min.js into browser so that Data.BigInt finds it?

我有以下 PureScript 程序,它在日志中打印 BigInt

module Main where

import Prelude
import Effect
import Effect.Console

import Data.Integral (fromIntegral)
import Data.BigInt

main :: Effect Unit
main = log $ show (fromIntegral 0x42 :: BigInt)

我想通过浏览器使用它。所以我制作了以下 HTML 文件:

<!doctype html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>

<body>
  <script src="index.js" type="module"></script>
</body>
</html>

index.js的内容是:

import * as Main from '../output/Main/index';

console.log("Before");
Main.main();
console.log("After");

失败是因为 Data.BigInt 将 FFI 用于名为 big-integer:

的 Javascript 模块

Uncaught TypeError: Failed to resolve module specifier "big-integer". Relative references must start with either "/", "./", or "../".

如何将 BigInteger.min.js 加载到 "big-integer" 模块说明符中?

到目前为止我尝试了什么

我尝试通过在 index.js 中手动导入来加载 BigInteger.min.js:

import * as Main from '../output/Main/index';
import * as BigInteger from './BigInteger.min.js';

console.log("Before");
Main.main();
console.log("After");

但这里的问题是我无法将其导入为 big-integer,只能导入 BigInteger,因为前者在词法上无效。

我也试过不合格的导入:

import {bigInt} from './BigInteger.min.js';

但这也不起作用(关于缺少 "big-integer" 模块的相同错误)。

我也厌倦了直接加载 BigInteger.minjs 作为 JS 源文件:

<body>
  <script src="BigInteger.min.js"></script>
  <script src="index.js" type="module"></script>
</body>

但还是一样的问题。

我还尝试将导入映射添加到我的 HTML 文件中:

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <script type="importmap">
    {
        "imports": {
            "big-integer": "./BigInteger.min.js"
        }
    }
</script>
</head>

然后失败:

Uncaught SyntaxError: The requested module 'big-integer' does not provide an export named 'default' (at foreign.js:3:8)

我不确定 BigInteger.min.js 文件来自哪里,但根据它的名称和您尝试使用它的方式,我假设它是一个专门准备用于是一个“通用模块”——即一个可以在任一节点中加载的模块,或者在 UMD/AMD 加载器下,或者在浏览器中。也许 this is the link 你是从哪里得到的?

这个包在浏览器中的工作方式是 old-school:它创建一个名为 bigInt 的全局变量,然后任何想使用它的人都可以访问它。要检查这一点,请尝试加载您的页面,打开 Dev Tools -> Console,然后输入 window.bigInt。有定义吗?

问题是,包括 PureScript 在内的大多数现代工具不再适用于那种界面。相反,每个人都转向 ES 模块,那里没有任何类型的全局变量,并且每个模块都可以通过名称导入其他模块。这是您尝试通过 import {bigInt} from './BigInteger.min.js' 执行的操作,但失败了,因为 BigInteger.min.js 不是 ES 模块。

至于如何解决这个问题 - 你有选择。


选项0:(误启动)安装Node模块

通常在这种情况下,人们会使用 npm install big-integer 在 Node 中安装模块,然后从那里引用它:

<script type="importmap">
  {
    "imports": {
      "big-integer": "../node_modules/big-integer/index.js"
    }
  }
</script>

但是,该特定包的问题在于,即使安装在 Node 中,它也不是 ES 模块。该包从一开始就被编写为 non-ES 模块,因此它永远不能作为 ES 导入。

在 Node 中这会工作得很好,因为 Node 可以同时使用 ES 和 CommonJS 模块,所以它只会加载 big-integer 包 old-style 和 运行。当然,它的效率有点低,但它确实有效。

但不是在浏览器中。现代浏览器在面对 import ... from ... 构造时,期望目标是 ES 模块,如果不是,那就是那样。


选项 1:存根

该包在您的浏览器中作为全局变量可用,唯一的问题是 ES 导入系统无法那样访问它。所以我们可以帮助它一点。创建一个新模块,比如 big-int-stub.js,并从中导出全局变量:

// big-int-stub.js
export default window.bigInt

然后使用 importmapbig-integer 重新映射到该存根:

<script type="importmap">
  {
    "imports": {
      "big-integer": "./big-int-stub.js"
    }
  }
</script>
<script src="./BigInteger.min.js"></script>

<script type="module">
  import bigInt from "big-integer"
  console.log(bigInt)
</script>

一个问题是,如果您希望能够导入默认导出以外的内容,例如import { min, max } from "big-integer",您还必须在导出中枚举它们中的每一个:

export default window.bigInt
export const min = window.bigInt.min
export const max = window.bigInt.max
...

另一个问题 - 不是包本身,而是更普遍的问题 - 是你要求浏览器在你的页面上加载很多小文件:首先 index.js,然后从那里 - output/Main/index.js,然后是它导入的所有内容,然后是那些导入的所有内容,依此类推。这对于玩具项目和练习来说是可以的,但对于任何实际应用来说,这很可能是不可接受的。


选项 2:捆绑

与其直接在您的页面中包含 index.js,不如先将其捆绑,然后再包含该捆绑包。

“捆绑”的想法是获取您的“根”模块,然后获取它导入的所有内容,以及那些导入的所有内容,依此类推,- 然后将它们组合在一个大的 JS 中根本不导入任何内容但完全 self-contained 的文件具有 运行 所需的所有代码。这样的组合文件称为“bundle”。以及执行此组合过程的程序 - “bundler”。

这种方法的优点很多:这不仅意味着浏览器只需要加载一个文件而不是几十个,而且一个好的打包器(尤其是 ES 模块)将能够包含不 所有代码,但只有那些实际可能被引用的函数和变量。这叫做“死代码消除”,如果你想听起来很酷,也可以称为“tree shaking”。

但是针对您的特定情况的关键优势是捆绑器还可以识别 CommonJS 模块,这意味着它可以将 big-integer 包合并到捆绑包中,即使它不是 ES 模块。

首先,给自己安装一个捆绑器。有许多不同的东西——历史上最流行的是 WebPack,但我个人(以及 PureScript 社区)最喜欢 esbuild。它的速度快了几个数量级,而且它的 API 实际上是理智的。要安装它,请使用 npm:

npm install esbuild

之后,只需将您的 index.js 交给它并要求它捆绑即可。指定输出文件名通常也是一个好主意:

esbuild --bundle ./index.js --outfile=./bundle.js

这将生成 bundle.js 文件,您可以将其包含在页面中:

<script src="bundle.js" type="module"></script>