这个答案是在考虑到在 Web 项目中使用 MathJax 的情况下编写的。如果您不是这种情况,那么其中大部分仍然适用,因为它与您明确使用的 Typescript 相关。
1。 MathJax 旨在在浏览器中下载
无论今天的情况如何,MathJax 历史上都是在浏览器中下载的,不应该像其他 npm 包一样导入到针对 Web 的项目中。在Installation and usehttps://www.npmjs.com/package/mathjax 下,您可以看到,如果您正在制作后端节点应用程序,则只能在这样的项目中使用 MathJax,否则应该使用脚本将 MathJax 导入前端。您可以将自己的 MathJax 副本作为静态文件资源托管在您的 Web 服务器上,并从前端的 script 标记中加载它(很像任何 CDN),但您不应该像您一样将它构建到前端通过将常规 npm 包导入到源文件中。为什么这很重要?因为它会影响 MathJax 的历史和今天的组织方式,进而影响它在 Typescript 项目中的处理方式。
2。 MathJax 包
MathJax 有两个版本,MathJax 2 和 3。2 在内部和语法上与版本 3 有很大不同,但即使建议使用版本 3,仍然使用它。来自 npmjs.org 的包 mathjax 是 MathJax 3 包。如果你去 MathJax Github repo,你可以下载旧的包,但我不认为它们可以作为 npm 包使用。在Server integration 下的official MathJax page 上,您可以阅读有关如何在后端下载 MathJax 的信息。您还可以看到有两个 npm 包:mathjax 和 mathjax-full,后者包含“完整源代码”。 MathJax 是使用 Typescript 支持编写的,因此我们可以预期存在类型。如果您访问这些包(https://www.npmjs.com/package/mathjax 和 https://www.npmjs.com/package/mathjax-full)的 npmjs.org 条目,您会看到 mathjax-full 包含 mathjax 不包含的类型。这也可以通过期待 node_modules 中下载的包来看到。
那么@types/mathjax 是什么包?好吧,经过检查,我们可以看到它连接到 MathJax 版本 2,因为它的类型声明涉及使用仅在版本 2 中使用的MathJax.Hub。还有它的index.d.ts,没有更具体的类型的入口点表示比
import mathjax from "mathjax"
或
import * as mathjax from "mathjax"
声明一个名称空间,其中包含导出
declare namespace MathJax {
export ...
export ...
}
命名空间在 Javascript 中用于将代码组织成不同的不相交的组件,这是一种较早的模块替代方案,模块在没有命名空间的情况下执行相同的操作。在 Typescript 文档 (https://www.typescriptlang.org/docs/handbook/modules.html) 中,字面意思是 In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module。包 @types/mathjax 没有,这导致它不是编译器报告的模块。
因此,除了 @types/mathjax 由于版本不同而没有连接到 npm 包 mathjax(我知道这是一个糟糕的组织)这一事实之外,它也不支持现代模块语法。我会说它也很旧,但它最近发布了,所以坦率地说,我不知道它是关于什么的,也许它与 MathJax 通常是如何导入的或者它应该在其他一些非常特殊的情况下工作有关情况。我所知道的是,根据 Typescript 文档,此类型包不被视为模块(如您的编译器所示),其类型与 MathJax 版本 3 不一致,例如我绝对不认为这个包和它应该支持的包之间有官方关系。
所以给定mathjax-full 我们有类型,对吧?是的,确实如此,但mathjax-full 中的文件组织不适合您的用例。首先,它缺少索引,因此没有合理的入口点可用于前面描述的那种import 语句(如前所述,当没有针对特定资源时,自然入口点是索引文件)。包含的package.json 既不包含包信息也不包含有关类型的信息(您可以在node_modules 中自行检查)。即使我尝试在没有 Typescript 的情况下使用它,由于未定义有关 require 的错误,它也不起作用。总而言之,这个包并不打算像您使用的普通 npm 包那样使用。它应该用于那些希望使用 MathJax 源代码的人,绝对不能用于“普通用户构建网络应用程序”用例。对于这些,MathJax 应该用作一个单独的黑盒项目,可以通过脚本导入访问(同样,由于历史原因......这是 15 年前左右的情况)。
3。如何使其在您的用例中发挥作用
为了让它在你的用例中工作,我的意思是让你的编译器接受你的导入(没有别的),你可以做一些事情。我这样说是因为我对 Typescript 在这里的工作方式也很感兴趣。
对于@types/mathjax 包,您可以在node_modules 中进入其index.d.ts 并在底部(顶层)添加export default MathJax。现在该文件有一个全局导出,因此导入语句如
import * as mathjax from "mathjax"
import mathjax from "mathjax"
不会导致您之前看到的投诉。但由于我之前简要描述的require 错误,它仍然无法正常工作。
以mathjax-full为例,通过查看node_modules中的代码,你可以看到js子目录下的源代码中确实有Typescript文件。但是由于不存在索引(也明确提示不应通过导入在 Web 项目中使用该包),默认导入将不起作用(编译器会抱怨类型)。但是,如果您以特定资产为目标,例如
import { mathjax } from "mathjax-full/js/mathjax"
它会找到相应的类型,你不会得到错误。你实际上得到了某种 MathJax 对象,但我不知道它是否会一直工作(如果你想试试)。此外,我怀疑你会在你的包中得到很多额外的不需要的文件(或者必须花费大量时间来自定义 tree-shaking),因为 MathJax 用于确定它在使用时需要哪些文件,并且可能很难打包器在构建时确定这一点。
4。那么你应该如何真正解决这个问题呢?
最好的方法是为 MathJax 使用 React 包。在这里,我将推荐我自己的包better-react-mathjax (https://www.npmjs.com/package/better-react-mathjax),但也有其他的。此类软件包试图弥合 MathJax 和 React 之间的差距,就像您现在正在体验的那样。
如果你想以更复杂的方式来做,你可以使用 MathJax 包中的源代码并将它们作为静态文件资源的一部分包含在内,然后在 React 中注入一个script 标记来获取文件MathJax 从您的静态后端启动脚本。如果您不想托管自己的副本,也可以这样做并从 CDN 获取相同的文件。
如果您确实需要在后端使用 MathJax,我会调查 mathjax-full 包并尝试将前面提到的官方 MathJax 文档中有关在 Node 中使用它的说明转换为您的用例,使用显式导入而不是默认的以获得类型支持。
建议
我代表您发现对代码组织和模块(可能是错误的)有一定的兴趣,这是一个复杂的主题。尝试编写自己的包并通过 npm 分发,以了解 npm、Node、Typescript、ecmascript 模块、commonjs 模块等所扮演的角色。了解不同的模块格式以及如何使用 esModuleInterop 和 allowSyntheticDefaultImports 等功能在 Typescript 项目中进行交互也是重要的主题,通过分发自己的包,您将真正了解源代码在项目,以及包中的源代码。