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)