Deno是代码浏览器:没有软件包管理器或集中式包存储库 - kitsonkelly


我们需要改变我们的思维模式。许多人都将包管理器和集中式代码注册表作为普遍要求,以拥有一个包管理器和一个集中式代码注册表。因为它们存在并不意味着它们是必需的。之所以出现它们,是因为它们以特定的方式解决了问题,我们刚刚接受它们作为解决该问题的唯一方法。我认为那是不正确的。

浏览器的特点
为了发布网站,我们不会登录到中央Google服务器,将我们的网站上传到google的中央注册表中。然后,如果有人想查看我们的网站,他们会使用命令行工具,该工具会读取browser.json配置文件,在我们本地计算机上的文件中添加一个条目,然后访问并获取整个网站,以及该网站链接到我们本地websites目录之前的所有其他网站。然后,我们启动浏览器以实际查看该网站。这太疯狂了吧?那么为什么要接受运行代码的这种模型呢?
Deno CLI的工作方式类似于浏览器,但是适用于代码的浏览器。您在代码中导入一个URL,Deno将去获取该代码并将其缓存在本地,就像浏览器一样。此外,就像浏览器一样,您的代码在沙箱中运行,沙箱对正在运行的代码的信任度为零,而与源代码无关。您(调用代码的人)从外部告诉该代码可以做什么和不能做什么。此外,就像浏览器一样,代码可以要求您执行操作,您可以选择授予或拒绝。
HTTP协议提供了提供有关代码信息所需的一切,并且Deno尝试完全利用该协议,而不必创建新协议。

发现代码
首先要考虑的是,像浏览器一样,Deno CLI不想对您运行的代码有任何看法。它列出了如何获取代码以及如何将代码从运行的计算机中沙箱化的规则。在我看来,这是运行环境应该拥有的立场。
在Node.js / npm生态系统中,我们已将本地计算机上的代码管理与集中式代码注册表结合在一起,以帮助促进发现。我认为,两者都有非常严重的缺陷。
早在互联网的早期,我们就尝试了npm类型的可发现性。您可以将您的网站添加到Yahoo!。根据正确的分类,人们可能会使用搜索功能,但这全都是基于提供内容者的观点来构建的,而不是真正基于针对消费者需求的优化而构建的。最终出现了Google。Google为什么赢了?因为它很有用。它以一种简单的方式将网站编入索引,该方式将需求的简单表达(搜索词)与满足需求的最相关的网页相匹配,同时考虑了多种因素,包括元数据提供的内容提供商是其中的一个因素。
虽然我们还没有用于Deno的代码的模型,但它是可行的模型。此外,我们之所以使用Google是因为它为我们解决了问题,而不是被告知“您必须使用Google”,以及Google还有其他可行的替代方案。

可重复的构建
在npm生态系统中,这成为一个问题。由于严重依赖语义版本控制,并且复杂的依赖关系图往往来自Node.js / npm生态系统,因此具有可重复的构建成为一个真正的问题。Yarn引入了锁定文件的概念,npm随之而来。
我个人的感觉是,这有点像是在摇尾狗,因为生态系统中开发人员的行为造成了一个问题,然后需要一个不完善的解决方案来解决它。我们中长期生活在生态系统中的任何人都知道解决许多问题的方法是 rm -rf node_modules package-lock.json && npm install。
话虽如此,Deno为此提供了两种解决方案。首先,是Deno缓存模块。可以将该高速缓存检入到源代码管理中,并且该 --cached-only标志将确保不尝试检索远程模块。DENO_DIR环境变量可用于指定所述高速缓存位于以提供进一步的灵活性。
其次,Deno支持锁定文件。--lock lock.json --lock-write会针对给定的工作负载写出一个具有所有依赖项哈希值的锁定文件。使用时,这将用于验证以后的运行--lock lock.json。
还有一些其他命令可以管理可重复的构建。 deno cache将解决所提供模块的所有依赖关系,并填充Deno缓存。deno bundle可以用于生成工作负载的单个文件“构建”,所有依赖关系都已解决并包含在该文件中,因此以后的deno run命令只需要单个文件即可。

