使用typescript改造koa开发框架的实现


Posted in Javascript onFebruary 04, 2020

强类型的 TypeScript 开发体验和维护项目上相比 JavaScript 有着明显的优势,那么对常用的脚手架进行改造也就势在必行了。

接下来开始对基于 koa 框架的 node 后端脚手架进行改造:

  1. 项目开发环境 和 typescript 编译环境的搭建;
  2. 对 node、koa、koa中间件和使用到的库 添加类型化支持;
  3. 基于 typesript 的特性改造项目。

项目开发环境搭建

基于 gulp 搭建开发编译环境,gulp-typescript 插件用于编译 typescript 文件, gulp-nodemon 则可以监控文件内容的变更,自动编译和重启node服务,提升开发效率。

npm install -D gulp gulp-nodemon gulp-typescript ts-node typescript

gulp 的配置

gulpfile.js 的设置

const { src, dest, watch, series, task } = require('gulp');
const del = require('del');
const ts = require('gulp-typescript');
const nodemon = require('gulp-nodemon');
const tsProject = ts.createProject('tsconfig.json');

function clean(cb) {
 return del(['dist'], cb);
}

// 输出 js 到 dist目录
function toJs() {
 return src('src/**/*.ts')
  .pipe(tsProject())
  .pipe(dest('dist'));
}

// nodemon 监控 ts 文件
function runNodemon() {
 nodemon({
  inspect: true,
  script: 'src/app.ts',
  watch: ['src'],
  ext: 'ts',
  env: { NODE_ENV: 'development' },
  // tasks: ['build'],
 }).on('crash', () => {
  console.error('Application has crashed!\n');
 });
}

const build = 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 对支持

npm install -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": "gulp build", // output
 "eslint": "eslint --fix --ext .js,.ts src/",
 "server": "export NODE_ENV=production && node dist/app" // production server
},

添加类型化支持

项目主要使用到了以下的组件

jsonwebtoken
koa
koa-body
koa-compress
koa-favicon
koa-logger
koa-router
koa-static
koa2-cors
log4js

那么就要安装对应的 type 文件,当然别忘了 @types/node

npm install -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 装饰器 改造项目

.net mvc 框架有个很便利的地方就是 使用装饰器对控制器进行配置,现在通过 typescript 的装饰器也可以实现相同的功能。这里需要使用到反射相关的库 reflect-metadata,用过 Java 或 C# 的小伙伴,对反射的原理一定不陌生。

定义http请求的装饰器

我们再也不需要在路由配置和控制器方法之前来回查找和匹配了

import 'reflect-metadata'
import { ROUTER_MAP } from '../constant'

/**
 * @desc 生成 http method 装饰器
 * @param {string} method - http method,如 get、post、head
 * @return Decorator - 装饰器
 */
function createMethodDecorator(method: string) {
 // 装饰器接收路由 path 作为参数
 return function httpMethodDecorator(path: string) {
  return (proto: any, name: string) => {
   const target = proto.constructor;
   const routeMap = Reflect.getMetadata(ROUTER_MAP, target, 'method') || [];
   routeMap.push({ name, method, path });
   Reflect.defineMetadata(ROUTER_MAP, routeMap, target, 'method');
  };
 };
}

// 导出 http method 装饰器
export const post = createMethodDecorator('post');

export const get = createMethodDecorator('get');

export const del = createMethodDecorator('del');

export const put = createMethodDecorator('put');

export const patch = createMethodDecorator('patch');

export const options = createMethodDecorator('options');

export const head = createMethodDecorator('head');

export const all = createMethodDecorator('all');

装饰控制器的方法

export default class Sign {
  
 @post('/login')
 async login (ctx: Context) {
  const { email, password } = ctx.request.body;
  const users = await userDao.getUser({ email });
  // ...
  return ctx.body = {
   code: 0,
   message: '登录成功',
   data
  };
 }

 @post('/register')
 async register (ctx: Context) {
  const { email, password } = ctx.request.body;
  const salt = makeSalt();
  // ...
  return ctx.body = {
   code: 0,
   message: '注册成功!',
   data
  }
 }
 
}

收集元数据和添加路由

我们已经把装饰器添加到对应控制器的方法上了,那么怎么把元数据收集起来呢?这就需要用到 node 提供的 fs 文件模块,node服务第一次启动的时候,扫描一遍controller文件夹,收集到所有控制器模块,结合装饰器收集到的metadata,就可以把对应的方法添加到 koa-router。

import 'reflect-metadata'
import fs from 'fs'
import path from 'path'
import { ROUTER_MAP } from './constant'
import { RouteMeta } from './type'
import Router from 'koa-router'

