前言

我们在上一篇文章Egg.js 源码分析-项目启动, 已经简单的分析了Eggjs 的启动机制, 以及其相应的实现原理,Eggjs 就是针对一系列的约定俗成的规则,在项目启动时,自动加载对应文件夹下面的文件,进行项目的初始化,我们可以参考官网给出的目录结构,去对我们的项目进行规范,包括文件结构规范, 代码逻辑分层规范,从而达到整个项目的规范。

之所以有这样的一个目录结构 ,其实还是针对于我们上一篇文章Egg.js 源码分析-项目启动 分析所得,在项目启动时,会加载如下的配置, 下面代码后面加了备注,只标注了我们应用对应的文件名称(没有标注eggjs 对应的文件名称)

  loadConfig() {// your-project-name/config/plugin.jsthis.loadPlugin();// your-project-name/config/config.default.js // your-project-name/config/`config.${this.serverEnv}`.js super.loadConfig();}
复制代码
  load() {// app > plugin > corethis.loadApplicationExtend();this.loadRequestExtend();this.loadResponseExtend();this.loadContextExtend();this.loadHelperExtend();// your_project_name/app.jsthis.loadCustomApp();// your_project_name/app/service/**.jsthis.loadService();this.loadMiddleware();// your_project_name/app/controller/**.js this.loadController();// your_project_name/ app/router.jsthis.loadRouter(); // Dependent on controllers}
复制代码

从上面可以知道我们应用的一个大致的结构,我们下面就来一一从头开始创建一个项目,深入了解Eggjs 的使用方式(规范)

初始化项目

我们利用egg-init 的手脚架egg-init 先初始化一个项目, 我们可以运行如下命令(一直没怎么接触APP 开发, 所以最近想研究下react-native ,所以创建了一个react-native-learning-server项目):

$ npm i egg-init -g
$ egg-init react-native-learning-server --type=simple
$ cd react-native-learning-server
$ npm i
$ npm run dev
复制代码

浏览器会打开默认端口:http://localhost:7001, 页面会显示hi, egg, 说明我们项目创建成功.

设置路由

我们现在假设我们想做一个类似掘金一样的APP, 我们可以有四个大菜单 Mine , Find , Message, Home

我们简单的设计几个API:

Mine:

API Method 描叙
/users GET 获取所有的用户
/user/:id GET 获取指定用户信息
/user POST 添加用户
/user/:id PUT 编辑用户
/user/:id DELETE 删除用户

Message:

API Method 描叙
/messages/:userId GET 获取用户所有的信息
/message POST 发送信息
/message/:id DELETE 删除指定信息

Find:

API Method 描叙
/search/:keyword GET 根据关键字,查询信息

Home:

API Method 描叙
/hot GET 查询最热信息
/heart GET 查询关注的热点

我们先只设计如上几个简单的API(我们这篇文章,只是想通过一个伪业务来实现Egg的一些使用方式, 重点是Eggjs 的使用)

上面我们已经初始化了项目, 我们现在编辑app/router.js去设计路由,其代码如下:

module.exports = app => {const { router, controller } = app;router.get('/', controller.home.index);// Userrouter.get('/users', controller.user.findAll);router.get('/user/:id', controller.user.findOne);router.post('/user', controller.user.add);router.put('/user/:id', controller.user.update);router.del('/user/:id', controller.user.delete);// Messagerouter.get('/messages/:userId', controller.message.findByUserId);router.post('/message', controller.message.add);router.del('/messages/:id', controller.message.delete);// Findrouter.get('/search/:keyword', controller.search.find);// Homerouter.get('/hot', controller.home.findHot);router.get('/heart', controller.home.findHeart);
};复制代码

我们先不管Controller的实现, 上面我们就已经实现了我们的路由了, 但是我们发现一个问题,就是当项目越来越大,那这个router.js 会越来越大,也会越来越难以维护,所以我们可以做如下的调整:

  1. 在app下面创建一个文件夹routers, 然后创建四个文件,分别为: user.js, message.js, search.js, home.js,
  2. 然后将router.js 中的路由进行分割,不同的路由都分割到对应的文件下.
  3. 在router.js 中去引用每个单独的路由 拆分后的router.js 如下:
'use strict';
const userRouter = require('./routers/user');
const messageRouter = require('./routers/message');
const homeRouter = require('./routers/home');
const searchRouter = require('./routers/search');
module.exports = app => {const { router, controller } = app;router.get('/', controller.home.index);userRouter(app);messageRouter(app);homeRouter(app);searchRouter(app);
};
复制代码

其routers/user.js 代码如下:

'use strict';module.exports = app => {const { router, controller } = app;router.get('/users', controller.user.findAll);router.get('/user/:id', controller.user.findOne);router.post('/user', controller.user.add);router.put('/user/:id', controller.user.update);router.del('/user/:id', controller.user.delete);
};复制代码

经过如上的拆分, router.js 的代码变得整洁, 而且相应的路由变得更加容易维护.

设置控制层(Controller)

上面我们已经开发(配置)好了Router, 但是Router 的回调函数,都指向的是app 的controller下面的对象, 如:controller.user.findAll 我们在上一章节已经分析了这个路径怎么来的: controller 是app(应用)的一个属性对象, eggjs 会在启动的时候调用this.loadController(); 方法,去加载整个应用app/controller文件下的所有的js 文件, 会将文件名作为属性名称,挂载在app.controller 对象上, 然后将对应js 文件export(暴露)出来的所有的方法有挂在在文件名称为属性的对象上,然后就可以通过controller.user.findAll这样的方式来引用Controller 下面的方法了。

有了这个思路,我们就可以很清晰的去维护我们控制层了,下面是我们一个home的范例:

'use strict';const Controller = require('egg').Controller;class HomeController extends Controller {async index() {this.ctx.body = 'hi , egg.';}async findHot() {this.ctx.body = this.ctx.request.url;}async findHeart() {this.ctx.body = this.ctx.request.url;}
}module.exports = HomeController;复制代码

设置Service

我们已经开发好了Router 和Controller , 但是在我们的controller 中,都是静态的内容, 一个项目我们需要跟数据库交互,我们一般将跟DB 交互的内容,都放在Service 层,下面我们就来开发我们的service.

我们首先在app目录下面,创建一个service 目录, 并且创建 user.js, message.js, search.js, home.js,文件, 我们先不连接真实的数据库, 创建的service 如下(home.js):

'use strict';const Service = require('egg').Service;class HomeService extends Service {async index() {return 'hi, egg';}findHot() {const hotArticle = [{title: 'Title 0001',desc: 'This is hot article 0001',},{title: 'Title 0002',desc: 'This is hot article 0002',},];return hotArticle;}findHeart() {const heartArticle = [{title: 'Title 0001',desc: 'This is heart article 0001',},{title: 'Title 0002',desc: 'This is heart article 0002',},];return heartArticle;}
}module.exports = HomeService;
复制代码

我们接下来修改Controller文件:

'use strict';const Controller = require('egg').Controller;class HomeController extends Controller {async index() {this.ctx.body = this.service.home.index();}async findHot() {this.ctx.body = this.service.home.findHot();}async findHeart() {this.ctx.body = this.service.home.findHeart();}
}module.exports = HomeController;复制代码

我们调用service方法如: this.service.home.index();, 跟controller 原理类似。

到此位置,我们项目的基本框架,已经搭建完成,我们现在可以思考先,我们怎么连接数据库。

连接DB

我们的数据库我们选择用MongoDB, 所以,我们可以选择用egg-mongoose 插件,我们可以按照文档进行操作: 首先安装插件:

$ npm i egg-mongoose --save

因为egg-mongoose 是作为Eggjs 的一个插件,所以我们要配置这个插件,我们现在app/config/plugin.js中配置插件:

'use strict';
module.exports = {// enable plugins mongoose: {enable: true,package: 'egg-mongoose',},
};
复制代码

接下来,我们要配置MongoDB 的连接, 我们修改app/config/config.default.js

'use strict';module.exports = appInfo => {const config = exports = {};// use for cookie sign key, should change to your own and keep securityconfig.keys = appInfo.name + '_1541735701381_1116';// add your config hereconfig.middleware = [];config.cluster = {listen: {path: '',port: 7001,hostname: '',},};config.mongoose = {client: {url: 'mongodb://127.0.0.1/react-native-demo',options: {},},};return config;
};复制代码

接下来我们需要给MongoDB 配置Model,我们在app 目录下面,创建一个model 文件夹, 并且创建user.js, 代码如下:

'use strict';
// {app_root}/app/model/user.js
module.exports = app => {const mongoose = app.mongoose;const Schema = mongoose.Schema;const UserSchema = new Schema({userName: { type: String },password: { type: String },});return mongoose.model('User', UserSchema);
};
复制代码

然后我们接下来,修改Service 来真正的连接数据库,修改service/user.js 代码如下:

'use strict';
const Service = require('egg').Service;class UserService extends Service {async findAll() {return await this.ctx.model.User.find();}
}module.exports = UserService;复制代码

连接数据库的真个流程完成了,我们可以打开http://localhost:7001/users, 页面就会显示从MongoDB 数据库里面查询的所有的数据。

总结

  1. 安装插件: $ npm i egg-mongoose --save
  2. 配置插件: app/config/plugin.js中配置插件
  3. 配置连接: 我们修改app/config/config.default.js,添加egg-mongoose连接信息
  4. 创建Model: 我们在{app_root}/app/model/user.js 下创建Model.
  5. 修改Service: 修改Servcie 的代码,操作MongoDB, await this.ctx.model.User.find();

问题

  1. 在上一篇文章Egg.js 源码分析-项目启动中,我们并没有分析到,eggjs 会加载model 文件夹下面的文件.那这里的model 目录下的文件是什么时候加载上的?
  2. config.default.js 中,配置mongoose 的连接信息,挂载在config.mongoose上, mongoose这个名称是否是固定的, 可以修改成其他的?
  3. app/config/plugin.js中,配置mongoose的插件信息, mongoose 的属性名称是否是固定的, 可以修改成其他的?

答案

  1. 在eggjs 中,并没有实现加载model 的功能,但是egg-mongoose这个插件实现了这个功能,其代码如下:
function loadModelToApp(app) {const dir = path.join(app.config.baseDir, 'app/model');app.loader.loadToApp(dir, 'model', {inject: app,caseStyle: 'upper',filter(model) {return typeof model === 'function' && model.prototype instanceof app.mongoose.Model;},});
}复制代码
  1. 必须叫做mongoose这个名称,因为在egg-mongoose这个插件中,会直接去读取应用中app.config.mongoose 的配置const { client, clients, url, options, defaultDB, customPromise, loadModel } = app.config.mongoose;,所以这个规则是egg-mongoose插件制定的。
  2. plugin.js的配置名称, 不是固定的, 可以随意,因为其真正重要的配置是: package: 'egg-mongoose',指明这个pulgin 用的是那个具体的包。

初始化数据

在一个项目上线的时候,我们经常需要准备一些初始化数据, 比如用户数据,我们一般会创建一个超级管理员的帐号, 这个帐号,是不需要用户注册的,所以我们可以在项目初始化的时候用脚本生成,我们按照如下步骤进行操作:

  1. 修改app/model/user.js添加isMaster属性,如下:
'use strict';
// {app_root}/app/model/user.js
module.exports = app => {const mongoose = app.mongoose;const Schema = mongoose.Schema;const UserSchema = new Schema({userName: { type: String, required: true },password: { type: String, required: true },isMaster: { type: Boolean, default: false, required: true },});return mongoose.model('User', UserSchema);
};复制代码
  1. {app_root}/app目录下,创建一个data的文件夹,然后创建一个user.json, 其内容如下:
[{"userName": "admin","password": "admin","isMaster": true}
]
复制代码
  1. 因为需要在项目启动的时候,去初始化数据,所以我们在{app_root}目录下,添加一个app.js(this.loadCustomApp()),代码如下:
'use strict';
// app.js
module.exports = app => {app.beforeStart(async () => {if (app.config.initData) {const initUsers = require('./app/data/user.json');const ctx = app.createAnonymousContext();ctx.model.User.create(initUsers, err => {if (err) {app.coreLogger.console.warn('[egg-app-beforeStart] init user data fail %s', err);}});}});
};
复制代码

我们在配置文件中添加了一个initData 的开关用来表示是否需要初始化数据,因为初始化数据,一般就是第一次需要(这个配置,应该作为运行脚本命令的参数传递,这样更易于维护,而且不用每次都去该config.default.js 的代码)

总结

按照上面的操作,我们基本完成了一个项目的基本骨架,我们只需要在上面搭积木就可以了,而且了解了Eggjs 的基本使用。源码

Egg.js 基本使用相关推荐

  1. egg.js连接mysql数据库遇到的问题

    最近在策划写一个博客采用前后端分离模式,前端使用vue后端使用egg.js,我也是刚开始学习egg.js,所以会将自己踩的坑都记录下来. 首先介绍下后端为什么采用egg.js吧,之前我是学习了koa2 ...

  2. 轻松搭建基于 Serverless 的 Egg.js Web 应用

    首先介绍下在本文出现的几个比较重要的概念: 函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传.函数计算准备计 ...

  3. 基于 Egg.js 框架的 Node.js 服务构建之用户管理设计

    转载需经本人同意且标注本文原始地址:https://zhaomenghuan.github.io/blog/nodejs-eggjs-usersytem.html 前言 近来公司需要构建一套 EMM( ...

  4. egg.js java 生产数据_Egg 2.15.0 发布,阿里开源的企业级 Node.js 框架

    值得注意的变化 新特性 [EXPERIMENT FEATURE] 支持单线程模式 Bug 修复 [TYPE] 支持config.static.dir的数组 [TYPE] 修复中间件类型不兼容 [TYP ...

  5. 一个vue加egg.js的博客

    之前自己的博客是用hexo做的,后面想做一个有后台的博客就打算用vue加node来试试,于是就有了这个博客. 项目地址 W-Blog W-Blog是一个基于vue和node的小小小博客 前端用vue, ...

  6. Serverless 实战 —— Serverless + Egg.js 后台管理系统实战

    Serverless + Egg.js 后台管理系统实战 作为一名前端开发者,在选择 Nodejs 后端服务框架时,第一时间会想到 Egg.js,不得不说 Egg.js 是一个非常优秀的企业级框架,它 ...

  7. 入门 | egg.js 入门之egg-jwt

    小小继续学习,这次学习的内容是egg-jwt 相关. 创建egg项目 这里创建一个egg新项目,这里使用的是ts模式. npm init egg --type=ts npm install 安装相关的 ...

  8. 学习 | egg.js 中间件和插件

    小小又开始学习了,这次学习的是中间件和插件. 这次将会对这两个点,进行学习. 中间件 对于egg.js 来说,中间件和express的中间件性质相似,和洋葱模型类似. 这里首先讲解的是egg.js的中 ...

  9. 学习 | egg.js 从入门到精通

    快速初始化 使用脚手架,对项目进行快速初始化 $ mkdir egg-example && cd egg-example $ npm init egg --type=simple $ ...

  10. egg 编码规范_从 Egg.js 到 NestJS,爱码客后端选型之路

    序 爱码客3.0 开始开发到现在已经过去快整整一年了,虽然我投入其中的时间只有短短4个月,但是在最初后端几乎只有我一个人投入的情况下,可以说也是研究了一些东西,蹚了二三次浑水,来来回回改过五六次结构, ...

最新文章

  1. 什么是视觉Visual SLAM
  2. java gui 窗口 传值_java – GUI – 在不同窗口之间传输数据(J...
  3. oracle 分列,SQL 问题 如何分列
  4. C++中的const数据成员
  5. pytorch常用函数API简析与汇总——以备查询
  6. python金字塔_高斯金字塔与拉普拉斯金字塔的原理与python构建
  7. Codeforces #499 Div2 E (1010C) Border
  8. 上海海事大学c语言题库,上海海事大学,C语言试卷6
  9. rsync内网服务器 推送和拉取公网代码配置
  10. Axure RP 8下载
  11. 介绍VMware虚拟化存储原理及数据恢复方法
  12. 那些年曹大写过的博客
  13. js/javaScript通过setTimeout做动画和需要注意的点
  14. 线性代数学习笔记(二十九)——方程组解的结构(一)
  15. mysql UNIX_TIMESTAMP时间差
  16. 计算机管理器磁盘清理,Win10提示windows磁盘空间清理管理器已停止工作
  17. android输入法切换
  18. 打造新一代计算平台,STEPVR将在2022开启元宇宙“大门”
  19. 三段式抽屉BottomSheetBehavior嵌套RecyclerView滑动冲突问题
  20. ECLIPSE android 布局页面文件出错故障排除Exception raised during rendering: java.lang.System.arraycopy([CI[CII)V

热门文章

  1. numpy.random.randint用法
  2. 当今在世的智商最高的十位天才 --
  3. Liunx下的进程空间地址理解与进程等待
  4. iPhone为什么会充电到80%就自动停止?使用快充会对电池造成损伤吗?
  5. 高级UI-沉浸式设计
  6. 算法-js系列(2):动态规划-中等(1)
  7. 算法设计手册(第2版)读书笔记, Springer - The Algorithm Design Manual, 2ed Steven S.Skiena 2008
  8. 大学计算机基础码,大学计算机基础字符的编码——区位码和国标码
  9. iPhone12、iPhone12 Pro、iPhone12 Max、iPhone12 Pro Max发布会时间
  10. NewCoder 剖分