信任代码
无论出于何种原因,我们都信任集中式注册表中的代码。我们甚至都没有考虑过。不仅如此,我们相信该代码已完全审查了其所有依赖关系,并且这些依赖关系将受到信任。我们进行了快速搜索,然后输入npm install some-random-package并认为“这很好!” 我认为丰富的npm软件包生态系统使人们感到沾沾自喜。
为了弥补这种松懈和自满,我们在工具链中实现了安全监视软件,以分析我们的依存关系以及成千上万行代码,以使我们知道某些代码可能是可利用的。公司设置私有注册表来托管可能比单个公共注册表稍微多一些的软件包。
感觉好像房间里有一头大象。最好的策略是我们不应该信任任何代码。一旦我们建立了该功能,然后将其打开即可轻松一些。但是,如果我们认为程序包管理器和集中式注册表可以解决此问题,或者甚至可以实质性地解决此问题,我们就会自欺欺人。实际上,我认为它们的利用使我们的警惕降低了。“好吧,在npm上,如果这对我不利,那么肯定有人会把它取下来。”
Deno在这方面的工作还不尽如人意,但它是从一个好的位置开始的。它在启动时具有零信任,并提供相当精细的权限。

依赖管理
我认为我们需要坦率地谈谈npm生态系统中的依赖关系。实际代码为132个字节,但整个包大小为3.4kb。可运行代码为整个程序包大小的3.8%。
我的意见是,这涉及几个因素。其中很大一部分是我们颠倒了模型,我说过Deno是代码浏览器。问题在于,这种向后的模型已经感染了我们创建网站的方式。尽管没有中央注册表,但是在构建网站时,我们会下载所有依赖的代码,并将其烘焙到服务器上加载的内容中,然后每个用户将一堆代码下载到本地计算机上。一些证据表明,所下载的代码中只有大约10%是该站点或Web应用程序唯一的,其余的是我们正在下载到开发工作站并捆绑在一起的所有代码。这种模型被破坏是Snowpack等解决方案试图解决的一些问题 。
另一个重要的问题是我们的依赖项虽然没有与我们的代码耦合。将我们的代码中依赖关系放到了package.json。
这导致我们进入Deno模型,我喜欢称其为Deps-in-JS,因为所有很酷的孩子都在做* -in-JS事情。将我们的外部依赖关系明确地表示为URL意味着代码依赖于其他代码简洁明了,并且我们的代码和依赖关系紧密地耦合在一起。如果要查看该依赖关系图,只需要deno info与本地或远程模块一起使用:

$ deno info https://deno.land/x/oak/examples/server.ts
local: $deno/deps/https/deno.land/d355242ae8430f3116c34165bdae5c156dca21aeef521e45acb51fcd21c9f724
type: TypeScript
compiled: $deno/gen/https/deno.land/x/oak/examples/server.ts.js
map: $deno/gen/https/deno.land/x/oak/examples/server.ts.js.map
deps:
https:
//deno.land/x/oak/examples/server.ts
  ├── https:
//deno.land/std@0.53.0/fmt/colors.ts
  └─┬ https:
//deno.land/x/oak/mod.ts
    ├─┬ https:
//deno.land/x/oak/application.ts
    │ ├─┬ https:
//deno.land/x/oak/context.ts
    │ │ ├── https:
//deno.land/x/oak/cookies.ts
    │ │ ├─┬ https:
//deno.land/x/oak/httpError.ts
    │ │ │ └─┬ https:
//deno.land/x/oak/deps.ts
    │ │ │   ├── https:
//deno.land/std@0.53.0/hash/sha256.ts
    │ │ │   ├─┬ https:
//deno.land/std@0.53.0/http/server.ts
    │ │ │   │ ├── https:
//deno.land/std@0.53.0/encoding/utf8.ts
    │ │ │   │ ├─┬ https:
//deno.land/std@0.53.0/io/bufio.ts
    │ │ │   │ │ ├─┬ https:
//deno.land/std@0.53.0/io/util.ts
--snip--

TypeScript的好处之一是,您可以全面验证代码与其他代码的兼容性。

请在没有软件包管理器或集中式软件包存储库的情况下小试一下,看看它如何进行。您可能永远不会回头!