background

Express and Koa are lightweight Web frameworks, and the no-constraint framework is pretty straightforward to start with, developing a couple of demos is pretty easy, but once the code gets going (and it will), you’ll find a lot of repetitive operations, repetitive logic. As a result, the project becomes more and more complex, the code becomes more and more ugly, and it is very difficult to maintain. My Quark-H5 also began to write randomly, to the end can only reconstruct a wave. At the same time, I did an online document management project using egg.js, which made me like a node white have a bright feeling. The quark-H5 server was reconstructed by referring to egg.js to realize the MVC structure based on KOA2

Making: portal

Koa MVC project directory structure planning

This is a reference to the eggJS directory structure, which is very clean and can be built with only a few lines of code because we package it properly

The basic loading flow of MVC

Koa2 --> APP --> Introduce Config --> introduce Controller --> Introduce Server --> introduce extend --> introduce Router --> Introduce Model --> introduce scheduled task --> initialize default middleware --> Materialize --> mount to CTX --> CTX global useCopy the code

The nodeJS FS file module is used to scan each module folder to obtain JS files, and the JS exported content is assigned to the global APP object. Modules are accessed through the global APP object

Here is the core load code implementation:

/core/index.js

/** * encapsulate koA MVC infrastructure initialization */ const path = require('path') const koa = require('koa'); const { initConfig, initController, initService, initModel, initRouter, initMiddleware, initExtend, initSchedule } = require('./loader'); class Application{ constructor(){ this.$app = new Koa(); / / registered by default middleware enclosing initDefaultMiddleware (); $this.$config = initConfig(this); // Initialize controller this.$controller = initController(this); $this.$service = initService(this); // Initialize middleware this.$middleware = initMiddleware(this); $router = router (this); $router = router (this); // Initialize the extension initExtend(this); This.$app. Use (async (CTX, next) => {this. CTX = CTX; await next() }) this.$app.use(this.$router.routes()); } // Set the built-in middleware initDefaultMiddleware(){const koaStatic = require('koa-static'); const koaBody = require('koa-body'); const cors = require('koa2-cors'); const views = require('koa-views'); $app.use(koaStatic(path.resolve(__dirname, '.. /public')), { gzip: true, setHeaders: function(res){ res.header( 'Access-Control-Allow-Origin', '*') }}); // this.$app.use(cors()); This.$app.use(koaBody({multipart: true, formidable: {maxFileSize: formidable) 3000*1024*1024 // Set the maximum upload file size, default 30M}})); $app.use(views(path.join(__dirname,'.. /views'), {extension:'ejs'}))} // Start (port){this.$app.listen(port). ()=>{ console.log('server is starting........ ! '); }); } } module.exports = Application;Copy the code

The loader is responsible for parsing the contents of each folder and mounting them to the global APP instance. /core/loader.js implements the logic

const path = require('path') const fs = require('fs') const Router = require('koa-router'); const schedule = require("node-schedule"); Const mongoose = require('mongoose') // Function scanFilesByFolder(dir, cb) { let _folder = path.resolve(__dirname, dir); if(! getFileStat(_folder)){ return; } try { const files = fs.readdirSync(_folder); files.forEach((file) => { let filename = file.replace('.js', ''); let oFileCnt = require(_folder + '/' + filename); cb && cb(filename, oFileCnt); })} Catch (error) {console.log(' File auto loading failed... ', error); /** * @param {string} path */ function getFileStat(path) {try {fs.statsync (path); return true; } catch (err) { return false; Const initConfig = function(app){let config = {}; scanFilesByFolder('.. /config',(filename, content)=>{ config = {... config, ... content}; }); return config; }; // Initialize router const initRouter = function(app){const router = new router (); require('.. /router.js')({... app, router}); return router; } // initController const initController = function(app){let controllers = {}; scanFilesByFolder('.. /controller',(filename, controller)=>{ controllers[filename] = controller(app); }) return controllers; Function initService(app){let services = {}; scanFilesByFolder('.. /service',(filename, service)=>{ services[filename] = service(app); }) return services; If (app.$config.mongodb){mongoose. Set ('useNewUrlParser', true) mongoose.set('useFindAndModify', false); mongoose.set('useUnifiedTopology', true); mongoose.connect(app.$config.mongodb.url, app.$config.mongodb.options); $mongoose = mongoose; $mongoose = mongoose; $db = mongoose. Connection} // let model = {}; scanFilesByFolder('.. /model',(filename, modelConfig)=>{ model[filename] = modelConfig({... app, mongoose}); }); return model; Function initMiddleware(app){let middleware = {} scanFilesByFolder('.. /middleware',(filename, middlewareConf)=>{ middleware[filename] = middlewareConf(app); Initial configuration}) / / middleware if (app) $config) middleware && Array. The isArray (app) $config) middleware)) {app. $config. Middleware. ForEach (mid = > {  if(middleware[mid]){ app.$app.use(middleware[mid]); } }) } return middleware; } // Init extension function initExtend(app) {scanFilesByFolder('.. /extend',(filename, extendFn)=>{ app[filename] = Object.assign(app[filename] || {}, Function initSchedule(){scanFilesByFolder('.. /schedule',(filename, scheduleConf)=>{ schedule.scheduleJob(scheduleConf.interval, scheduleConf.handler) }) } module.exports = { initConfig, initController, initService, initRouter, initModel, initMiddleware, initExtend, initSchedule }Copy the code

At this point we have completed the core loading part of the package, introducing /core/index.js in app.js

Const Application = require('./core'); const app = new Application(); app.start(app.$config.port || 3000);Copy the code

This starts a back-end service, which then implements a simple query interface

Interface sample

Create user.js in /model folder

/model/user.js module.exports = app => { const { mongoose } = app; const Schema = mongoose.Schema // Schema const usersSchema = new Schema({ username: { type: String, required: [true,'username cannot be empty ']}, password: {type: String, required: [true,'password cannot be empty ']}, name: {type: String, default: '' }, email: { type: String, default: '' }, avatar: { type: String, default: '' } }, {timestamps: {createdAt: 'created', updatedAt: 'updated'}}) return mongoose.model('user', usersSchema); };Copy the code

Create user.js under /service directory

// service/user.js module.exports = app => ({// get personal information async getUser() {return await app.$model.user.find(); }});Copy the code

3, create user controller, /controller create user.js

// controller/user.js module.exports = app => ({let {CTX, $service} = app; // controller/user.js module.exports = app => ({let {CTX, $service} = app; let userData = await $service.user.getUser(); ctx.body = userData; }})Copy the code

4. Add the router configuration

module.exports = app => { const { router, $controller } = app; Router.get ('/ userList ', $controller.user.getUser); return router };Copy the code

This completes the simple interface example. NPM run dev can visit http://localhost:3000/userlist to access the interface

The above is my own thoughts and understanding of KOA2’s implementation of MVC. Meanwhile, I salute to Egg and welcome the correction and criticism of various gods.

More recommended

  • Vue + Koa from scratch to create an H5 page visual editor – Quark-H5

  • Egg + Vue +mongodb practice development online document management platform — Ink document