使用typescript改造koa开发框架的实现
强类型的TypeScript开发体验和维护项目上相比JavaScript有着明显的优势,那么对常用的脚手架进行改造也就势在必行了。
接下来开始对基于koa框架的node后端脚手架进行改造:
- 项目开发环境和typescript编译环境的搭建;
- 对node、koa、koa中间件和使用到的库添加类型化支持;
- 基于typesript的特性改造项目。
项目开发环境搭建
基于gulp搭建开发编译环境,gulp-typescript插件用于编译typescript文件,gulp-nodemon则可以监控文件内容的变更,自动编译和重启node服务,提升开发效率。
npminstall-Dgulpgulp-nodemongulp-typescriptts-nodetypescript
gulp的配置
gulpfile.js的设置
const{src,dest,watch,series,task}=require('gulp');
constdel=require('del');
constts=require('gulp-typescript');
constnodemon=require('gulp-nodemon');
consttsProject=ts.createProject('tsconfig.json');
functionclean(cb){
returndel(['dist'],cb);
}
//输出js到dist目录
functiontoJs(){
returnsrc('src/**/*.ts')
.pipe(tsProject())
.pipe(dest('dist'));
}
//nodemon监控ts文件
functionrunNodemon(){
nodemon({
inspect:true,
script:'src/app.ts',
watch:['src'],
ext:'ts',
env:{NODE_ENV:'development'},
//tasks:['build'],
}).on('crash',()=>{
console.error('Applicationhascrashed!\n');
});
}
constbuild=series(clean,toJs);
task('build',build);
exports.build=build;
exports.default=runNodemon;
typescript的配置
tsconfig.json的设置
{
"compilerOptions":{
"baseUrl":".",//import的相对起始路径
"outDir":"./dist",//构建输出目录
"module":"commonjs",
"target":"esnext",//node环境支持esnext
"allowSyntheticDefaultImports":true,
"importHelpers":true,
"strict":false,
"moduleResolution":"node",
"esModuleInterop":true,
"forceConsistentCasingInFileNames":true,
"noImplicitAny":true,
"suppressImplicitAnyIndexErrors":true,
"noUnusedParameters":true,
"noUnusedLocals":true,
"noImplicitReturns":true,
"experimentalDecorators":true,//开启装饰器的使用
"emitDecoratorMetadata":true,
"allowJs":true,
"sourceMap":true,
"paths":{
"@/*":["src/*"]
}
},
"include":[
"src/**/*"
],
"exclude":[
"node_modules",
"dist"
]
}
eslint的配置
当然eslint也要添加对typescript对支持
npminstall-D@typescript-eslint/eslint-plugin@typescript-eslint/parser
.eslintrc.json的设置
{
"env":{
"es6":true,
"node":true
},
"extends":[
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended"
],
"globals":{
"Atomics":"readonly",
"SharedArrayBuffer":"readonly"
},
"parser":"@typescript-eslint/parser",
"parserOptions":{
"ecmaVersion":2018,
"sourceType":"module"
},
"plugins":[
"@typescript-eslint"
],
"rules":{
"indent":["warn",2],
"no-unused-vars":0
}
}
package.json运行配置
最后就是设置package.json的scripts
"scripts":{
"start":"gulp",//dev
"build":"gulpbuild",//output
"eslint":"eslint--fix--ext.js,.tssrc/",
"server":"exportNODE_ENV=production&&nodedist/app"//productionserver
},
添加类型化支持
项目主要使用到了以下的组件
jsonwebtoken
koa
koa-body
koa-compress
koa-favicon
koa-logger
koa-router
koa-static
koa2-cors
log4js
那么就要安装对应的type文件,当然别忘了@types/node
npminstall-D@types/jsonwebtoken@types/koa@types/koa-compress@types/koa-favicon@types/koa-logger@types/koa-router@types/koa-static@types/koa2-cors@types/log4js@types/node
使用typescript装饰器改造项目
.netmvc框架有个很便利的地方就是使用装饰器对控制器进行配置,现在通过typescript的装饰器也可以实现相同的功能。这里需要使用到反射相关的库reflect-metadata,用过Java或C#的小伙伴,对反射的原理一定不陌生。
定义http请求的装饰器
我们再也不需要在路由配置和控制器方法之前来回查找和匹配了
import'reflect-metadata'
import{ROUTER_MAP}from'../constant'
/**
*@desc生成httpmethod装饰器
*@param{string}method-httpmethod,如get、post、head
*@returnDecorator-装饰器
*/
functioncreateMethodDecorator(method:string){
//装饰器接收路由path作为参数
returnfunctionhttpMethodDecorator(path:string){
return(proto:any,name:string)=>{
consttarget=proto.constructor;
constrouteMap=Reflect.getMetadata(ROUTER_MAP,target,'method')||[];
routeMap.push({name,method,path});
Reflect.defineMetadata(ROUTER_MAP,routeMap,target,'method');
};
};
}
//导出httpmethod装饰器
exportconstpost=createMethodDecorator('post');
exportconstget=createMethodDecorator('get');
exportconstdel=createMethodDecorator('del');
exportconstput=createMethodDecorator('put');
exportconstpatch=createMethodDecorator('patch');
exportconstoptions=createMethodDecorator('options');
exportconsthead=createMethodDecorator('head');
exportconstall=createMethodDecorator('all');
装饰控制器的方法
exportdefaultclassSign{
@post('/login')
asynclogin(ctx:Context){
const{email,password}=ctx.request.body;
constusers=awaituserDao.getUser({email});
//...
returnctx.body={
code:0,
message:'登录成功',
data
};
}
@post('/register')
asyncregister(ctx:Context){
const{email,password}=ctx.request.body;
constsalt=makeSalt();
//...
returnctx.body={
code:0,
message:'注册成功!',
data
}
}
}
收集元数据和添加路由
我们已经把装饰器添加到对应控制器的方法上了,那么怎么把元数据收集起来呢?这就需要用到node提供的fs文件模块,node服务第一次启动的时候,扫描一遍controller文件夹,收集到所有控制器模块,结合装饰器收集到的metadata,就可以把对应的方法添加到koa-router。
import'reflect-metadata'
importfsfrom'fs'
importpathfrom'path'
import{ROUTER_MAP}from'./constant'
import{RouteMeta}from'./type'
importRouterfrom'koa-router'
constaddRouter=(router:Router)=>{
constctrPath=path.join(__dirname,'controller');
constmodules:ObjectConstructor[]=[];
//扫描controller文件夹,收集所有controller
fs.readdirSync(ctrPath).forEach(name=>{
if(/^[^.]+?\.(t|j)s$/.test(name)){
modules.push(require(path.join(ctrPath,name)).default)
}
});
//结合meta数据添加路由
modules.forEach(m=>{
constrouterMap:RouteMeta[]=Reflect.getMetadata(ROUTER_MAP,m,'method')||[];
if(routerMap.length){
constctr=newm();
routerMap.forEach(route=>{
const{name,method,path}=route;
router[method](path,ctr[name]);
})
}
})
}
exportdefaultaddRouter
最后
这样对koa项目脚手架的改造基本完成,源码请查看koa-server
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。