如何将 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
然后使用 importmap
将 big-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>
我有以下 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
:
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'
(atforeign.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
然后使用 importmap
将 big-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>