如果您不使用 npm 说明符,那您就错了
在 Deno 早期,我们建议通过 HTTP 方式,使用 esm.sh 和 unpkg.com 等转译服务导入 npm 包。然而,这种导入 npm 包的方式存在局限性,例如缺乏安装钩子(install hooks)、重复依赖解析问题、数据文件加载等。这就是为什么在 Deno 2 发布并添加原生 npm 支持后,我们推荐直接使用 npm:
说明符。
// ❌
import React from "https://esm.sh/react@19";
// ✅
import React from "npm:react@19";
在这篇博客文章中,我们将讨论通过这些转译服务使用 npm 的局限性,以及原生导入 npm 包的所有好处。
🚨️ 立即试用 Deno 2。 🚨️
Deno 提供与 Node/npm 的向后兼容性、内置包管理、一体化零配置工具链、原生 TypeScript 支持等。
托管转译服务的局限性
重复依赖问题
当你导入 https://esm.sh/react
和 https://esm.sh/react-dom
时,后者可能导入了重复的 react
版本。
import react from "https://esm.sh/react";
import reactDom from "https://esm.sh/react-dom"; // <-- no guarantee that this is the same react version
尽管有一些方法可以通过特殊的 esm.sh URL 标志来管理,但这可能很繁琐和棘手。从 esm.sh 导入缺乏语义化版本控制,这使得依赖去重变得困难。
相反,通过 npm:
说明符原生导入 npm 包,Deno 能够理解你依赖项的语义化版本。这就像使用 npm 与 Node 一样,并且会按预期工作。这意味着更小的模块图和更少的加载模块数量。
无安装钩子和原生插件
某些 npm 包要求在安装时编译原生插件。当使用 npm 原生导入时,安装钩子会运行一个安装脚本,调用 node-gyp
来构建插件。
遗憾的是,通过 HTTP 导入 npm 包时没有安装钩子,因此一些需要单独安装步骤的 npm 包无法完全安装。
Deno 的原生 npm 支持允许安装钩子使用 --allow-scripts
标志运行。
deno install --allow-scripts=npm:duckdb
无数据文件
某些 npm 包附带非 JavaScript 文件,例如文本文件、CSV、JSON 等,这些文件将在运行时加载。然而,这些转译服务无法提供兼容版本,导致异常错误和整体欠佳的开发者体验。
在 Deno 中原生导入 npm 包与在 Node 中导入相同——数据文件会被下载并在运行时从模块中访问,这是预期的行为。
原生导入 npm 包的优势
node_modules
无 编程是一场与复杂性作斗争的战役。任何多余的代码、配置、文件夹、进程等都可能将注意力从关键业务逻辑上分散开来。这就是 Deno 零配置(并提供合理默认值)且拥有完整的内置工具链的原因,这样你就可以直接投入到编写代码中。
你可以使用 Deno 的 npm:
说明符来安装 npm 包,而无需在你的目录中创建 node_modules
文件夹。这是因为 Deno 会将你的 npm 包安装到你的全局缓存中。
你甚至可以在 deno.json
中使用 npm:
说明符。
请注意,如果存在 package.json
文件,Deno 将自动默认创建 node_modules
文件夹,因为许多 npm 包预期并需要它。但是,你可以通过 deno.json
中的 nodeModulesDir
属性来控制是否创建 node_modules
。
package.json
无 有时你只想编写一个简单的 JavaScript 或 TypeScript 程序,并直接运行和分享它,而无需额外的代码或步骤。
在 Node 中,package.json
是依赖清单文件,如果你的程序有任何依赖,它就是必需的。如果你想与他人分享你的程序,他们将需要 package.json
,并且在运行你的程序之前,还需要额外一步来安装这些依赖。
在 Deno 中,你可以在 import 语句中内联 npm:
说明符(以及包版本),这样就完全不需要 package.json
。你可以通过分享你的 JS 或 TS 文件来分享你的代码。如果其他人使用 Deno 运行它,Deno 将自动下载正确的依赖,并且你的程序会以与你在自己机器上运行时相同的方式运行。文件更少、步骤更少、烦恼更少。
事实上,将没有依赖清单的单文件脚本视为“不可变脚本”可以促成可组合程序生态系统的创建。
Windmill.dev 利用 Deno 的可选依赖清单来创建不可变脚本,这是其用户生成工作流的基础构建块。
当然,在你的依赖项可能需要 package.json
的情况下,你始终可以使用它。
推荐在任何 Deno 程序中导入 npm 和 jsr 包——无论是 CLI、服务器、库等——甚至在 Deno 被使用的其他场景中,如 Jupyter notebooks 和 REPL。接下来让我们看看这一点。
Jupyter notebooks 和 REPL
Deno 的 Jupyter 支持非常适合在 JavaScript/TypeScript 中探索数据集,甚至可以用作 REPL(尽管你也可以使用 deno repl
——下文有更多介绍)。
你可以在 Jupyter notebooks 中直接使用 npm:
,这样你就可以导入关键的 npm 模块进行数据探索、分析,甚至图表绘制。这里有一个在 Jupyter notebooks 中使用 npm:polars
的例子。
使用 JavaScript 和 TypeScript 处理数据集不仅拥有许多 Python 中可用的数据库,还允许你轻松地将分析结果渲染为 HTML。这里有一个使用 npm:@observablehq/plot
的例子。
npm:@observablehq/plot
和 jsr:@ry/jupyter-helper
使用 JavaScript 渲染 HTML 图表。有兴趣在 Deno Jupyter 中使用 JS/TS 探索数据集吗?这里有一些很棒的库(及其 Python 对应项)可以帮助你入门:
Python | JS/TS |
---|---|
polars | npm:polars |
ipyaggrid | npm:ag-grid-community |
ipychart | npm:@observablehq/plot |
bqplot | npm:@observablehq/plot |
anywidget | jsr:@anywidget |
你也可以在你的 deno repl
中使用 npm:
。
请注意,就像任何其他 Deno 程序一样,Jupyter notebooks 和 REPL 都会利用你的全局缓存来处理依赖项。这不仅意味着目录中供应商文件更少,而且一旦依赖项被缓存,执行速度也会更快。
提升安全性
在 Node 中,当你导入一个 npm 模块时,该模块拥有无限制的访问权限。由于 npm 模块的超强可组合性,已经有数十起恶意 npm 模块的安全漏洞报告,这些模块从表单中窃取用户数据、执行 shell 注入攻击、在你的机器上安装恶意软件等等。
Deno 从一开始就以安全为核心设计,它采用了一种选择加入的权限模型,当你的任何依赖项请求访问任何敏感内容时,它都会提醒你。例如,npm:chalk
需要访问多个环境变量。
除了 Deno 的权限系统提供的额外安全层之外,Deno 还要求你在 npm 安装过程中选择允许前置和后置安装脚本。虽然某些 npm 包需要这些生命周期安装脚本才能正常运行,但它们也拥有对你系统的完全访问权限。这意味着实质上允许包作者在你的机器、CI 环境等上运行任何脚本,如果你不清楚正在安装哪些包,这会很危险。
在 Deno 中,如果你安装了一个需要执行生命周期安装脚本的 npm 包,你将收到以下警告消息:
要启用安装脚本,你可以使用 --allow-scripts
标志。请注意,此标志还可以接受特定包名称的参数,这不仅为你提供了更高的可见性,还提供了更细粒度的控制。
私有 npm 注册表
许多大型组织托管自己的私有 npm 注册表来管理内部包。Deno 以与 Node 相同的方式支持这一点——通过 .npmrc
文件配置 Deno 从该私有注册表获取包。
// .npmrc
@mycompany:registry=http://mycompany.com:8111/
//mycompany.com:8111/:_auth=secretToken
// deno.json
{
"imports": {
"@mycompany/package": "npm:@mycompany/package@1.0.0"
}
}
// main.ts
import { hello } from "@mycompany/package";
console.log(hello());
$ deno run main.ts
Hello world!
你也可以在你的 package.json
文件中使用私有 npm 包。
// package.json
{
"dependencies": {
"@mycompany/package": "1.0.0"
}
}
// main.ts
import { hello } from "@mycompany/package";
console.log(hello());
$ deno run main.ts
Hello world!
接下来
尽管 Deno 因其 Web 原生协议将始终提供 HTTP 导入,但我们建议 Deno 2 及更高版本默认使用 npm:
或 jsr:
说明符。
要了解更多关于 Deno 2 可以使用的 npm
包和框架,请查看我们的教程: