当前位置: 首页 > 科技观察

像Vue-Router一样配置Node路由?

时间:2023-03-23 11:39:38 科技观察

本文转载自微信公众号《黑头鱼》,作者前端黑头鱼。转载本文请联系前端胖头鱼公众号。前后端分离后,前端童鞋会需要处理一些node层的工作,比如模板渲染,接口转发,一些业务逻辑等,比较常用的框架有koa,koa-router等现在我们需要实现这样一个需求:当用户访问/fe时,页面显示hellofe当用户访问/backend时,页面显示hellobackend你以为我不需要koa吗,koa-路由器和本机节点满足此要求?模块可以解决问题。consthttp=require('http')consturl=require('url')constPORT=3000http.createServer((req,res)=>{let{pathname}=url.parse(req.url)letstr='hello'if(pathname==='/fe'){str+='fe'}elseif(pathname==='/backend'){str+='backend'}res.end(str)}).listen(PORT,()=>{console.log(`appstartat:${PORT}`)})确实,对于很简单的需求,使用框架似乎有点浪费,但是对于上面的实现,也有不足之处,比如我们需要自己解析路径。路径分析和逻辑编写耦合在一起。如果以后要实现的需求越来越多,那么gg。那么接下来,我们来试试如何使用koa和koa-router来实现app.jsconstKoa=require('koa')constKoaRouter=require('koa-router')constapp=newKoa()constrouter=newKoaRouter()constPORT=3000router.get('/fe',(ctx)=>{ctx.body='hellofe'})router.get('/backend',(ctx)=>{ctx.body='hellobackend'})app.use(路由器.routes())app.use(router.allowedMethods())app.listen(PORT,()=>{console.log(`appstartat:${PORT}`)})通过以上处理,路径解析为鉴于koa-router已经处理过了,但是整体写法还是有一些问题。没有办法重用匿名函数。路由配置和逻辑处理在一个文件中,不分离。项目做大了也很麻烦。接下来我们再优化一下,先看看整体的目录结构├──app.js//应用入口├──controller//逻辑处理、子模块│├──hello.js│├──aaaaa.js├──middleware//中间件统一注册│├──index.js├──routes//路由配置,可分模块│├──index.js├──views//模板配置,分页面或modules,本例未使用│├──index.html预览各个文件app.js应用交集的逻辑constKoa=require('koa')constmiddleware=require('./middleware')constapp=newKoa()constPORT=3000middleware(app)app.listen(PORT,()=>{console.log(`appstartat:${PORT}`)})routes/index.js路由配置中心constKoaRouter=require('koa-router')constrouter=newKoaRouter()constkoaCompose=require('koa-compose')consthello=require('../controller/hello')module.exports=()=>{router.get('/fe',hello.fe)路由器。get('/backend',hello.backend)返回koaCompose([router.routes(),router.allowedMethods()])}controller/hello.js逻辑hello模块的module.exports={fe(ctx){ctx.body='hellofe'},backend(ctx){ctx.body='hellobackend'}}middleware/index.js中间件统一注册constroutes=require('../routes')module.exports=(app)=>{app.use(routes())}写到这里,大家心里可能有一个疑问?一个简单的需求,做起来好像太复杂了,有必要吗?答案是:必须的,这样的目录结构可能不是最合理的,但是路由、控制器、视图层等各司其职,各司其职,对以后的扩展有很大的帮助。不知道大家有没有注意到routes/index.js路由配置中心里的路由配置constKoaRouter=require('koa-router')constrouter=newKoaRouter()constkoaCompose=require('koa-compose')consthello=require('../controller/hello')module.exports=()=>{router.get('/fe',hello.fe)router.get('/backend',hello.backend)returnkoaCompose([router.routes(),router.allowedMethods()])}每条route对应一个controller去处理,很分离,很通用!!!这好像是我们前端写vue-router或者react-router的常用配置方式。但是当模块多了,这个文件夹就会变成constKoaRouter=require('koa-router')constrouter=newKoaRouter()constkoaCompose=require('koa-compose')//需要require每个模块文件进来consthello=require('../controller/hello')consta=require('../controller/a')constc=require('../controller/c')module.exports=()=>{router.get('/fe',hello.fe)router.get('/backend',hello.backend)//配置各模块和控制器的路由router.get('/a/a',a.a)router.post('/a/b',a.b)router.get('/a/c',a.c)router.get('/a/d',a.d)router.get('/c/a',c.c)router.post('/c/b',c.b)router.get('/c/c',c.c)router.get('/c/d',c.d)//...等等returnkoaCompose([router.routes(),router.allowedMethods()])}有没有什么方法可以不用我们手动一个一个引入controller,然后手动调用koa-router的getpost等方法注册呢?比如我们只需要做如下配置就可以完成上面手动配置的功能。routes/a.jsmodule.exports=[{path:'/a/a',controller:'a.a'},{path:'/a/b',methods:'post',controller:'a.b'},{path:'/a/c',controller:'a.c'},{path:'/a/d',controller:'a.d'}]routes/c.jsmodule.exports=[{path:'/c/a',controller:'c.a'},{path:'/c/b',methods:'post',controller:'c.b'},{path:'/c/c',controller:'c.c'},{path:'/c/d',controller:'c.d'}]然后使用pure-koa-router模块进行简单配置就可以了constpureKoaRouter=require('pure-koa-router')constroutes=path.join(__dirname,'../routes')//指定路由constcontrollerDir=path.join(__dirname,'../controller')//指定controller根目录app.use(pureKoaRouter({routes,controllerDir}))这样我们全程关注路由配置,不再需要手动require一堆文件。简单介绍一下上面的配置{path:'/c/b',methods:'post',controller:'c.b'}path:路径配置,可以是字符串/c/b,也可以是数组['/c/b'],当然也可以是正则表达式/\c\b/methods:指定请求类型,可以是字符串get或者数组['get','post'],默认是get方法,controller:路由匹配的逻辑处理方法,c.b表示从controllerDir目录下的c文件导出的b方法,a.b.c表示从/a/b路径下的b文件导出的c方法源码在controllerDir目录下,我们一步步分析实现逻辑可以点击查看源码整体结构module.exports=({routes=[],controllerDir='',routerOptions={}})=>{/xxxreturnkoaCompose([router.routes(),router.allowedMethods()])})pure-koa-router接收1.routes可以指定路由的文件目录,这样pure-koa-router就会读取d中的所有文件irectory(constroutes=path.join(__dirname,'../routes'))可以指定具体的文件,让pure-koa-router读取指定文件的内容作为路由配置constroutes=path.join(__dirname,'../routes/tasks.js')可以直接指定文件export的内容(constroutes=require('../routes/index'))2.controllerDir,控制器的根目录3.routerOptionsnewKoaRouter时传入的参数,详见koa-router这个包会在koaCompose执行后返回包裹的中间件,供koa实例添加。参数适配assert(Array.isArray(routes)||typeofroutes==='string','routesmustbeanArrayoraString')assert(fs.existsSync(controllerDir),'controllerDirmustbeafiledirectory')if(typeofroutes==='string'){routes=routes.replace('.js','')if(fs.existsSync(`${routes}.js`)||fs.existsSync(routes)){//处理传入的文件if(fs.existsSync(`${routes}.js`)){routes=require(routes)//处理传入的目录}elseif(fs.existsSync(routes)){//读取目录中的每个文件并合并routes=fs.readdirSync(routes.reduce((result,fileName)=>{returnresult.concat(require(nodePath.join(routes,fileName)))},[])}}else{//routes如果是字符串必须是路径afileordirectorythrownewError('routesisnotafileoradirectory')}}路由注册不管路由是作为文件还是目录传入,还是直接导出配置内容,最终的结构是这样的。路由内容预览[//最基本的配置{path:'/test/a',methods:'post',controller:'test.index.a'},//多个路由到一个controller{path:['/test/b','/test/c'],controller:'test.index.a'},//多路由到多控制器{path:['/test/d','/test/e'],controller:['test.index.a','test.index.b']},//到控制器的单一路由{path:'/test/f',controller:['test.index.a','测试。index.b']},//正则{路径:/\/test\/\d/,controller:'test.index.c'}]主动注册letrouter=newKoaRouter(routerOptions)letmiddlewareroutes.forEach((routeConfig={})=>{let{path,methods=['get'],controller}=routeConfig//路由方法类型参数适配methods=(Array.isArray(methods)&&methods)||[methods]//控制器参数适配controller=(Array.isArray(controller)&&controller)||[controller]middleware=controller.map((controller)=>{//'test.index.c'=>['test','index','c']letcontrollerPath=controller.split('.')//方法namecletcontrollerMethod=controllerPath.pop()try{//读取/test/index文件的c方法controllerMethod=require(nodePath.join(controllerDir,controllerPath.join('/')))[controllerMethod]}catch(error){throwerror}//判断读取controllerMethod的参数,必须是一个方法assert(typeofcontrollerMethod==='function','koamiddlewaremustbeafunction')returncontrollerMethod})//最后使用router.register注册router.实现注册过程(路径、方法、中间件)源代码基本上就在这里。最后pure-koa-router将路由配置和controller分离,这样我们就可以专注于路由配置和controller的实现。希望我能帮到你一点点。