美文网首页Flutter圈子
Deno从零到架构级系列(一)——开篇

Deno从零到架构级系列(一)——开篇

作者: 飝狐 | 来源:发表于2020-08-04 17:38 被阅读0次
    deno

    好久没在简书上发文了。从Deno开始,飞狐带来的绝对到目前为止前所未有的Deno系列。话不多说,用技术说话。

    你学不动的 Deno 来了

    还记得 Github 上那个让人学不动的 Deno 么?就在2020年5月13日,Deno1.0正式发布。作为新晋网红运行时,Deno真的会替代 Node 吗?这个问题可以追溯到2018年,从Node之父 Ryan Dahl的演讲说起,Ryan在演讲中谈及对Node有十大不满之处,并且在演讲的最后公布了Deno项目。我在这里只列三大新特性。

    • 首先,Deno 作为一个JavaScript/TypeScript 运行时,底层基于性能超高的 Rust 编写,在性能和存储安全上有先天的优势。
    • 其次,Deno 拥有完整的标准库,不再有 NPM 或 node_modules 文件夹,允许从任何地方导入所需模块。
    • 另外,Deno 集成 TypeScript,不再像以前一样借助工具编译,而是通过内部转换。不过似乎又要剥离。

    综上所述,你会发现,Deno真的是青出于蓝而胜于蓝。是否替代,只是时间问题而已。我也看到很多人在做deno和node的性能比较,但在目前我认为做这两者的性能比较完全没有必要。

    deno

    安装

    我们一开始甭管deno底层用的go还是rust,为啥要从go换成rust、或者是deno的技术架构是咋样?这些目前都不要关心,我们就把deno当成一个新的运行时,只做运行时。从零到一,通过搭建一套脚手架,再慢慢去深入。Deno可以在Mac、Linux、Windows三大系统上运行。Deno也不需要其他依赖。通过如下方式安装:

    Shell (Mac, Linux): curl -fsSL https://deno.land/x/install/install.sh | sh
    PowerShell (Windows): iwr https://deno.land/x/install/install.ps1 -useb | iex
    Homebrew (Mac): brew install deno
    Chocolatey (Windows): choco install deno
    Scoop (Windows): scoop install deno
    Cargo: cargo install deno

    这是来自官方的安装教程,更多的安装方式,大家可以去官网看。我用的Mac,也安装了homebrew,所以我用的brew。

    第一个例子

    第一个例子也是来自官方,每一行我都加了解释。如下:

    // 创建文件 /server.ts
    // 不需要像node一样去npm,这里直接引入
    import { serve } from "https://deno.land/std@0.63.0/http/server.ts";
    // 构建服务,设置端口
    const s = serve({ port: 8000 });
    // 这里是直接返回
    for await (const req of s) {
      req.respond({ body: "Hello World\n" });
    }
    

    整个代码非常简单,语法也是ts,和node非常像。有node基础的同学直接入手。
    运行命令:deno run --allow-net ./server.ts,
    然后在浏览器打开http://localhost:8000,就可以看到hello world了,如下图:

    hello world

    框架之选

    众所周知,node的框架比较成熟,国外的有nest.js、国内的有egg.js。而目前deno的生态其实并不成熟,框架也都是模仿其他框架,多的就不介绍了,这里我给大家推荐两个框架。如下:

    • oak
    • alosaur

    推荐oak的原因很简单,就是我们整个脚手架搭建都是基于oak的。oak模仿的是node的框架koa,从名字也可以看出来。而推荐alosaur的原因就一点,可学性很强。有兴趣可以去看这个框架的源码,非常多值得借鉴的地方。飞狐教大家搭建脚手架,虽然不用这个框架,但很多底层实现也是借鉴的这个框架,比如注解路由。好啦,框架就介绍到这里啦,后面我们再慢慢深入。
    话不多说,我们先来个oak例子:

    // 引入oak框架
    import { Application } from "https://deno.land/x/oak/mod.ts";
    // 初始化
    const app = new Application();
    // 跟koa一样,运行上下文
    app.use((ctx) => {
      ctx.response.body = "Hello World!";
    });
    // 监听端口
    await app.listen({ port: 8000 });
    

    在运行的时候,可能会报错,如下:

    报错

    是不是有点受挫。其实大可不必,这个问题是deno版本迭代时std版本未更新至最新引起的。
    注意,deno是新玩意儿,有不少坑,官方也在频繁迭代解决这些坑。 所以,理解万岁。最简单的解决办法只需要把oak的版本升级成最新就好了,如下面的例子。

    路由

    路由部分oak跟koa不一样的是,oak直接提供路由,只需引入即可。如下:

    // 升级到oak的最新版本
    import { Application, Router } from "https://deno.land/x/oak@v6.0.1/mod.ts";
    // 这是官方的例子
    const books = new Map<string, any>();
    books.set("1", {
      id: "1",
      title: "听飞狐聊deno",
      author: "飞狐",
    });
    // 创建路由
    const router = new Router();
    // 路由,和koa-router的用法一样
    router
      .get("/", (context) => {
        context.response.body = "Hello world!";
      })
      .get("/book", (context) => {
        context.response.body = Array.from(books.values());
      })
      .get("/book/:id", (context) => {
        if (context.params && context.params.id && books.has(context.params.id)) {
          context.response.body = books.get(context.params.id);
        }
      });
    
    const app = new Application();
    // 应用路由
    app.use(router.routes());
    // 允许路由中间件引入
    app.use(router.allowedMethods());
    
    await app.listen({ port: 8000 });
    

    切记,一定要用最新的版本。同样的,输入命令运行。如下图(postman测试接口):

    deno接口

    拆分路由

    MVC模式大家都不陌生了,我们这里仅仅做个简单的拆分,把路由和控制层独立出去。 创建一个controller文件夹,在该文件夹下创建一个bookController文件,如下:

    // 创建bookController.ts,
    // 我们把数据先移到这来再说
    const books = new Map<string, any>();
    
    books.set("1", {
      id: "1",
      title: "听飞狐聊deno",
      author: "飞狐",
    });
    // 这里直接返回一个对象,把路由映射的方法也搬到这里
    export default {
      getbook: ((context: any) => {
        context.response.body = Array.from(books.values());
      }),
      getbookById: ((context: any) => {
        if (context.params && context.params.id && books.has(context.params.id)) {
          context.response.body = books.get(context.params.id);
        }
      })
    }
    

    再在根目录下创建router.ts,代码如下:

    import { Router } from 'https://deno.land/x/oak@v6.0.1/mod.ts';
    // 引入控制层的文件
    import bookController from './controller/bookController.ts'
    
    const router = new Router();
    router
      .get("/", (context) => {
        context.response.body = "Hello world!";
      })
      .get("/book", bookController.getbook)
      .get("/book/:id", bookController.getbookById)
    
    export default router
    

    原来的入口文件server.ts,就变得十分简洁了,如下:

    import { Application } from 'https://deno.land/x/oak@v6.0.1/mod.ts';
    import router from './router.ts'
    
    const app = new Application();
    app.use(router.routes());
    app.use(router.allowedMethods());
    
    await app.listen({ port: 8000 });
    

    这样就很简洁了,也利于扩展。比如:

    • 后续用到业务逻辑的时候,我们可以再添加service层
    • 后续用到中间件的时候,我们可以再添加middleware层
    • 后续扩展异常处理等等

    在这个基础上,在后面的篇章里我们再继续深入。今天的内容其实已经完成了。这里再介绍一下,为啥说这个系列是架构级思想呢?因为我们在写代码的时候,就会基于一些特定的场景考虑,比如微服务等。就像golang里的go-micro,会集成grpc、etcd、gin等等一样。有兴趣我也可以写一套golang系列分享给大家,小弟我有太多想和大家伙儿分享的东东了,比如,TensorFlow.js、Julia量化交易等。呃~好像跑偏了,还是拉回来先送大家一个结尾彩蛋,为下一篇打基础。

    彩蛋(改造入口文件)

    从现在起,后面的部分我们要用类class的方式来写代码了,先从入口文件开始,改造如下

    // server.ts
    import { Application } from 'https://deno.land/x/oak@v6.0.1/mod.ts';
    import router from './router.ts'
    
    const app = new Application();
    class Server {
      constructor () {
        this.init()
      }
    
      async init () {
        app.use(router.routes());
        app.use(router.allowedMethods());
        this.listen()
      }
    
      async listen () {
        await app.listen({ port: 8000 });
      }
    }
    
    new Server()
    

    这里我不用注释了,单独讲解一下这个地方,主要三点:

    1. 创建一个server类,创建一个初始化函数init()
    2. 在构造函数里调用初始化函数,把路由,中间件,监听函数,一切需要初始化执行的函数都拎进去
    3. 最后运行这个类。这样做的好处,一个标准化,一个是利于扩展。

    下回预告

    回顾一下,这篇内容很浅显,主要是安装deno,简单实用oak框架,拆分路由而已。
    下回我们直接聊typescript装饰器模式,注解,并且实现注解路由。控制层也按照类class的写法,就跟Java的springmvc一样,如下图:

    注解路由

    从上图可以看到,这样根本就不需要router文件啦。不管是node、deno还是golang,飞狐真的很不喜欢一个单独的router文件去维护路由,太麻烦。所以每次搭架子的时候,我一定是先把路由给搞定了,免得麻烦。现在理解为啥下回直接开干注解路由了吧,deno的注解路由还是很一波三折的,因为完全照搬node会有些坑。期待吧,嘿嘿😜~

    相关文章

      网友评论

        本文标题:Deno从零到架构级系列(一)——开篇

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