Deno

虽然Deno的主版本号已经达到1, 但Deno仍然不是一个足够成熟的项目.
包括它的标准库和工具链在内, 有太多的功能还处于不稳定状态,
开发环境也不如Node.js集成性好, 尚不足以将其用于生产环境.
  • 用Rust开发.
  • API尽可能与Web环境兼容.
  • 具有主见的 deno fmt, deno lint, deno test, deno bench.
  • 默认安全, 具有细粒度的权限系统, 支持动态请求权限.
    目前, 由于Deno有很多内部使用的字段, 细粒度权限用起来可能很不方便, 最后还是会被逼去使用粗粒度权限.
  • 原生支持TypeScript, 这点正变得越来越重要, 因为JavaScript社区在向TypeScript转变.
  • Node.js兼容性.
稍有规模的Deno项目就会导致LSP的响应速度变慢, 很容易劝退想要使用Deno的开发人员.
相比之下, 基于Node.js和TypeScript的项目则完全没有类似的问题.
Deno的LSP默认配置相当保守, 例如, documentPreloadLimit 的默认值为1000, 实际开发中很容易超出.
Deno曾经提供一个内置的命令 deno bundle 用于将项目打包成一个文件.
ry曾经提议删除此命令, 但这个想法最终被撤回.
然而, 在1.31版本里, 该命令在没有经过任何社区讨论的情况下就被列为弃用,
官方手册建议改用esbuild.
WHATWG流模型设计得相当糟糕, 远远比不上Node.js的流.
举例来说, 使用者甚至无法知道一个流是否关闭, 或是什么时候关闭的.
https://docs.deno.com/runtime/tutorials/manage_dependencies
Deno目前的依赖管理方案似乎受到Go语言很大影响, 使用体验糟糕.
在任何多文件项目中, 开发者要么将每条依赖的URL写一遍,
要么通过创建一个 deps.ts 文件来聚合依赖,
前者让项目变得难以维护, 因为URL散落在各处, 后者破坏了模块导入的直接性,
不同模块的成员被混在一个文件里导出, 难以识别.
模块会根据被导入的情况, 决定其协议.
当模块作为本地模块被导入时, 模块的 import.meta.url 为file协议.
当模块作为远程模块在被导入时, 其 import.meta.url 为http/https协议.
问题在于相当多的API只支持一种协议, 例如 Deno.readFile 仅支持file协议, 不支持http/https协议.
这种情况导致开发者在使用 import.meta.url 时有更多的心智负担.
根本问题出在Deno不暴露导入模块(缓存)的本地路径, 而这本身是一个在设计层面就可以解决的问题.
https://github.com/denoland/deno/issues/5987
相当可悲的, Deno通过让fetch函数支持file协议来解决读取文件相关的问题.
为避免反复访问远程资源, 建议一并使用缓存库: https://github.com/denosaurs/cache
https://docs.deno.com/runtime/manual/basics/import_maps#import-maps-are-for-applications
这强制要求模块编写者在代码里使用相对导入.
根本问题出在Deno的模块设计上, Deno在导入模块时根本无从而知导入地图的存在.
deno install 命令允许将脚本安装为全局命令, 类似于Node.js的bin属性.
然而, deno install 在安装脚本时, 默认不会考虑脚本所在目录的 deno.json 文件, 相应的导入别名亦不会被尊重.
在执行 deno install 时, 通过 --import-map 选项手动指定 deno.json 文件可以解决问题.
这种解决方案极大的增加了安装来自互联网的CLI脚本所需输入的命令的长度.
此外, 如果 deno.json 文件包含非 improtsscopes 字段, 相关实现有一个可悲的错误, 会导致在执行命令时输出以下警告:
Import map diagnostics: - Invalid top-level key "tasks". Only "imports" and "scopes" can be present.
deno install 也支持 --config deno.json 选项, 遗憾的是该选项在这个场合下没有任何用处.
https://github.com/denoland/deno/issues/3196
TypeScript不支持将 .ts 扩展名的模块路径编译为 .js 扩展名,
而Deno的模块路径一定是 .ts 扩展名.
因此, 为Node.js模块设计的TypeScript项目的代码将无法在Deno里运行.
TypeScript 5开始支持allowImportingTsExtensions配置项, 这允许在项目里使用 .ts 扩展名,
但该配置项被限制为只能在配置为noEmit或emitDeclarationOnly的情况下使用,
这意味着使用 .ts 扩展名的同时不能生成可用于Node.js的代码.
因为这个原因, 创建一个同时可用于Deno和Node.js的库仍然是不可能的.
https://github.com/oakserver/oak
https://fresh.deno.dev/
Deno官方于2022年推出的全栈Web框架, 对标Next.js.
不建议使用它, 因为:
  • 几乎没有React库为Deno支持做测试.
  • 内部使用的是React的轻量变体Preact, 这已经被证明是一个错误的技术路线.
Deno可以通过CDN导入JSDOM.
import { JSDOM } from 'https://esm.sh/jsdom@22.1.0'
https://github.com/b-fuze/deno-dom
该项目是Rust的html5ever包的绑定.
使用Deno FFI的SQLite.
优点:
  • 接口与better-sqlite3基本相同.
  • 高性能.
缺点:
  • Deno FFI处于不稳定状态:
    • 依赖该库的项目需要限制Deno的版本号.
    • 为了使用该库, 需要开启所有的权限选项.
  • 平台上的预构建二进制存在差异:
    https://github.com/denodrivers/sqlite3/issues/118
编译为WASM的SQLite.
优点:
  • 最小权限.
  • 跨平台.
缺点:
  • 受到Deno API的限制.
  • 默认启用的编译选项非常少, 很可能需要自己编译.
自Deno 1.21开始, 标准库提供了相当多的测试功能,
这使得在大多数情况下不需要使用第三方库就可以完成测试.
Deno的这种技术选择有效避免了Node.js测试的供应商绑定问题.
当前标准库已经支持BDD风格, mock/stub, 快照匹配, 计时器模拟:
https://deno.land/std@0.181.0/testing
https://github.com/denoland/deno_std/issues/1779
deno run 命令运行脚本时, 会加载TypeScript类型检查器, 这会导致deno进程使用更多的内存.
如果先通过 deno compile 编译后再运行, 脚本的内存用量会有明显改善.