背景Express和Koa都是轻量级的web框架,没有任何约束的框架一开始会很清爽,开发几个demo,就可以轻松掌握它,但是一旦代码真正往上走(而且它会往上走),你会发现有很多重复的操作和重复的逻辑。结果,项目的复杂度越来越高,代码越来越丑陋,维护起来非常困难。我的quark-h5也开始乱写,写到最后只能重构一个wave。正好期间用egg.js做了一个在线文档管理的项目,让我感觉自己是一个node小白。在重构quark-h5服务端时,我参考了egg.js实现了基于koa2的MVC结构。Github:传送门
Koamvc项目目录结构规划这里是参考eggjs的目录结构,这样的目录结构很清爽,构建应用也是因为我们有一个像样的包,只需要几行代码就可以实现mvckoa2的基本加载过程-->app-->importconfig--->importcontroller--->importserver--->importextend--->importrouter--->importmodel--->导入定时任务--->初始化默认中间件--->实现--->挂载到ctx--->ctx使用nodejsfs文件模块全局扫描各个模块文件夹,获取js文件,导出js内容分配给全局app对象,通过app全局对象访问模块。下面看核心加载代码实现:/core/index.js/***封装koamvc基础设施初始化工作*/constpath=require('path')constkoa=require('koa');const{initConfig,initController,initService,initModel,initRouter,initMiddleware,initExtend,initSchedule}=require('./loader');类Application{构造函数(){这个。$app=newKoa();//注册默认中间件this.initDefaultMiddleware();//初始化配置this.$config=initConfig(this);//初始化控制器this.$controller=initController(this);//初始化服务this.$service=initService(this);//初始化中间件this.$middleware=initMiddleware(this);//初始化模型this.$model=initModel(this)//初始化路由器this.$router=initRouter(this);//初始化扩展initExtend(this);//初始化定时任务scheduleinitSchedule(this)//将ctx注入appthis.$app.use(async(ctx,next)=>{this.ctx=ctx;awaitnext()})this.$app.use(this.$router.routes());}//设置内置中间件initDefaultMiddleware(){constkoaStatic=require('koa-static');constkoaBody=require('koa-body');constcors=require('koa2-cors');constviews=require('koa-views');//配置静态网页this.$app.use(koaStatic(path.resolve(__dirname,'../public')),{gzip:true,setHeaders:function(res){res.header('Access-Control-允许来源','*')}});//跨域处理this.$app.use(cors());//Body接口数据处理this.$app.use(koaBody({multipart:true,formidable:{maxFileSize:3000*1024*1024//设置上传文件最大大小限制,默认30M}}));//配置要渲染的文件路径和文件后缀this.$app.use(views(path.join(__dirname,'../views'),{extension:'ejs'}))}//startservicestart(port){this.$app.listen(port,()=>{console.log('服务器正在启动.......!');});}}module.exports=应用程序;loader加载器负责解析每个文件夹中的内容,挂载到全局app实例上/core/loader.js实现逻辑constpath=require('path')constfs=require('fs')constRouter=require('koa-router');constschedule=require("node-schedule");constmongoose=require('mongoose')//自动扫描指定目录下的文件并加载函数scanFilesByFolder(dir,cb){let_folder=path.resolve(__dirname,dir);if(!getFileStat(_folder)){返回;}尝试{常量文件=fs.readdirSync(_folder);files.forEach((file)=>{letfilename=file.replace('.js','');letoFileCnt=require(_folder+'/'+filename);cb&&cb(filename,oFileCnt);})}catch(error){console.log('文件自动加载失败...',error);}}//检查文件夹是否存在/***@param{string}path*/functiongetFileStat(path){try{fs.statSync(path);返回真;}catch(err){返回错误;}}//配置信息constinitConfig=function(app){letconfig={};scanFilesByFolder('../config',(文件名,内容)=>{配置={...配置,...内容};});returnconfig;};//初始化路由constinitRouter=function(app){constrouter=newRouter();require('../router.js')({...app,router});returnrouter;}//初始化控制器constinitController=function(app){letcontrollers={};scanFilesByFolder('../controller',(filename,controller)=>{controllers[filename]=controller(app);})returncontrollers;}//初始化servicefunctioninitService(app){letservices={};scanFilesByFolder('../service',(filename,service)=>{services[filename]=service(app);})returnservices;}//初始化modelfunctioninitModel(app){//链接数据库,配置数据库链接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);//展开应用程序的两个属性app.$mongoose=猫鼬;app.$db=mongoose.connection}//初始化模型文件夹letmodel={};scanFilesByFolder('../model',(filename,modelConfig)=>{model[filename]=modelConfig({...app,mongoose});});returnmodel;}//初始化中间件middlewarefunctioninitMiddleware(app){letmiddleware={}scanFilesByFolder('../middleware',(filename,middlewareConf)=>{middleware[filename]=middlewareConf(app);})//初始化配置中间件.use(middleware[mid]);}})}returnmiddleware;}//初始化扩展函数initExtend(app){scanFilesByFolder('../extend',(filename,extendFn)=>{app[filename]=Object.assign(app[filename]||{},extendFn(app))})}//加载定时任务函数initSchedule(){scanFilesByFolder('../schedule',(filename,scheduleConf)=>{schedule.scheduleJob(scheduleConf.interval,scheduleConf.handler)})}module.exports={initConfig,initController,initService,initRouter,initModel,initMiddleware,initExtend,initSchedule}至此我们完成了核心加载包部分,在app.js中引入/core/index.js项目入口,在app.js中引用core创建实例constApplication=require('./core');constapp=newApplication();app.开始(应用程序。$config.port||3000);这样就启动了一个后端服务,然后实现了一个简单的查询接口接口示例1.创建用户模型,在/model文件夹下新建user.js/model/user.jsmodule。exports=app=>{const{mongoose}=app;constSchema=mongoose.Schema//SchemaconstusersSchema=newSchema({username:{type:String,required:[true,'usernamecannotbeempty']},password:{type:String,required:[true,'passwordcannotbeempty']},name:{type:String,default:''},email:{type:String,default:''},avatar:{type:String,default:''}},{timestamps:{createdAt:'created',updatedAt:'updated'}})返回mongoose.model('user',usersSchema);};2.创建用户查询服务,在/service目录下新建user.js///service/user.jsmodule.exports=app=>({//获取个人信息asyncgetUser(){returnawaitapp.$model.user.find();}});3.创建用户控制器,在/controller文件夹下创建user.js///controller/user.jsmodule.exports=app=>({//获取用户信息asyncgetUser(){let{ctx,$service}=app;letuserData=await$service.user.getUser();ctx.body=userData;}})4.添加路由配置module.exports=app=>{const{router,$controller}=app;//示例接口router.get('/userlist',$controller.user.getUser);返回路由器};这是一个简单的界面示例npmrundev可以访问http://localhost:3000/userlist来访问这个界面。以上是我自己对koa2实现mvc的思考和理解。同时向egg致敬,欢迎各位高手指正批评。更多推荐Vue+Koa从零开始打造H5页面可视化编辑器——Quark-h5
egg+vue+mongodb开发实践在线文档管理平台——墨客文档
