使用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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。