ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Deno 1.0 深度精读:理解 Rust 打造的新一代 JS/TS 运行时与去中心化模块生态

Deno 1.0 深度精读:理解 Rust 打造的新一代 JS/TS 运行时与去中心化模块生态 文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载本篇是「前端精读周刊」系列对 Deno 1.0 发布节点的深度精读围绕 Deno 的出身背景、安装方式、安全模型、标准库、内置 TypeScript、ESModule 模块化与deps.ts依赖管理展开并结合本仓库相关主题文章补充原理细节。读完本文你将完整掌握 Deno 1.0 时期的核心设计理念、常用命令与配置写法并对Deno 能否替代 Node这一长期命题形成自己的判断依据。一、Deno 是什么与 Node 的渊源和更大的野心Deno 的作者是 Ryan Dahl他正是 Nodejs 背后的策划者。Ryan Dahl 曾在公开演讲《我对 Nodejs 感到遗憾的 10 件事》中系统反思 Node 的设计缺陷这也是他另起炉灶做 Deno 的直接原因。但需要明确的是Deno 并不定位为 Nodejs 的替代品从整体功能看Deno 有更大的野心——据精读文章的推测它想取代现在陈旧的前后端开发模式让 Deno 一统前后端开发全流程。从技术底座看两者的差异非常鲜明Nodejs 由 C 编写其异步能力建立在 libuv 之上Deno 由 Rust 编写并选择了 Tokio 这个异步编程框架承载事件循环Deno 依然使用 V8 引擎解析 JavaScript因此 JS 的执行内核没有改变Deno内置了对 TypeScript 的解析这是相对 Node 生态需要 ts-node、tsc 等额外工具的显著变化。值得注意的是本文精读发布之时2020 年 5 月 13 日 Deno 1.0 正式发布Deno 刚刚迈入 1.0。仓库内另一篇精读《Rust 是 JS 基建的未来》见 前沿技术/218.精读《Rust 是 JS 基建的未来》.md指出Deno 的 linter、code formatter、文档生成器均采用 swcRust 编译器构建因此 Deno 本身也属于Rust 重塑 JS 基建浪潮的一员。这与本仓库第 113 期对 Nodejs V12 的解读见 前沿技术/113.精读《Nodejs V12》.md形成呼应Node 12 在既有 C 架构上做增量升级而 Deno 则选择用 Rust 从底层重新设计整个运行时。二、快速安装 Deno四种方式任选Deno 1.0 时期支持四种主流安装方式覆盖 Shell、PowerShell、macOS 与 Windows 包管理器ShellmacOS / Linuxcurl -fsSL https://deno.land/x/install/install.sh | shPowerShellWindowsiwr https://deno.land/x/install/install.ps1 -useb | iexHomebrewmacOSbrew install denoChocolateyWindowschoco install deno安装完成后deno命令即可在终端直接使用。对于前端开发者来说不需要先安装 Node、再通过 npm 全局安装这与内置工具链的整体设计一脉相承。三、deno run运行脚本与远程模块deno run是 Deno 执行脚本的入口可以类比为node但功能不同——它直接支持运行远程文件这是 Deno 的一大特色也是颇具争议的设计deno run https://deno.land/std/examples/welcome.ts这条命令会直接从网络加载并执行标准库中的 welcome 示例。远程依赖意味着代码的引用路径本身就是依赖声明这也是后面章节去中心化包管理的雏形。在 ts 文件中同样允许用远程脚本加载资源。下面是一个经典的 HTTP Server 示例它从标准库导入serve函数并启动 8000 端口服务import { serve } from https://deno.land/stdv0.42.0/http/server.ts; const s serve({ port: 8000 }); console.log(http://localhost:8000/); for await (const req of s) { req.respond({ body: Hello World\n }); }这个例子同时展示了三个关键点远程 import依赖直接用完整 URL 引入并带有版本号v0.42.0异步迭代for await天然适配服务器请求流这也是 Deno 拥抱现代 JS 异步语法的体现内置 HTTP 能力无需安装 express 之类的三方框架即可起一个本地服务。四、默认安全的权限模型Deno 的默认安全secure by default是它区别于 Node 的最重要设计之一。默认情况下Deno 程序没有环境访问权限、网络访问权限、文件读写权限也没有运行子进程的能力。如果直接运行一个依赖权限的文件会立即报错deno run file-needing-to-run-a-subprocess.ts # error: Uncaught PermissionDenied: access to run a subprocess, run again with the --allow-run flag也就是说权限必须由用户在启动时显式授予而不是程序运行中自行申请。Deno 提供了细粒度的授权参数--allow-read授予文件读权限--allow-write授予文件写权限--allow-net授予网络访问权限--allow-run授予运行子进程的权限此外还有--allow-env环境变量等对应参数。授权参数还可以带上具体路径做更精细的限制例如deno --allow-read/etc上面表示仅/etc文件夹下的文件拥有读权限其他路径的读操作仍会被拒绝。这种最小权限 显式声明的模型让一段脚本能访问什么资源在启动命令里一目了然。除了直接在命令行加参数、在 Bash 脚本中调用外还可以用 Make 运行或者使用类似的 drake 任务运行器启动。更进一步Deno 提供了deno install命令把脚本转化为一个全局快捷指令deno install --allow-net --allow-read -n serve https://deno.land/std/http/file_server.ts其中-n即--name用于对脚本重命名。执行上面命令后serve命令就等同于deno run --allow-net --allow-read https://deno.land/std/http/file_server.tsdeno install把权限参数 远程脚本打包成了一个可复用的本地命令权限声明也随之固化在命令里这是 CLI 工具分发的理想形态。五、标准库官方出品的npm 精选Deno 在标准库上很有特点对常用功能提供官方版本保证可用性与稳定性。原文列出了一份 Deno 标准库模块与 Npm 三方库的对照表Deno ModuleDescriptionnpm EquivalentscolorsAdds color to the terminalchalk, kleur, and colorsdatetimeHelps working with the JavaScript Date objectencodingAdds support for external data structures like base32, binary, csv, toml and yamlflagsHelps working with command line argumentsminimistfsHelps with manipulation of the file systemhttpAllows serving local files over HTTPhttp-serverlogUsed for creating logswinstontestingFor unit testing assertion and benchmarkingchaiuuidUUID generationuuidwsHelps with creating WebSocket client/serverws从这张表可以看到Deno 既做运行环境、又做基础生态直接缓解了 Npm 生态下的选择困难症。对于这一点精读文章给出了辩证的视角利好面集成官方包对功能确定的模块如 uuid、datetime很有必要而且提高了底层库的稳定性——同一个fs、http由官方持续维护不会出现某个库作者弃坑的风险辩证面Deno 生态也有三方库本质上三方库和官方库在功能上没有任何壁垒实现代码都类似唯一区别是谁能为稳定性站台。假设微软和 Deno 官方同时推出基于 Npm 生态与 Deno 生态的官方库、并都承诺持续维护你更相信谁官方是否有优势最终取决于官方自身的实力而不是官方这个身份本身。这个判断同样适用于本仓库《Rust 是 JS 基建的未来》中对 Deno 的评价Deno 的官方工具链由 Rust 阵营的 swc 驱动官方库的稳定性建立在底层基础设施的成熟度之上而非单纯的名分。六、内置 TypeScript无需任何配置即可运行 TSDeno 内置支持 TypeScript因此不需要 ts-node直接deno run test.ts就能运行 TypeScript 文件。注意其内部实现Deno 是利用 TypeScript 引擎把 TS 解析为 JS 后再交由 V8 引擎解析执行本质上运行链路并没有质变——只是对开发者来说省去了 ts-node 这类额外工具生态会更规范。由于内置了 TS 支持自然也不需要写tsconfig.json配置但你依然可以定制它deno run -c tsconfig.json [file-to-run.ts]-c参数允许指定自定义的 TypeScript 编译配置。Deno 默认还开启了 TS 严格模式strict mode这意味着 Deno 在默认配置下就会强制类型检查为高质量代码保驾护航。基于这一点可以认为Deno 是为了构建高质量理想库而诞生的运行环境它基于已有生态来做但做了更多内置技术选型。这与 Facebook 的 Rome前端工具链全家桶思路很像但 Deno 做得更彻底——Deno 从实现上基于 Rust 重新实现了一套模块加载标准从更底层的方式重新解读 W3C 标准规范以期望解决 JavaScript 生态的各种痛点问题。作为对比基于纯 JS 生态也可以写出deno run test.ts这类引擎但那只是由 JS 驱动执行、编译可能还要借助 Webpack底层并不触及模块加载等根本性设计。七、支持 Web 标准fetch、setTimeout 开箱即用Deno 支持 W3C 标准规范因此fetch、setTimeout等 Web 平台 API 都可以被直接使用无需任何 polyfill 或额外引入。如果你只使用 Deno 支持的这些标准函数写代码可以保证同一份代码在Deno、Node、Web 三个平台跨平台运行。精读文章也客观指出虽然距离完全实现 W3C 所有标准规范还有一些路要走但 Deno 兼容规范的决心是明确的。对于前端开发者来说浏览器里能跑的代码Deno 里也能跑极大地降低了从浏览器到服务端的迁移成本——这正是一统前后端开发全流程野心的技术根基。八、ESModule 与去中心化包管理路径即依赖声明模块化是 Deno 的亮点。Deno 使用官方 ESModule 规范但引用路径必须加上后缀import * as log from https://deno.land/std/log/mod.ts; import { outputToConsole } from ./view.ts;Deno 不需要像 npm 那样单独申明依赖清单代码的引用路径本身就是依赖申明——它包含完整的路径以及文件后缀也支持网络资源。因为路径可以是任何网络地址Deno 得以摆脱 NPM 中心化的包管理模式。关于 ESModule 本身的机制可以参阅本仓库的 前沿技术/52.精读《图解 ES 模块》.mdESM 是静态模块系统导入导出关系在编译期即可确定且导出采用活绑定live binding——这与 Deno 直接用 URL 声明依赖、由运行时静态解析的设计互为表里。缓存机制没有 node_modules 但仍有缓存对于import * as log from https://deno.land/std/log/mod.ts;这行代码Deno 会把远程模块下载到一个缓存文件夹用户不会感知到这个文件夹与下载过程的存在。也就是说Deno 环境中没有node_modules目录项目目录保持干净。当远程代码更新后可以通过deno --reload强制刷新缓存重新拉取最新版本。辩证看待去中心化虽然引用了网络源、摆脱了 node_modules但Deno 去中心化这件事需要辩证看待精读文章列出了三个现实问题实际上还存在一个 node_modules只是用户看不到——缓存目录客观存在网络下载速度被放到了运行时第一次启动时下载依赖会比较慢普通模式下无 lock必须配合deps.ts使用下文会讲到才能锁定版本。反过来看即使是被打上中心化恶人标签的 npm也有去中心化的一面npm 支持私有化部署无论速度还是稳定性都可以由公司自己掌控。从稳定性来说npm 在 1.0 时期仍拥有压倒性优势。三方库生态与 CDN 引用Deno 还有第三方库生态截至 2020 年 5 月精读时点共有 221 个三方库。由于 Deno 走网络资源可以借助 Pika 提供的 CDN 服务直接引用网络资源包import * as pkg from https://cdn.pika.dev/preact^10.3.0;这样看上去很轻量但对公司来说**还是需要自建一个Pika**来保障稳定性并承担全球 CDN 缓存等工作。也就是说去中心化的便利背后稳定性和分发基础设施的成本并没有消失只是从谁用谁付变成了生态共建。九、告别 package.jsondeps.ts 与依赖锁定npm 生态下包信息存放在package.json中包含但不限于以下内容项目元信息项目依赖和版本号依赖分类比如dependencies、devDependencies甚至peerDependencies入口标记main和module还有 TS 用的types与typings脚手架的bin等等npm scripts。随着标准的不断更新package.json的信息已经非常臃肿。而 Deno 使用deps.ts集中管理依赖export { assert } from https://deno.land/stdv0.39.0/testing/asserts.ts; export { green, bold } from https://deno.land/stdv0.39.0/fmt/colors.ts;deps.ts就是一个普通文件只是将项目的依赖精确描述出来。这样其他地方引用assert时就可以这么写// import { assert } from https://deno.land/stdv0.39.0/testing/asserts.ts; import { assert } from ./deps.ts;通过deps.ts把远程 URL 统一收口到本地文件既集中了版本管理又让业务代码中的 import 保持简洁。如果需要锁定依赖可以通过deno --locklock.json方式申明用 lock 文件固化各个依赖的精确版本弥补普通模式无 lock的短板。十、deno doc从 JSDoc 到文档deno doc filename命令可以根据文件按 JSDoc 规则生成文档同时也支持 TS 语法。比如下面这段代码/** Asynchronously fulfill a response with a file from the local file * system. */ export async function send( { request, response }: Context, path: string, options: SendOptions { root: } ): Promisestring | undefined { // ... }执行deno doc后生成的文档如下function send(_: Context, path: string, options: SendOptions): Promisestring | undefined Asynchronously fulfill a response with a file from the local file system.函数签名与 JSDoc 注释被自动提取、格式化输出。值得说明的是Deno 官方文档本身就是用这个命令生成的——用文档工具生成文档项目的文档这本身就是对工具链自举能力的最佳验证。十一、内置工具链test / 格式化 / bundle前端 JavaScript 工具链相当混乱虽然业界已有 Umi 等框架做了开箱即用的封装但回到 JavaScript 设计的初衷——它本应可以在浏览器直接使用包括浏览器对不依赖构建工具的模块化支持——注定了未来 Webpack 这类重型构建工具一定会被淘汰。Deno 通过内置一套工具链的方式解决这个问题包括测试提供deno test命令与Deno.test()测试函数格式化提供官方 VSCode 插件Deno 的 linter、formatter 由 swc 驱动见 前沿技术/218.精读《Rust 是 JS 基建的未来》.md编译提供deno bundle命令。不过值得注意的是在最重要的编译环节deno bundle在 1.0 时期提供的能力是相对欠缺的比如还不支持 Tree Shaking摇树优化。用 Rust 等语言提升构建效率是业界一直在尝试的方向例如有开发者基于 esbuild 做了 umijs/plugin-esbuild 插件用于提升 Umi 构建速度但为了防止生产构建产物与 Webpack 默认规则不一致仅使用了其压缩minifier功能。对 Deno 来说也一样——1.0 时期没有任何证据表明 deno 的构建结果可以完美适配 webpack 环境所以请勿认为 Deno 发布了 1.0 版本就等于可以在生产环境使用。十二、总结1.0 的真实定位与长期思考正如精读原文结尾所指出的Deno 虽然发布了 1.0 版本但仍不能完全替代 Nodejs。这背后的原因主要是历史兼容成本——完整支持整个 Node 生态不只是设计问题更是一个体力活需要一个个高地去攻克。同样Deno 对 Web 的支持让人耳目一新但仍不能放到生产环境使用除了官方和三方生态还在逐渐完善外deno bundle对 Tree Shaking 能力的缺失、以及构建产物无法保证与 Webpack 完全相同都会导致对稳定性要求极高的大型应用迁移成本非常高。最亮眼的改动是模块化部分依赖完全去中心化从长远来看是一个非常好的设计只是基础设施和生态要达到较为理想的水平仍需时间。最后站在预言者角度思考 Deno 到底会不会火精读文章给出了三个层次的判断Deno 的初心是做一个更好的 Node但对于这种级别的生态底层工具来说重新做一个并重新火起来的难度不亚于重新做一个阿里巴巴并取代现在的阿里——不同的时间点做同一件事哪怕后者可以吸取教训大概率也无法复制以前的成功路线从功能上看Deno 解决了 Node 很多痛点包括去中心化管理有点云开发的意思但在 2020 年基于 Nodejs 和 Webpack 的云开发已经出现而且 Deno 基于 V8 解析 JavaScript对性能和功能都没有革命性提升从技术上作出突破也几乎不可能Deno 的思想确实比 Node 先进但不能说比 Node 好十倍就无法撼动 Node 的生态——即便是 Node 作者自己可能也不行。当然文章也留下一句意味深长的自省然而我上面说的可能都是错的。这正是技术判断应有的开放态度。对开发者而言理解 Deno 的安全模型、内置 TS、ESModule 与deps.ts设计本身就是在理解 JavaScript 生态演进的两种可能路径而至于未来 10 年 Deno 官方生态是否会壮大到影响 Web 生态需要每个人结合自己的职业方向独立思考和判断。本期精读出自「前端精读周刊」仓库入口见 readme.md该周刊长期聚焦前沿技术与源码解读可作为持续跟踪运行时与前端基建演进的参考读物。赞分享文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载相关推荐JSHint 与 Deno新一代 JavaScript 运行时的代码检查JSHint 与 Deno新一代 JavaScript 运行时的代码检查 你还在为 JavaScript 代码在不同运行时环境中出现兼容性问题而烦恼吗当你将Lint静态分析youki 的 Crates 架构一个 Rust 编写的 OCI 容器运行时的模块化 crate 生态解析youki 的 Crates 架构一个 Rust 编写的 OCI 容器运行时的模块化 crate 生态解析 youki 本身是一个 Cargo workspa容器运行时云原生Plop与Deno现代JavaScript运行时的代码生成Plop与Deno现代JavaScript运行时的代码生成 你是否还在为项目中重复创建相似文件而烦恼团队协作时如何确保每个人创建的代码文件格式统一本文将开发工具CLI上一篇Kernel-Bridge高级开发者教程Hypervisor虚拟化技术实战下一篇办公效率工具Thief多任务处理与跨平台办公解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表