Deno 的目标不是兼容 Node,而是兼容浏览器。Deno 不是要取代 Node.js,也不是下一代 Node.js,也不是要放弃 npm 重建 Node 生态。比如react和vue的存在,两个框架都很好,并不是谁要消灭谁,我们可以哪个合适用哪个,想用哪个用哪个。deno 的目前是要拥抱浏览器生态。
前期文章讲解过《初识Deno》(https://mp.weixin.qq.com/s/ZlnbBITXDqxpwLf32n9-bw)和《从模块谈Deno和Node的区别(https://mp.weixin.qq.com/s/LshySTHs7rlMsBcmx6qVuw) 。本期内容主要围绕Deno核心模块中比较重要的两个部分讲解:安全沙箱机制和Deno依赖引入管理。
标题提到过核心模块,讲解一下我个人对于Deno模块的理解,我的划分:第三方库、标准库、底层运行时、安全沙箱机制和依赖管理。顺带我们可以看看Deno源码项目目录的划分(本次所针对的都是version1.5.3的版本),主要包含以下部分:
![bc30e79a820680a283e5cfc8401a1f2c.png](https://i-blog.csdnimg.cn/blog_migrate/17a8f2b7a3d8440e0e5bdb625e1569bd.png)
Deno天然支持ECMAScript 6(以下简称es6),可以直接用es6 的模块引入方式(即import/export,后面简称es module)。Deno 依靠 es module 引入URL 来承载和导入程序包。这种引入插件的方式非常灵活,我们可以直接创建软件包而无需在 NPM 这样的存储库中发布它们。
下面我们通过官方简单demo来展现Deno通过es module 引入URL请求资源并且执行,当我们运行代码` deno run https://deno.land/std/examples/welcome.ts `,会正确返回如下图的结果。
下图是官方demo中的代码:
可以看到上面项目我们本地没有任何代码或者项目,只是运行了一个远程的官方demo文件,在控制台就直接执行并且正确执行结果。不需要自己去下载要引入的代码,‘拿来主义’直接执行就好了,非常省事。
默认情况下,缓存中的模块将被重用,而无需获取或重新编译它。那是否存在缓存不被更新依赖到旧代码的问题?肯定会的。有时候我们并不希望使用缓存的模块,我们可以强制 deno 重新下载模块并重新编译到缓存中。可以使用 deno cache 子命令的--reload 选项来使本地缓存无效。
带着问题学习可能会更易于理解,我们来看看下一个问题。由于Deno直接引入外部代码或者本地代码,没有包管理器的情况下管理外部依赖关系就会导致问题。会不会在用的地方引入会导致引入文件比较杂乱、项目越大越难维护、重复引入等问题?答案是肯定的。
在 Deno 中解决这些问题的标准做法是创建一个 deps.ts 文件。此文件中引用了所有必需的远程依赖关系,并且重新导出了所需的方法和类。本地模块从 deps.ts 导入所需方法和类,而不是远程依赖。这样就可以轻松跨大型代码库更新模块,并解决“程序包管理器问题”。开发依赖项也可以在单独的 dev_deps.ts 文件中进行管理。
deps.ts中引入文件示例图:
![5133a22307bcd07334576bdabe98cfa5.png](https://i-blog.csdnimg.cn/blog_migrate/797b141753c1b8bdb9416ad6296ecbfd.png)
![5733f4369edfaf2de4e1330c6d2638ac.png](https://i-blog.csdnimg.cn/blog_migrate/171ec5fb90dc53b6d615ed3e38145e7d.png)
depsTest.ts 运行示例图:
可以看到上面可以正确运行出结果,感觉是未来的趋势,精简而干练。
那么Deno为何可以通过URL直接引入其他资源直接执行呢?deno有自己的缓存远端资源的逻辑。下面我们来看看deno/core代码模块的https_catsh.rs部分源码。
即使作为前端人员不是很懂rust,从下面源码大致可以get到两个方法:base_url_tofilename和url_to_filename。base_url_tofilename将请求的url解析后返回成`主域名+文件hash`的字符串返回,url_to_filename直接调用的base_url_tofilename,返回一个包含缓存文件名的数组,并且该模块代码会进行文件内容的写入等一些列操作。get_cache_filename是总出口文件,供core/http_fetcher.rs文件模块使用。
下图是经过base_url_tofilename方法处理后的前后文件名的对应关系。
文件模块core/http_fetcher.rs,可以起一个http服务,查询文件是否在本地存在。如果文件存在,就直接从本地拿文件资源,如果本地缓存中没有文件,就通过http请求与远端建立链接,获取远端资源写入到本地进行缓存。
下面代码则是缓存处理的初始化配置信息。默认是从缓存中获取数据,不需要中心加载所有数据,以及只有使用权限。
一 、安全沙箱机制
URL的方式直接引入文件会不会导致和node一样的权限紊乱问题?Deno的答案是“NO”。Deno有自己的安全沙箱机制。
首先对沙箱进行简单的释义。沙箱是一个限制程序运行的环境,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。
Deno 的安全沙箱机制来自 web。安全沙箱机制是 web 的特色,浏览器中运行的 js 代码可能来自任何地方,而且都不是我们主动下载运行的,也不一定这些代码都是受信任的,但是我们依然敢运行这些代码。是因为浏览器的沙箱机制保证了这些 js 代码不会访问系统资源。
Deno的安全沙箱机制主要包含文件读取权限、网络权限、系统权限。默认我们代码是没有访问这些权限的,在命令行参数中为 Deno 进程授权后才能访问安全敏感的功能。在实际开发过程中我们可以加上--allow-all (允许所有权限,无安全限制)、-allow-env (允许环境访问,比如读取和设置环境变量)、--allow-net= (允许网络访问)、--allow-write=(允许写入文件系统)或 --allow-read=(允许读取文件系统)等权限,赋予我们的代码一定的权限。
如下代码为rust中判断是否有创建文件夹权限,有权限才可循环创建文件夹,否则提示没权限。
如下图,permission.rs文件中会对各项权限进行判断。判断有没有写入权限,有没有网络权限等。
在deno使用过程中我一直有个疑惑:为什么deno run 'https://deno***'下的文件没提示需要网路权限,在查看源码发现是deno内部处理了对deno网络权限权限的白名单,如下图所示:
最后,作为文章的收尾,提一下作为deno rust语言入口文件的main.ts,通过main方法把所有代码模块串联起来,运行出我们所看到的deno效果。主入口方法如下图所示。
由于对rust语言以及deno的原理熟悉度有限,本编文章主要讲解到这里。如果文章中有不合适的地方或者讲解的不对的,欢迎留言交流。一起学习Deno,fighting~~