1. 程式人生 > 程式設計 >Node 使用 Egg 框架 之 上TS 的教程(三)

Node 使用 Egg 框架 之 上TS 的教程(三)

Node + Egg + TS + typegoose + Resetful + schedule + type-graphql+ websocket

樑老師課堂,不間斷,最終版教程來啦。

本次教材地址:github.com/liangwei010…

本次教材講解:typegoose 和 type-graphql 以及 websocket

網上找了一圈,都沒有 egg + typegoose + type-graphql 的教程,還是我來吧。 mongoose 和 graphql 一起使用,會有啥問題呢?當然,正常的問題是不存在,但是很多廢程式碼。拿上節課的程式碼來說。

// schema.graphql(user schema的欄位宣告)
type
User { userNo: Number userName: String } 複製程式碼
const UserSchema: Schema = new Schema({
  userNo: {
    type: Number,index: true,},userName: String,}),複製程式碼

在schema.graphql我們要宣告如上的欄位。我們可以發現,其實我們的欄位都是一樣的,卻多寫了一遍。當我們的model很多的時候,就會產生大量的這個問題。引出我們的主角:typegoose 和 type-graphql 。

使用 typegoose 和 type-graphql

  1. typegoose 主要作用:就是將 加了註解的類 轉化成 對應的 mongoose的屬性、例項方法、靜態方法等等。

  2. type-graphql 的主要作用:也是講 將 加了註解的類 轉化成對應的 graphql 的宣告欄位、query、Mutation等。

看到這裡我們應該可以理解到為啥程式碼比之前的寫法少了重複的程式碼。

egg-demo
├── app
│   ├── controller (前端的請求會到這裡來!)
│   │   └── home.ts
│   ├── model(資料庫表結構抽象出來的模型)
│   │   └── User.ts
│   ├── graphql(graphql資料夾)
│   │   └── schemaResolver(所有Resolver)
│   │       └── userResolver.ts(user的Resolver檔案)
│   ├── ├──index.ts (type
-graphql的初始化) │ ├── service(controller 層不建議承載過多的業務,業務重時放在service層) │ │ └── user.ts │ ├── io(websocket資料夾) │ │ └── controller(前端通過websocket通訊的請求) │ │ └── chat.ts(controller相應的處理) │ ├── schedule(定時任務資料夾) │ │ └── addUserJob.ts │ └── router.ts (Url的相關對映) ├── config (框架的配置檔案) │ ├── config.default.ts │ ├── config.local.ts │ ├── config.prod.ts │ └── plugin.ts ├── test (測試資料夾) │ └── **/*.test.ts ├── typings (目錄用於放置 d.ts 檔案) │ └── **/*.d.ts ├── README.md ├── package.json ├── tsconfig.json └── tslint.json 複製程式碼

本次教程改動了model資料夾和graphql資料夾,以及增加了IO資料夾。

Model

@ObjectType()
export default class BaseModel extends Typegoose {

  @Field({ description: "id" })
  _id?: string

  @prop()
  @Field({ description: "建立時間" })
  createdAt: Date

  @prop()
  @Field({ description: "更新時間" })
  updatedAt: Date
}
複製程式碼
/**
  * 使用者類
*/
@ObjectType()
@index({ userNo: 1 })
export class User extends BaseModel {
  @prop({ required: true })
  @Field(() => Int,{ description: "編號" })
  userNo: number;

  @prop({ required: true })
  @Field({ nullable: true,description: "名稱" })
  userName?: string;

  //#region(例項方法 和 例項方法)
  @instanceMethod
  public async userInstanceTestMethods(this: InstanceType<User>) {
    const user: User = new User();
    user.userName = '我是例項化方法測試';
    user.userNo = 9527;
    return user;
  }

  @staticMethod
  public static async userStaticTestMethods(this: ModelType<User> & typeof User) {
    const user: User = new User();
    user.userName = '我是靜態方法測試';
    user.userNo = 9527;
    return user;
  }
  //#endregion
}
export const UserModel = new User().getModelForClass(User)
複製程式碼
  • @prop({ required: true }) 這個註解是:這個是要轉化成 mongoose 欄位的,並且是必須填寫。
  • @Field({ nullable: true,description: "名稱" })這個註解是:這個欄位將會轉成 graphql的 schema,可選,描述是名稱。
  • @instanceMethod 是 mongoose model 的例項方法。
  • @staticMethod 是 mongoose model 的靜態方法。
  • 通過匯出的 UserModel ,我們將能操作mongoose 的find,create、例項方法,靜態方法,hook等。
  • 通過 model 繼承BaseModel,我們每一個model將能少寫三個正常js的graphql 的欄位。

graphql 對應schema 的 Resolver

@Resolver(User)
export class UserResolver {

  @Query(() => [User],{ description: '查詢使用者列表' })
  async getUser() {
    return await UserModel.find();
  }

  @Mutation(() => User,{ description: '增加使用者' })
  async addUser() {

    let user = new UserModel();
    user.userNo = 666;
    user.userName = 'liang';

    return await UserModel.create(user);
  }
}
複製程式碼
  • @Resolver(User) :意思為這個User model 對應的 Resolver。
  • @Query(() => [User],{ description: '查詢使用者列表' }) :意思為:這個是函式是Query,返回的是一個陣列,描述是XXX。
  • @Mutation(() => User,{ description: '增加使用者' }): 同理。

寫到這裡,我們已經可以執行啦。我們輸入:http://127.0.0.1:7001/graphql 將能看到新的介面。我們的 Query 和 Mutation 還有相應的註解都在其中。

graphql 介面

其實到這裡,我這裡不建議專案中原來用js的上這種方式。如果是存在許多js和ts共存的,我個人建議採用教程二裡面的方式去重構。

websocket 教程

這個簡單很多,我們只要:

  // config.default.ts 配置一下
  config.io = {
    init: {},namespace: {
      '/': {
        connectionMiddleware: ['connection'],packetMiddleware: ['packet'],'/chat': {
        connectionMiddleware: ['connection'],packetMiddleware: [],};
  // plugin.ts 開啟
  io: {
    enable: true,package: 'egg-socket.io',}
複製程式碼

加到路由中:訪問 /的websocket將會到這個sendMsg函式處理。

  io.of('/').route('chat',io.controller.chat.sendMsg);
複製程式碼

到了這裡,ts版本會報錯,因為controller中定義的chat他不認識。這裡需要我們手寫一下index.d.ts就好了。

declare module 'egg' {
  interface CustomController {
    chat: any;
  }

  interface EggSocketNameSpace {
    emit: any
  }
}
複製程式碼

前端我這裡用的vue框架,vue中的使用簡單些。就是下載包,然後在main.js中使用一波就好了。在相應的頁面去做這個事情就好了。

// main.js
import VueSocketIO from 'vue-socket.io'

Vue.use(new VueSocketIO({
 debug: true,connection: 'http://127.0.0.1:7001',}))

複製程式碼
//接收服務端的資訊
this.sockets.subscribe("test",data => {
   alert(data)
 });
複製程式碼

好啦,egg 上ts的教程,完美結束。願大家寫起來都開心啊。不用在寫沒有提示的點點點了。。。若是有幫助,給github裡的專案start一下啊,我也是琢磨了很久的呢。看到網上又沒有這方面教程,寫這些個更希望希望能幫到大家。謝謝~~