美文网首页我爱编程
egg.js typescript 开发采坑记录

egg.js typescript 开发采坑记录

作者: 寒枫傲天 | 来源:发表于2018-07-24 15:39 被阅读1415次

    常年使用sails 开发node 后端 苦不堪言,文档缺失,迭代较慢,并且周边也不是那么完善,虽说基本能满足需求 但是还是少了灵活性。 待到egg 推出之时就在观察,最近新开项目遂采用egg - ts

    • egg-mysql
      这玩意没有index.d.ts .所以 你想使用app.mysql 是编译不过的,所以要用 ts 的merge 来给Application上挂载一个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 加点方法这样就有提示了。
    

    egg-sequelize
    为node中较为常用的orm 框架。
    这个玩意我们可以细说。
    首先你要做一个model 来定义这个表中的字段,但是为了方便开发,我们得加点料。

    • /app/model/model.ts 这里写一个基类的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, // 自动维护时间戳 [ created_at、updated_at ]
          underscored: true, // 不使用驼峰样式自动添加属性,而是下划线样式 [ createdAt => created_at ]
          // 禁止修改表名,默认情况下,sequelize将自动将所有传递的模型名称(define的第一个参数)转换为复数
          // 但是为了安全着想,复数的转换可能会发生变化,所以禁止该行为
          freezeTableName: false,
          ...options,
          scopes: {
            // 定义全局作用域,使用方法如: .scope('onlyTrashed') or .scope('onlyTrashed1', 'onlyTrashed12') [ 多个作用域 ]
            onlyTrashed: {
              // 只查询软删除数据
              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上 同时给model 扩展方法。
    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: '手机号码',
        },
        avatar: {
          type: STRING(150),
          allowNull: true,
          comment: '头像',
        },
        real_name: {
          type: STRING(30),
          allowNull: true,
          comment: '真实姓名',
        },
        signature: {
          type: STRING(255),
          allowNull: true,
          comment: '签名',
        },
        notify_count: {
          type: INTEGER,
          allowNull: false,
          defaultValue: 0,
          comment: '消息通知个数',
        },
        status: {
          type: BOOLEAN,
          allowNull: false,
          defaultValue: 1,
          comment: '用户状态: 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;
    }
    
    

    这里我们就完成了一个model的开发

    我们说这是采坑 那么就要说哪里坑了? 上面除了自己搞index.d.ts有点麻烦之外其他看着还好。
    注意了 坑来了~
    用sequelize 那么migration 也要用了吧?

    我们再package.json 内 的script 加入

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

    执行命令npm run migration:create -- --name user生成一个migrations 文件夹,还会有一个空的文件 ....
    1.你需要自己把user.ts 内的model复制一份过去。
    2.你需要执行 ·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,所以我们需要再建一个文件config.default.js.bak 这样做的目的是为了避免被egg 把js文件清除。 改bak 为 普通的js 再执行 up 会发现终于成功了。。。。

    2018-11-05
    不知为何 egg-sequlize 安装不上 (命令行命令无用),隧只能做些改变 使用 sequlize-cli 这一套。。。
    有个注意项: 需要自己写个 config.json给 seqlize去读
    这个问题还带出来个 mysql 字符集问题,原因就是config,json 没有指定为utf8mb4 所以默认为latin,导致中文或者emoji无法存入。

    相关文章

      网友评论

        本文标题:egg.js typescript 开发采坑记录

        本文链接:https://www.haomeiwen.com/subject/hptymftx.html