const addRouter = (router: Router) => {
 const ctrPath = path.join(__dirname, 'controller');
 const modules: 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 => {
  const routerMap: RouteMeta[] = Reflect.getMetadata(ROUTER_MAP, m, 'method') || [];
  if (routerMap.length) {
   const ctr = new m();
   routerMap.forEach(route => {
    const { name, method, path } = route;
    router[method](path, ctr[name]);
   })
  }
 })
}

export default addRouter

最后

这样对koa项目脚手架的改造基本完成,源码请查看koa-server

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
在视频前插入广告
Nov 20 Javascript
JavaScript 10件让人费解的事情
Feb 15 Javascript
浅谈JavaScript函数参数的可修改性问题
Dec 05 Javascript
Javascript 正则表达式实现为数字添加千位分隔符
Mar 10 Javascript
jQuery找出网页上最高元素的方法
Mar 20 Javascript
js库Modernizr的介绍和使用
May 07 Javascript
微信小程序数据存储与取值详解
Jan 30 Javascript
vue checkbox 全选 数据的绑定及获取和计算方法
Feb 09 Javascript
Vue利用canvas实现移动端手写板的方法
May 03 Javascript
jQuery事件blur()方法的使用实例讲解
Mar 30 jQuery
如何使用JS console.log()技巧提高工作效率
Oct 14 Javascript
vue导入.md文件的步骤(markdown转HTML)
Dec 31 Vue.js
Vue解析剪切板图片并实现发送功能
Feb 04 #Javascript
Vue实现剪切板图片压缩功能
Feb 04 #Javascript
Vue中keep-alive组件作用详解
Feb 04 #Javascript
WEB前端性能优化的7大手段详解
Feb 04 #Javascript
JavaScript对象属性操作实例解析
Feb 04 #Javascript
JavaScript this使用方法图解
Feb 04 #Javascript
解决微信小程序scroll-view组件无横向滚动的问题
Feb 04 #Javascript
You might like
php empty函数判断mysql表单是否为空
2010/04/12 PHP
php的api数据接口书写实例(推荐)
2016/09/22 PHP
Extjs中ComboBox加载并赋初值的实现方法
2012/03/22 Javascript
jquery动画2.元素坐标动画效果(创建一个图片走廊)
2012/08/24 Javascript
js与jQuery 获取父窗、子窗的iframe
2013/12/20 Javascript
Javascript复制实例详解
2016/01/28 Javascript
JavaScript中eval函数的问题
2016/01/31 Javascript
JavaScript中的对象继承关系
2016/08/01 Javascript
AngularJS入门教程之REST和定制服务详解
2016/08/19 Javascript
微信小程序 location API实例详解
2016/10/02 Javascript
使用JS在浏览器中判断当前网络连接状态的几种方法
2017/05/05 Javascript
nodejs简单实现TCP服务器端和客户端的聊天功能示例
2018/01/04 NodeJs
vue--点击当前增加class,其他删除class的方法
2018/09/15 Javascript
Bootstrap Paginator+PageHelper实现分页效果
2018/12/29 Javascript
微信小程序3D轮播实现代码
2019/09/19 Javascript
vue项目中使用bpmn为节点添加颜色的方法
2020/04/30 Javascript
vue-quill-editor的使用及个性化定制操作
2020/08/04 Javascript
Python使用PyGreSQL操作PostgreSQL数据库教程
2014/07/30 Python
Python实现的检测网站挂马程序
2014/11/30 Python
Python selenium 三种等待方式详解(必会)
2016/09/15 Python
玩转python selenium鼠标键盘操作(ActionChains)
2020/04/12 Python
python通过百度地图API获取某地址的经纬度详解
2018/01/28 Python
机器学习之KNN算法原理及Python实现方法详解
2018/07/09 Python
Python使用pydub库对mp3与wav格式进行互转的方法
2019/01/10 Python
用Python写一个模拟qq聊天小程序的代码实例
2019/03/06 Python
Python配置文件处理的方法教程
2019/08/29 Python
基于Python的Jenkins的二次开发操作
2020/05/12 Python
美国转售二手商品的电子商务平台:BLINQ
2018/12/13 全球购物
String是最基本的数据类型吗?
2013/06/13 面试题
中国央视网签名寄语
2014/01/18 职场文书
租车协议书范本
2014/04/22 职场文书
2014年人事科工作总结
2014/11/19 职场文书
2014年学生工作总结
2014/11/20 职场文书
休学证明范本
2015/06/19 职场文书
写作技巧:怎样写好一份优秀工作总结?
2019/08/14 职场文书
nginx 配置缓存
2022/05/11 Servers