课程名称:web前端架构师
课程章节:第14周 第八章 添加 Mongoose 以及 egg.js 插件原理
主讲老师:张轩
课程内容: Egg.js 结合 Mongoose
Egg.js 结合 Mongoose
添加 mongoose到 egg.js
在 app.js 中将 mongoose 挂载到 app 上
import { IBoot, Application } from 'egg';
import { createConnection } from 'mongoose';
export default class AppBook implements IBoot {
private readonly app: Application;
constructor(app: Application) {
this.app = app;
const { url } = this.app.config.mongoose;
const db = createConnection(url);
db.on('connected', () => {
console.log('connected success');
});
this.app.mongoose = db;
}
// 配置文件即将加载,这是最后动态修改配置的时机
configWillLoad(): void {
this.app.config.coreMiddleware.push('myLogger');
}
async willReady(): Promise<void> {
console.log(this.app.config.coreMiddleware);
}
}
在这之前,我们需要将 MongoDB 信息放到 配置文件中
import { EggAppConfig, EggAppInfo, PowerPartial } from 'egg';
export default (appInfo: EggAppInfo) => {
const config = {} as PowerPartial<EggAppConfig>;
// add your special config in here
const bizConfig = {
sourceUrl: `https://github.com/eggjs/examples/tree/master/${appInfo.name}`,
myLogger: {
allowedMethod: [ 'POST' ],
},
// 将 mongoose 放到这里,是因为这里 在app.ts 书写时就会有提示信息
mongoose: 'mongodb://user:pass@localhost:27017/dbName',
};
// the return config will combines to EggAppConfig
return {
...config as {},
...bizConfig,
};
};
这样就将 mongoose 挂载到 app 上了,这是还需要声明下 app.mongoose 到类型,可以在 typings/index.d.ts 上声明
import 'egg';
import { type Connection } from 'mongoose'
declare module 'egg' {
interface Application {
mongoose: Connection
}
}
添加 Servie, 这里我们创建一个 User Service
首先创建 User Schema
import { Service } from 'egg';
import { Schema } from 'mongoose';
const ObjectId = Schema.Types.ObjectId;
const UserSchema = new Schema({
username: String,
password: {
type: String,
select: false,
},
hobbies: [ String ],
date: Date,
createAt: Date,
age: Number,
team: {
type: ObjectId,
ref: 'team',
},
}, {
collection: 'users',
});
创建model,然后我们创建一个 getUser 方法,用来查询用户
export default class UserController extends Service {
public async getUser() {
const User = this.getUserModel();
console.log(await User.find());
return User.find().limit(10);
}
private getUserModel() {
return this.app.mongoose.model('User', UserSchema);
}
}
这样我们就可以在 controller 中使用这个service,来查询用户信息了
import { Controller } from 'egg';
export default class UserController extends Controller {
public async index() {
const { ctx } = this;
ctx.body = await ctx.service.user.getUser();
ctx.logger.debug('debug info');
}
}
这样就可以通过访问对应接口,来查询用户了
但是上面代码中,我们是将 Mongoose Model 写在了service 下,我们可以将 Model 写在一个 model 文件夹下,任何目录下可以通过访问 app.model.xxx来操作 MongoDB 的话,就会更加方便
});
export default class UserController extends Service {
public async getUser() {
return this.app.model.User.find().limit(10)
}
}
实现这个功能之前需要先了解下 egg.js 的加载器
加载器 Loader
Egg 在 Koa 的基础上进行增强最重要的就是基于一定的约定,根据功能差异将代码放到不同的目录下管理,对整体团队的开发成本提升有着明显的效果。Loader 实现了这套约定,并抽象了很多底层 API 可以进一步扩展。
文件顺序
上面已经列出了默认会加载的文件,Egg 会按如下文件顺序加载,每个文件或目录再根据 loadUnit 的顺序去加载(应用、框架、插件各有不同)。
- 加载 plugin,找到应用和框架,加载 config/plugin.js
- 加载 config,遍历 loadUnit 加载 config/config.{env}.js
- 加载 extend,遍历 loadUnit 加载 app/extend/xx.js
- 自定义初始化,遍历 loadUnit 加载 app.js 和 agent.js
- 加载 service,遍历 loadUnit 加载 app/service 目录
- 加载 middleware,遍历 loadUnit 加载 app/middleware 目录
- 加载 controller,加载应用的 app/controller 目录
- 加载 router,加载应用的 app/router.js
生命周期
框架提供了这些生命周期函数供开发人员处理:
- 配置文件即将加载,这是最后动态修改配置的时机(configWillLoad)
- 配置文件加载完成(configDidLoad)
- 文件加载完成(didLoad)
- 插件启动完毕(willReady)
- worker 准备就绪(didReady)
- 应用启动完成(serverDidReady)
- 应用即将关闭(beforeClose)
我们可以将我们加载 model 文件放在 willReady 中
加载器函数 loadToApp
用于加载一个目录下的文件到 app,比如 app/controller/home.js 会加载到 app.controller.home。
// app.js
// 以下只是示例,加载 controller 请用 loadController
module.exports = (app) => {
const directory = path.join(app.config.baseDir, 'app/controller');
app.loader.loadToApp(directory, 'controller');
};
一共有三个参数 loadToApp(directory, property, LoaderOptions)
- directory 可以为 String 或 Array,Loader 会从这些目录加载文件
- property 为 app 的属性
- LoaderOptions 为一些配置. https://www.eggjs.org/zh-CN/advanced/loader#loaderoptions
我们可以使用 loalToApp 方法来加载 model
// app.ts
import { IBoot, Application } from 'egg';
import path from 'path';
...
export default class AppBook implements IBoot {
private readonly app: Application;
...
// 配置文件即将加载,这是最后动态修改配置的时机
configWillLoad(): void {
const dir = path.join(this.app.config.baseDir, 'app/model');
this.app.loader.loadToApp(dir, 'model', {
caseStyle: 'Upper',
});
}
}