egg.js typescript 開發(fā)采坑記錄

常年使用sails 開發(fā)node 后端 苦不堪言,文檔缺失,迭代較慢,并且周邊也不是那么完善,雖說基本能滿足需求 但是還是少了靈活性。 待到egg 推出之時(shí)就在觀察,最近新開項(xiàng)目遂采用egg - ts

  • egg-mysql
    這玩意沒有index.d.ts .所以 你想使用app.mysql 是編譯不過的,所以要用 ts 的merge 來給Application上掛載一個(gè)mysql 我們可以這么做
./typings/index.d.ts 寫入
declare module 'egg' {
  interface mysql {
    get(tableName: String, find: {}): Promise<Any>

    query(sql: String, values: Any[]): Promise<Any>
  }
  interface Application {
    mysql: mysql;
  }
}
可以在上面給mysql 加點(diǎn)方法這樣就有提示了。

egg-sequelize
為node中較為常用的orm 框架。
這個(gè)玩意我們可以細(xì)說。
首先你要做一個(gè)model 來定義這個(gè)表中的字段,但是為了方便開發(fā),我們得加點(diǎn)料。

  • /app/model/model.ts 這里寫一個(gè)基類的basemodel 這樣以后所有的model都可以用到基類
import { Application } from 'egg';
import { snakeCase } from 'lodash';
import * as moment from 'moment';
import { DefineAttributes, SequelizeStatic } from 'sequelize';
export default function BaseModel(
  app: Application,
  table: string,
  attributes: DefineAttributes,
  options: object = {},
) {
  const { Op, UUID, UUIDV4 } = app.Sequelize;

  const modelSchema = app.model.define(table, {
    id: {
      type: UUID,
      unique: true,
      primaryKey: true,
      allowNull: false,
      defaultValue: UUIDV4,
    },
    ...attributes,
    ...getDefaultAttributes(options, app.Sequelize),
  }, {
      timestamps: true, // 自動(dòng)維護(hù)時(shí)間戳 [ created_at、updated_at ]
      underscored: true, // 不使用駝峰樣式自動(dòng)添加屬性,而是下劃線樣式 [ createdAt => created_at ]
      // 禁止修改表名,默認(rèn)情況下,sequelize將自動(dòng)將所有傳遞的模型名稱(define的第一個(gè)參數(shù))轉(zhuǎn)換為復(fù)數(shù)
      // 但是為了安全著想,復(fù)數(shù)的轉(zhuǎn)換可能會(huì)發(fā)生變化,所以禁止該行為
      freezeTableName: false,
      ...options,
      scopes: {
        // 定義全局作用域,使用方法如: .scope('onlyTrashed') or .scope('onlyTrashed1', 'onlyTrashed12') [ 多個(gè)作用域 ]
        onlyTrashed: {
          // 只查詢軟刪除數(shù)據(jù)
          where: {
            deleted_at: {
              [Op.not]: null,
            },
          },
        },
      },
    });

  modelSchema.getAttributes = (): string[] => {
    return Object.keys(attributes);
  };

  modelSchema.findAttribute = (attribute: string): object | undefined => {
    return (attributes as any)[attribute];
  };

  modelSchema.fillable = (): string[] => {
    return [];
  };

  modelSchema.hidden = (): string[] => {
    return [];
  };

  modelSchema.visible = (): string[] => {
    return [];
  };

  return modelSchema;
}

function getDefaultAttributes(options: object, sequelize: SequelizeStatic): object {
  const { DATE } = sequelize;

  const defaultAttributes = {
    created_at: {
      type: DATE,
      get() {
        return moment((this as any).getDataValue('created_at')).format('YYYY-MM-DD HH:mm:ss');
      },
    },
    updated_at: {
      type: DATE,
      get() {
        return moment((this as any).getDataValue('updated_at')).format('YYYY-MM-DD HH:mm:ss');
      },
    },
  };

  // 需要從 options 讀取的配置信息,用于下方做過濾的條件
  const attributes = ['createdAt', 'updatedAt', 'deletedAt'];

  Object.keys(options).forEach((value: string) => {
    if (attributes.includes(value) && (options as any)[value] === false) {
      delete (defaultAttributes as any)[snakeCase(value)];
    }
  });
  return defaultAttributes || {};
}

  • /app/model/index.d.ts 將model 掛載到application上 同時(shí)給model 擴(kuò)展方法。
import { User } from './user';
declare module 'egg' {
  interface Application {
    Sequelize: SequelizeStatic
    model: Sequelize
  }

  interface Context {
    model: {
      User: Model<User, {}>;
    }
  }
}
Mode
declare module 'sequelize' {
  interface Model<TInstance, TAttributes> {
    fillable(): string[];
    hidden(): string[];
    visible(): string[];
    getAttributes(): string[];
    findAttribute(attribute: string): object | undefined;

    getDataValue(key: string): any;

