zoukankan      html  css  js  c++  java
  • ts装饰器基本的用法,并使用装饰器完成@Controller等装饰器,简化路由开发

    TS

    TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集,而且本质上向这个语言添加了可选的静态类	型和基于类的面向对象编程。
    TypeScript 扩展了 JavaScript 的句法,所以任何现有的 JavaScript 程序可以不加改变的在 TypeScript 下工作。	TypeScript 是为大型应用之开发而设计,而编译时它产生 JavaScript 以确保兼容性。
    这段是我在网上扒下来的
    

    为什么要用TS对express进行封装呢?

    主要是为了用ts里面的装饰器,虽然装饰器已经是es7里面的一个提案了,但是在js里面
    使用的话还需要用babel进行配置(太麻烦了,图省事所以用Ts)
    

    我们先来了解一下什么是装饰器

    装饰器(Decorator) 仅提供定义劫持,能够对类及其方法、方法入参、属性的定义
    并没有提供任何附加元数据的功能。
    主要是为了提供附加的功能
    

    那么TS里面的装饰器如何使用呢

    //需要在tsconfig开启装饰器
    {
    	"compilerOptions": {
        	"experimentalDecorators": true
    	}
    }
    
    二话不说npm一把梭
    npm init -y //使用默认配置
    npm i typescript -S //安装ts包
    tsc --init //初始化ts配置
    mkdir src //创建src目录
    mkdir decorators //创建装饰器目录
    touch common.ts //创建common 用于辨析而装饰器
    touch index.ts //创建index文件
    到此为止已经搭建了项目的基本架构
    

    接下来我们介绍一下TS里面有那些类型的装饰器

    类装饰器

    应用于类的构造函数
    参数:target (类)
    写法:function classDecorators(target:any){
    	//我们在这个地方来对象进行操作
        //比我我们给类里面的原型加一点东西
        target.prototype.usernmae='沉默'
    }
    

    属性装饰器

    应用于属性上面的装饰器
    参数:targte (类),name(当前属性的名字)
    写法:
    
    function attrDecorators(target:any,name:string){
        target[name]=150 //我们给当前属性赋了一个值
    }
    

    方法装饰器

    应用于方法上面的装饰器
    参数:targte (类),name(当前方法的名字),descriptor (当前方法的描述对象)
    写法:
    
    function funcDecorators(target:any,name:string,descriptor:PropertyDescriptor){
        //我们在这个装饰器里面来写一些,在每次执行的时候打印一个时间
        let origin=descriptor.value;//这个就是当前方法的内容,我们给他保存起来
        descriptor.value=function(){
            console.log(new Date())
            //执行我们保存的那个方法 arguments是传进来的那些参数
            return origin.apply(this,arguments)
        }
        return descriptor
    }
    

    方法参数的装饰器

    应用于方法参数上面的装饰器
    参数:target(类),name(当前参数方法的名称),index(按当前参数的在形参列表里面的索引值)
    
    function paramDecorators(target:any,name:string,index:number){
    	//这个装饰器也没有上面例子好举了
    }
    

    这是我们讲的ts的装饰器,我们一起加在一个类上面,来看看他的执行顺序是上面样的


    我们可以看到先走的是属性的装饰器,之后是参数在是方法然后是类

    我看别人的装饰器里面都能传值,为什么我看你这个不能呢!

    不要着急,马上就会讲到,TS里面还有一个叫装饰器工厂的装饰器就是用来完成这个工作的。
    装饰器工厂的定义方法就是返回的是一个方法
    来看例子
    
    function classDesc(path:string){
      //这就是一个装饰器工厂,上面的path就是你可以传进来的值
      console.log(path,'class')
      return (target:any)=>{
          console.log(typeof target,'class')
      }
    }
    
    其他类型的装饰器的写法也都类似与这样,只不过是参数不一样
    

    接下来我们讲讲js里面的元数据

    元数据即为描述数据的数据,也有个称呼叫
    注解(Annotation) 仅提供附加元数据支持,并不能实现任何操作。需要另外的 Scanner 根据元数据执行相应操作。
    但是我一听到注解就像Java里面的东西
    总而言之,装饰器和注解是不一样的,我上面有讲过装饰器的概念,主要是为了提供附加功能,注解不提供这些,它只提供元数据的支持,不能操作任何东西,所以只能根据元数据来实现具体的操作。
    

    知道元数据之后我们就可以来封装标题上面所讲的装饰器了,封装那些需要用到

    我们最终会是实现这些装饰器
    Controller Get Post Body Query 等
    之前我们以及创建了common.ts文件,那么就在这里先创建第一个装饰器(Controller)
    
    const PATH_METADETA='path';
    const PATH_METADETA = "path";
    const PARAMS_METADETA = "params";
    function Controller(path:string):ClassDecorator{
    	//创建装饰器工厂,可以用来接收参数,上面以及讲过了
      return (target)=>{
      	/*
          给当前类添加一个元数据
          Reflect.defineMetadata用来添加元数据,总共有四个值第一个是key,唯一的,
          第二个是值,第三个是当前对象,第四个是方法名,但我们当前只给类设置元数据,就不需要
          填方法
          为什么要设置元数据呢,装饰器本来就是用来扩展方法的,但是他是用来对本方法进行扩展的,
          在此装饰器里面我们拿不到,具体的数据,所以我们用元数据去设置具体的数据,在其他地方进行
          通用的注册使用,这个装饰器的作用就是用设置元数据的(我也不知道这样讲能不能明白:))
          */
      	Reflect.defineMetadata(PATH_METADETA, path, target);//当前装饰器传过来
          //的数据给设置进入了类的元数据里面了,日常的时候这个类并不影响我们的使用
      }
    }
    //我们需要编写Post Get 这些的方法,本质上这些方法除了Post,Get这些不一样,传过来的东西都
    //是一样的
    function createdMethod(methods: string) {
      return (path: string): MethodDecorator => {
        return (target: any, key: string | symbol, descriptor: any) => {
          //给当前方法设置了一个
          Reflect.defineMetadata(PATH_METADETA, path, target, key);
          //添加方法元数据,描述当前方法为什么方法
          Reflect.defineMetadata(METHOD_METADETA, methods, target, key);
        };
      };
    }
    
    目前我们先写这两个装饰器,之后在写params的装饰器
    

    装饰器虽然是写好了

    但是我们在那里去用呢,不会这么简单吧。
    当然不会那么简单,我们现在还需要一个注册的地方,我们需要让express拿到装饰器装饰上去的
    元数据。
    
    我们在decorators里面在建一个文件 叫	register.ts 用来注册我们装饰的类
    下面是代码
    
    import {Router} from 'express';
    function register(app:any,controller:Array<any>){
    	const router=Router();
     	controller.forEach((v:any)=>{
             //这个v就是类
             //获取当前类key为path的元数据 也就是之前我们装饰器设置的值
             let classPath=Reflect.getMetadata('path',v);
             //遍历当前属性,查询当前类属性上面有哪些元数据
             for(const key in new v()){
             	//这个getMetadata里面的key是当前遍历的方法的名称
                 //也就拿到当前方法设置的元数据
                 let attrMethod = Reflect.getMetadata('method', new v(), key);
                 let attrPath = Reflect.getMetadata('path', new v(), key);
                 //我们在定义一个回调方法,这个就是express的回调方法
                 let fn:any=(req,res,next)=>{
                 	//我们先把我们当前的方法给执行一下,在传两个参数(这个方法就是@Post@Get这个中下面的方法)
                   let result=new v()[key].apply(new v(),[req,res]);
                   if (result instanceof Promise) {
                   	result.then(value => {
                   		!res.headersSent && res.send(value);
                   	}).catch(err => {
                   		next(err)
                   	})
                   } else if (result !== undefined) {
                   		!res.headersSent && res.send(result);
                   }
                     
                 }
                 //组装一下路由的路径
                 let routerParams = [classPath + attrPath]
                 //在添加回调
                 routerParams.push(fn)
                 //去调用router方法
                 router[attrMethod].apply(router, routerParams)
             }
             //用来解决body
             app.use(bodyParser.json())
             app.use(bodyParser.urlencoded({ extended: false }))
             app.use(router)//使用路由
     	})
    }
    
    在index.ts 里面启动express并注册,我们的装饰器应该就能和router联动起来了
    
      register(app,[UserController]);
    
      var server = app.listen(3122, function () {
          var host = server.address().address;
          var port = server.address().port;
          console.log('Example app listening at http://%s:%s', host, port);
      })
    
    自此也就算是写完了!我也是研究了一下午,看了github上面的一些主流的框架的源码,剥离出来的简单的版本
    
    我也是一个没干多久小菜鸟,希望这个篇文]章对你们有所帮助
    我自己写的那个项目在[github](https://github.com/mrzhouxl/express-decorator)
  • 相关阅读:
    Educational CF # 17 C 二分,字符串 D 最长路,dp
    HDU-1878 判断无向图欧拉回路,水
    LightOJ-1094 求树直径,水
    POJ-1144 Tarjan求割点,双连通模板题
    HDU-1269 Tarjan求强连通分量,模板题
    POJ-1094 拓扑排序
    POJ-1847 最短路裸题,Floyd, Bellman, Dijkstra, Spfa
    LightOJ-1005 组合数学,组合数水题
    CF #392(2) C 暴力模拟
    Android xUtils3使用
  • 原文地址:https://www.cnblogs.com/silentCM/p/14509664.html
Copyright © 2011-2022 走看看