如果您不使用 npm 标识符,那您就用错了
在 Deno 的早期,我们建议通过 HTTP 和诸如 esm.sh 和 unpkg.com 等转译服务导入 npm 包。然而,以这种方式导入 npm 包存在局限性,例如缺少安装钩子、重复依赖项解析问题、加载数据文件等。这就是为什么在 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 中,您可以将 npm:
标识符内联到您的 import 语句中(以及包版本),这样您根本不需要 package.json
。您可以通过共享您的 JS 或 TS 文件来共享您的代码。如果其他人使用 Deno 运行它,Deno 将自动下载正确的依赖项,并且您的程序将以与您机器上相同的方式运行。更少的文件、步骤和挫败感。
实际上,将没有依赖项清单的单文件脚本视为“不可变脚本” 可以引导创建可组合程序的生态系统。
Windmill.dev 使用 Deno 的可选依赖项清单来创建不可变脚本,这是其用户生成工作流程的基础构建块。
当然,在您的依赖项可能需要 package.json
的情况下,您始终可以使用它。
建议在任何 Deno 程序(CLI、服务器、库等)中导入 npm 和 jsr 包 — 即使在其他使用 Deno 的环境中,例如 Jupyter 笔记本和 REPL。让我们接下来看看。
Jupyter 笔记本和 REPL
Deno 的 jupyter 支持非常适合在 JavaScript/TypeScript 中探索数据集,甚至可以用作 REPL(尽管您也可以使用 deno repl
— 稍后详述)。
您可以直接在 Jupyter 笔记本中使用 npm:
,这样您就可以导入用于数据探索、分析甚至图表的关键 npm 模块。这是一个在 Jupyter 笔记本中使用 npm:polars
的示例
使用 JavaScript 和 TypeScript 处理数据集不仅拥有 Python 中可用的许多数据库,而且还允许您轻松地将分析渲染为 HTML。这是一个使用 npm:@observablehq/plot
的示例
npm:@observablehq/plot
和 jsr:@ry/jupyter-helper
使用 JavaScript 渲染 HTML 图表。有兴趣使用 JS/TS 在 Deno jupyter 中探索数据集吗?以下是一些很棒的库(及其 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 笔记本和 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/[email protected]"
}
}
// 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 将始终提供 HTTP 导入,因为它基于 Web 原生协议,但我们建议 Deno 2 及更高版本默认使用 npm:
或 jsr:
标识符。
要了解有关您可以与 Deno 2 一起使用的 npm
包和框架的更多信息,请查看我们的教程