    setDataValue(key: string, value: any): void;
  }
}
  • app/model/user.ts
import { Application } from 'egg';
import BaseModel from './model';

export default function User(app: Application) {
  const { INTEGER, DATE, STRING, BOOLEAN } = app.Sequelize;

  const modelSchema = BaseModel(app, 'users', {
    name: {
      type: STRING(32),
      unique: true,
      allowNull: false,
      comment: '用戶名',
    },
    email: {
      type: STRING(64),
      unique: true,
      allowNull: true,
      comment: '郵箱地址',
    },

    phone: {
      type: STRING(20),
      unique: true,
      allowNull: true,
      comment: '手機(jī)號(hào)碼',
    },
    avatar: {
      type: STRING(150),
      allowNull: true,
      comment: '頭像',
    },
    real_name: {
      type: STRING(30),
      allowNull: true,
      comment: '真實(shí)姓名',
    },
    signature: {
      type: STRING(255),
      allowNull: true,
      comment: '簽名',
    },
    notify_count: {
      type: INTEGER,
      allowNull: false,
      defaultValue: 0,
      comment: '消息通知個(gè)數(shù)',
    },
    status: {
      type: BOOLEAN,
      allowNull: false,
      defaultValue: 1,
      comment: '用戶狀態(tài): 1 正常; 0 禁用',
    },
    password: {
      type: STRING(255),
      allowNull: false,
    },
    last_actived_at: DATE,
  }, {
      paranoid: true,

      setterMethods: {
        async password(value: any) {
          (this as any).setDataValue('password', await app.createBcrypt(value))
        },
      },
    });
  return modelSchema;
}

這里我們就完成了一個(gè)model的開發(fā)

我們說這是采坑 那么就要說哪里坑了? 上面除了自己搞index.d.ts有點(diǎn)麻煩之外其他看著還好。
注意了 坑來了~
用sequelize 那么migration 也要用了吧?

我們?cè)賞ackage.json 內(nèi) 的script 加入

"migrate:new": "egg-sequelize migration:create",
    "migrate:up": "egg-sequelize db:migrate",
    "migrate:down": "egg-sequelize db:migrate:undo"

執(zhí)行命令npm run migration:create -- --name user生成一個(gè)migrations 文件夾,還會(huì)有一個(gè)空的文件 ....
1.你需要自己把user.ts 內(nèi)的model復(fù)制一份過去。
2.你需要執(zhí)行 ·npm run migration:up·



Sequelize CLI [Node: 8.11.3, CLI: 4.0.0, ORM: 4.38.0]

Loaded configuration file "node_modules/egg-sequelize/lib/database.js".
Using environment "development".

ERROR: Dialect needs to be explicitly supplied as of v4.0.0

采坑了吧!
我們看源文件 是需要從配置里 config.default.js讀取 而非 config.default.ts,所以我們需要再建一個(gè)文件config.default.js.bak 這樣做的目的是為了避免被egg 把js文件清除。 改bak 為 普通的js 再執(zhí)行 up 會(huì)發(fā)現(xiàn)終于成功了。。。。

2018-11-05
不知為何 egg-sequlize 安裝不上 (命令行命令無用),隧只能做些改變 使用 sequlize-cli 這一套。。。
有個(gè)注意項(xiàng): 需要自己寫個(gè) config.json給 seqlize去讀
這個(gè)問題還帶出來個(gè) mysql 字符集問題,原因就是config,json 沒有指定為utf8mb4 所以默認(rèn)為latin,導(dǎo)致中文或者emoji無法存入。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Egg.js是阿里開源的基于Koa的一個(gè)企業(yè)級(jí)Node框架,具體介紹在這里不做詳細(xì)說明,想要了解更多可以查看Egg...
    白酒__閱讀 9,351評(píng)論 0 3
  • 前段時(shí)間聽說,辦公室里的一個(gè)女生因?yàn)槭?,不甘心就這么跟前任分開,跑去借酒消愁,喝到胃出血,在醫(yī)院躺了半個(gè)月。 住...
    設(shè)計(jì)師薩克斯閱讀 323評(píng)論 0 0
  • 昨天聊的太嗨到很晚就沒有寫。今天必須補(bǔ)上。最需要記錄的必須是在張琪家過夜。第一次來到屬于一個(gè)人的小空間,真的好棒。...
    Sanity娜娜閱讀 227評(píng)論 1 1
  • 王小波在《小說的藝術(shù)》中說道:“寫小說則需要深得虛構(gòu)之美,也需要些無中生有的才能?!薄端疂G傳》第二十三回“...
    文可清心也閱讀 689評(píng)論 0 2
  • 風(fēng)呼嘯,雨瓢潑,天昏地暗雷電摧,風(fēng)吹樹搖行人稀,萬千雨點(diǎn)顯威風(fēng)。
    紀(jì)尚凱閱讀 174評(píng)論 0 0

友情鏈接更多精彩內(nèi)容