美文网首页Docker容器
不懂Deno?这篇文章看了就会了

不懂Deno?这篇文章看了就会了

作者: huhandsome | 来源:发表于2020-09-16 16:01 被阅读0次

    原文出自我的博客:进阶全栈工程师,Deno你学会了吗?
    如果你一直关注 Web 开发领域,那么最近可能已经听到了很多关于 Deno 的信息——一种新的 JavaScript 运行时,它可能也会被认为是 Node.js 的继承者。但是这意味着什么,我们需要“下一个 Node.js” 吗?

    什么是 Deno?

    Deno 是一个 JavaScript/TypeScript 的运行时,默认使用安全环境执行代码,有着卓越的开发体验。

    Deno 建立在 V8、Rust 和 Tokio 的基础上。

    功能亮点

    • 默认安全。外部代码没有文件系统、网络、环境的访问权限,除非显式开启。
    • 支持开箱即用的 TypeScript 的环境。
    • 只分发一个独立的可执行文件 (deno)。
    • 有着内建的工具箱,比如一个依赖信息查看器 (deno info) 和一个代码格式化工具 (deno fmt)。
    • 有一组经过审计的 标准模块,保证能在 Deno 上工作。
    • 脚本代码能被打包为一个单独的 JavaScript 文件。

    Deno VS Node

    • Deno 不使用 npm,而是使用 URL 或文件路径引用模块。

    • Deno 在模块解析算法中不使用 package.json

    • Deno 中的所有异步操作返回 promise,因此 Deno 提供与 Node 不同的 API。

    • Deno 需要显式指定文件、网络和环境权限。

    • 当未捕获的错误发生时,Deno 总是会异常退出。

    • 使用 ES 模块,不支持 require()。第三方模块通过 URL 导入。

      import * as log from "https://deno.land/std/log/mod.ts";
      复制代码
      
    - Node Deno
    API 引用方式 模块导入 全局对象
    模块系统 CommonJS & 新版 node 实验性 ES Module ES Module 浏览器实现
    安全 无安全限制 默认安全
    Typescript 第三方,如通过 ts-node 支持 原生支持
    包管理 npm + node_modules 原生支持
    异步操作 回调 Promise
    包分发 中心化 npmjs.com 去中心化 import url
    入口 package.json 配置 import url 直接引入
    打包、测试、格式化 第三方如 eslint、gulp、webpack、babel 等 原生支持

    安装 Deno

    Deno 能够在 macOS、Linux 和 Windows 上运行。Deno 是一个单独的可执行文件,它没有额外的依赖。

    官方安装

    这里就不具体说明,文档地址:deno.land/#installati…

    多版本管理安装

    这里主要讲一下dvm,了解Node 的同学,都知道Node有个版本控制nvm,而Deno也有一个版本控制叫dvm

    With Shell:

    curl -fsSL https://deno.land/x/dvm/install.sh | sh
    复制代码
    

    With PowerShell:

    iwr https://deno.land/x/dvm/install.ps1 -useb | iex
    复制代码
    

    dvm 使用:

    $ dvm --help
    dvm 1.1.10
    Deno Version Manager - Easy way to manage multiple active deno versions.
    
    USAGE:
        dvm [SUBCOMMAND]
    
    OPTIONS:
        -h, --help
                Prints help information
    
        -V, --version
                Prints version information
    
    
    SUBCOMMANDS:
        completions    Generate shell completions
        help           Prints this message or the help of the given subcommand(s)
        info           Show dvm info
        install        Install deno executable to given version [aliases: i]
        list           List installed versions, matching a given <version> if provided [aliases: ls]
        use            Use a given version
    
    Example:
      dvm install 1.3.2     Install v1.3.2 release
      dvm install           Install the latest available version
      dvm use 1.0.0         Use v1.0.0 release
    复制代码
    

    Deno 初体验

    相信一些读者安装完 Deno 已经迫不及待了,现在我们立马来体验一下 Deno 应用程序。首先打开你熟悉的命令行,然后在命令行输入以下命令:

     $ deno run https://deno.land/std/examples/welcome.ts
    
    Download https://deno.land/std/examples/welcome.ts
    Warning Implicitly using latest version (0.68.0) for https://deno.land/std/examples/welcome.ts
    Download https://deno.land/std@0.68.0/examples/welcome.ts
    Check https://deno.land/std@0.68.0/examples/welcome.ts
    Welcome to Deno 🦕
    复制代码
    

    通过观察以上输出,我们可以知道当运行 deno run https://deno.land/std/examples/welcome.ts 命令之后,Deno 会先从https://deno.land/std/examples/welcome.ts URL 地址下载 welcome.ts 文件,该文件的内容是:

    console.log("Welcome to Deno 🦕);
    复制代码
    

    当文件下载成功后,Deno 会对 welcome.ts 文件进行编译,即编译成 welcome.ts.js文件。需要注意的是,如果你在命令行重新运行上述命令,则会执行缓存中已生成的文件,并不会再次从网上下载 welcome.ts 文件。

    $ deno run https://deno.land/std/examples/welcome.ts
    Welcome to Deno 🦕
    复制代码
    

    那如何证明再次执行上述命令时, Deno 会优先执行缓存中编译生成的 JavaScript 文件呢?这里我们要先介绍一下 deno info 命令,该命令用于显示有关缓存或源文件相关的信息:

    $ deno info
    DENO_DIR location: "/Users/sunilwang/.deno"
    Remote modules cache: "/Users/sunilwang/.deno/deps"
    TypeScript compiler cache: "/Users/sunilwang/.deno/gen"
    复制代码
    $ tree $HOME/.deno
    /Users/sunilwang/.deno
    ├── deps
    │   └── https
    │       └── deno.land
    │           ├── 1a52c1fa5e4c9dc57f6866469b3f52feb606c441668dab162bf6424a87e87678
    │           └── 1a52c1fa5e4c9dc57f6866469b3f52feb606c441668dab162bf6424a87e87678.metadata.json
    └── gen
        └── https
            └── deno.land
                ├── 1a52c1fa5e4c9dc57f6866469b3f52feb606c441668dab162bf6424a87e87678.buildinfo
                ├── 1a52c1fa5e4c9dc57f6866469b3f52feb606c441668dab162bf6424a87e87678.js
                └── 1a52c1fa5e4c9dc57f6866469b3f52feb606c441668dab162bf6424a87e87678.meta
    复制代码
    

    在上述的输出信息中,我们看到了 TypeScript compiler cache 这行记录,很明显这是 TypeScript 编译器缓存的目录,进入该目录后,通过一层层的查找,找到了 1a52c1fa5e4c9dc57f6866469b3f52feb606c441668dab162bf6424a87e87678.js 编译后的缓存文件。

    打开目录中 文件,我们可以看到以下内容:

    "use strict";
    console.log("Welcome to Deno 🦕");
    //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiaHR0cHM6Ly9kZW5vLmxhbmQvc3RkQDAuNjguMC9leGFtcGxlcy93ZWxjb21lLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLENBQUMsR0FBRyxDQUFDLG9CQUFvQixDQUFDLENBQUMifQ==%
    复制代码
    

    那么现在问题又来了,如何强制刷新缓存,即重新编译 TypeScript 代码呢?针对这个问题,在运行 deno run 命令时,我们需要添加 --reload 标志,来告诉 Deno 需要重新刷新指定文件:

     $ deno run --reload https://deno.land/std/examples/welcome.ts
    
    Download https://deno.land/std/examples/welcome.ts
    Warning Implicitly using latest version (0.68.0) for https://deno.land/std/examples/welcome.ts
    Download https://deno.land/std@0.68.0/examples/welcome.ts
    Check https://deno.land/std@0.68.0/examples/welcome.ts
    Welcome to Deno 🦕
    复制代码
    

    除了 --reload 标志之外,Deno run 命令还支持很多其他的标志,感兴趣的读者可以运行 deno run --help 命令来查看更多的信息。

    模块规范

    Deno 完全遵循 es module 浏览器实现,所以 Deno 也是如此:

    // 支持
    import * as fs from "https://deno.land/std/fs/mod.ts";
    import { deepCopy } from "./deepCopy.js";
    import foo from "/foo.ts";
    
    // 不支持
    import foo from "foo.ts";
    import bar from "./bar"; // 必须指定扩展名
    复制代码
    

    我们发现其和我们平常在 webpack 或者 ts 使用 es module 最大的不同:

    • 可以通过 import url 直接引用线上资源;
    • 资源不可省略扩展名和文件名。

    权限

    默认情况下,Deno是安全的。因此 Deno 模块没有文件、网络或环境的访问权限,除非您为它授权。在命令行参数中为 deno 进程授权后才能访问安全敏感的功能。

    在以下示例中,mod.ts 只被授予文件系统的只读权限。它无法对其进行写入,或执行任何其他对安全性敏感的操作。

    $ deno run --allow-read mod.ts
    复制代码
    

    权限列表

    以下权限是可用的:

    • -A, --allow-all 允许所有权限,这将禁用所有安全限制。
    • --allow-env 允许环境访问,例如读取和设置环境变量。
    • --allow-hrtime 允许高精度时间测量,高精度时间能够在计时攻击和特征识别中使用。
    • --allow-net=<allow-net> 允许网络访问。您可以指定一系列用逗号分隔的域名,来提供域名白名单。
    • --allow-plugin 允许加载插件。请注意:这是一个不稳定功能。
    • --allow-read=<allow-read> 允许读取文件系统。您可以指定一系列用逗号分隔的目录或文件,来提供文件系统白名单。
    • --allow-run 允许运行子进程。请注意,子进程不在沙箱中运行,因此没有与 deno 进程相同的安全限制,请谨慎使用。
    • --allow-write=<allow-write> 允许写入文件系统。您可以指定一系列用逗号分隔的目录或文件,来提供文件系统白名单。

    权限白名单

    Deno 还允许您使用白名单控制权限的粒度。

    这是一个用白名单限制文件系统访问权限的示例,仅允许访问 /usr 目录,但它会在尝试访问 /etc 目录时失败。

    $ deno run --allow-read=/usr https://deno.land/std/examples/cat.ts /etc/passwd
    
    error: Uncaught PermissionDenied: read access to "/etc/passwd", run again with the --allow-read flag
        at unwrapResponse (rt/10_dispatch_json.js:24:13)
        at sendAsync (rt/10_dispatch_json.js:75:12)
        at async Object.open (rt/30_files.js:45:17)
        at async https://deno.land/std@0.68.0/examples/cat.ts:4:16
    复制代码
    

    改为 /etc 目录,赋予正确的权限,再试一次:

    $ deno run --allow-read=/etc https://deno.land/std/examples/cat.ts /etc/passwd
    复制代码
    

    --allow-write 也一样,代表写入权限。

    网络访问

    // fetch.ts
    const result = await fetch("https://deno.land/");
    复制代码
    

    这是一个设置 host 或 url 白名单的示例:

    $ deno run --allow-net=github.com,deno.land fetch.ts
    复制代码
    

    如果 fetch.ts 尝试与其他域名建立网络连接,那么这个进程将会失败。

    允许访问任意地址:

    $ deno run --allow-net fetch.ts
    复制代码
    

    TCP

    前面我们已经介绍了如何运行官方的 welcome 示例,下面我们来介绍如何使用 Deno 创建一个简单的 TCP echo 服务器。

    // 官方示例代码: https://deno.land/std/examples/echo_server.ts
    
    const hostname = "0.0.0.0";
    const port = 8080;
    const listener = Deno.listen({ hostname, port });
    
    console.log(`Listening on ${hostname}:${port}`);
    
    for await (const conn of listener) {
      Deno.copy(conn, conn);
    }
    复制代码
    

    相信很多读者会跟我一样,直接在命令行运行以下命令:

    $ deno run  https://deno.land/std/examples/echo_server.ts
    
    error: Uncaught PermissionDenied: network access to "0.0.0.0:8080", run again with the --allow-net flag
        at unwrapResponse (rt/10_dispatch_json.js:24:13)
        at sendSync (rt/10_dispatch_json.js:51:12)
        at opListen (rt/30_net.js:33:12)
        at Object.listen (rt/30_net.js:204:17)
        at https://deno.land/std@0.68.0/examples/echo_server.ts:4:23
    复制代码
    

    很明显是权限错误,从错误信息中,Deno 告诉我们需要设置 --allow-net 标志,以允许网络访问。为什么会这样呢?这是因为 Deno 是一个 JavaScript/TypeScript 的运行时,默认使用安全环境执行代码。下面我们添加 --allow-net 标志,然后再次运行 echo_server.ts 文件:

    $ deno run --allow-net https://deno.land/std/examples/echo_server.ts
    
    Listening on 0.0.0.0:8080
    复制代码
    

    当服务器成功运行之后,我们使用 nc 命令来测试一下服务器的功能:

    $ nc localhost 8080
    hell semlinker
    hell semlinker
    复制代码
    

    HTTP

    友情提示:在实际开发过程中,你可以从 deno.land/std 地址获取所需的标准库版本。示例中我们显式指定了版本,当然你也可以不指定版本,比如这样:deno.land/std/http/se…

    在上述代码中,我们导入了 Deno 标准库 http 模块中 serve 函数,然后使用该函数快速创建 HTTP 服务器,该函数的定义如下:

    // http_server.ts
    import { serve } from "https://deno.land/std/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" });
    }
    复制代码
    

    创建完 HTTP 服务器,我们来启动该服务器,打开命令行输入以下命令:

    $ deno run --allow-net ./http_server.ts
    
    Download https://deno.land/std/http/server.ts
    Warning Implicitly using latest version (0.68.0) for https://deno.land/std/http/server.ts
    Download https://deno.land/std@0.68.0/http/server.ts
    Download https://deno.land/std@0.68.0/encoding/utf8.ts
    Download https://deno.land/std@0.68.0/io/bufio.ts
    Download https://deno.land/std@0.68.0/_util/assert.ts
    Download https://deno.land/std@0.68.0/async/mod.ts
    Download https://deno.land/std@0.68.0/http/_io.ts
    Download https://deno.land/std@0.68.0/textproto/mod.ts
    Download https://deno.land/std@0.68.0/http/http_status.ts
    Download https://deno.land/std@0.68.0/async/deferred.ts
    Download https://deno.land/std@0.68.0/async/delay.ts
    Download https://deno.land/std@0.68.0/async/mux_async_iterator.ts
    Download https://deno.land/std@0.68.0/async/pool.ts
    Download https://deno.land/std@0.68.0/bytes/mod.ts
    Check file:///Users/sunilwang/github/deno_test/http_server.ts
    
    http://localhost:8000/
    复制代码
    

    接着打开浏览器,在地址栏上输入 http://localhost:8080/ 地址,之后在当前页面中会看到以下内容:

    Hello World
    复制代码
    

    HTTP 中间件框架 Oak

    Oak 是一个受到 Koa 启发的项目,Koa 是一个很受欢迎并提供 HTTP 服务的 Node.js 中间件框架。我们将会使用 oak 和 Deno 构建一个 Hello Wrod 小应用。

    我们需要在我们的项目库中创建两个文件,分别是 serve.tsroutes.ts。一个用于应用,另一个则是用于服务的路由。

    serve.ts 文件的内容如下面的文件显示。看看我们是如何在 serve.ts 文件中从 oak 引入 Application 模块的。

    // serve.ts
    import { Application, Context } from 'https://deno.land/x/oak/mod.ts'
    import router from './router.ts'
    
    const app = new Application()
    const PORT = 8080
    
    // Logger
    app.use(async (ctx: Context, next: Function) => {
      await next()
      const rt = ctx.response.headers.get('X-Response-Time')
      console.log(`${ctx.request.method} ${ctx.request.url} - ${rt}`)
    })
    
    // Timing
    app.use(async (ctx: Context, next: Function) => {
      const start = Date.now()
      await next()
      const ms = Date.now() - start
      ctx.response.headers.set('X-Response-Time', `${ms}ms`)
    })
    
    app.use(router.routes())
    app.use(router.allowedMethods())
    // 404
    app.use((ctx: Context) => {
      ctx.response.status = 404;
      ctx.response.body = { msg: "Not Found" };
    })
    console.log(`Listening on port ${PORT}, http://localhost:${PORT}`)
    
    await app.listen({ port: PORT })
    复制代码
    

    很显然 Oak 的的灵感来自于 Koa,而路由中间件的灵感来源于 koa-router 这个库。如果你以前使用过 Koa 的话,相信你会很容易上手 Oak。我们来看下面的routes.ts

    // routes.ts
    import { Router, RouterContext } from 'https://deno.land/x/oak/mod.ts'
    
    const router = new Router()
    
    router.get('/', (context: RouterContext) => {
      context.response.body = 'Hello World!'
    })
    
    router.get('/router', (context: RouterContext) => {
      context.response.body = 'Hello Router!'
    })
    
    router.get('/router/:id', (context: RouterContext) => {
      context.response.body = `Hello Router, Id:${context.params.id}!`
    })
    
    export default router
    复制代码
    

    创建完 oak 服务,我们来启动该服务器,打开命令行输入以下命令:

     $ deno run --allow-net ./serve.ts
    
    Check file:///Users/sunilwang/github/deno_test/serve.ts
    Listening on port 8080, http://localhost:8080
    复制代码
    

    接着打开浏览器,在地址栏上依次输入

    之后在当前页面中会看到以下内容:

    GET http://localhost:8080/ - 3ms
    GET http://localhost:8080/router - 1ms
    GET http://localhost:8080/router/123 - 0ms
    GET http://localhost:8080/404 - 0ms
    复制代码
    

    可能碰到的问题

    在开发过程中,经常遇到DNS解析域名错误的问题。导致js依赖包没法下载

    我们一起来完成以下步骤:

    • 先来发现问题
    • 域名是否能访问?
    • 解析域名(www.ipaddress.com/)的IP。在没有使用的IP。在没有使用) openssl 或 shadowsocks 情况下是否能ping通
    • 修改Hosts文件
      • win: (C:\Windows\System32\drivers\etc)
      • mac (/etc/hosts)
    • 再重新run一下应用

    总结

    Deno 是个很有意思的小工具,但不是下一代的 Node.js,如果有一天有大流量的项目大面积使用,才有学的价值,现在这个时间点只能作为玩具玩玩。

    Node.js 还会持续繁荣,就像因为早起的一些设计缺陷,JavaScript 的作者不是很喜欢 js,但是由于出现的时候填补了浏览器脚本的空白,外加生态的繁荣,让 js 一直火爆到今天。

    期待 Deno 有新的发展,也看好 Node.js 继续繁荣。

    最后送大家自己收藏的Deno 资源全图谱:github.com/hylerrix/aw…

    参考

    福利部分

    阿里云服务器

    阿里云企业级服务器限时特惠

    阿里云建站 速成美站

    阿里云SaaS商标注册服务

    CDN爆款产品


    腾讯云服务器

    腾讯云境外服务器

    腾讯云数据库

    相关文章

      网友评论

        本文标题:不懂Deno?这篇文章看了就会了

        本文链接:https://www.haomeiwen.com/subject/zxchyktx.html