IT序号网

重构知识解答

luoye 2021年05月25日 程序员 164 0

所有的URL处理函数都放到app.js里显得很乱,而且,每加一个URL,就需要修改app.js。随着URL越来越多,app.js就会越来越长。

如果能把URL处理函数集中到某个js文件,或者某几个js文件中就好了,然后让app.js自动导入所有处理URL的函数。这样,代码一分离,逻辑就显得清楚了。最好是这样:

url2-koa/ 
| 
+- .vscode/ 
|  | 
|  +- launch.json <-- VSCode 配置文件 
| 
+- controllers/ 
|  | 
|  +- login.js <-- 处理login相关URL 
|  | 
|  +- users.js <-- 处理用户管理相关URL 
| 
+- app.js <-- 使用koa的js 
| 
+- package.json <-- 项目描述文件 
| 
+- node_modules/ <-- npm安装的所有依赖包

于是我们把url-koa复制一份,重命名为url2-koa,准备重构这个项目。

我们先在controllers目录下编写index.js

var fn_index = async (ctx, next) => { 
    ctx.response.body = `<h1>Index</h1> 
        <form action="/signin" method="post"> 
            <p>Name: <input name="name" value="koa"></p> 
            <p>Password: <input name="password" type="password"></p> 
            <p><input type="submit" value="Submit"></p> 
        </form>`; 
}; 
 
var fn_signin = async (ctx, next) => { 
    var 
        name = ctx.request.body.name || '', 
        password = ctx.request.body.password || ''; 
    console.log(`signin with name: ${name}, password: ${password}`); 
    if (name === 'koa' && password === '12345') { 
        ctx.response.body = `<h1>Welcome, ${name}!</h1>`; 
    } else { 
        ctx.response.body = `<h1>Login failed!</h1> 
        <p><a href="/">Try again</a></p>`; 
    } 
}; 
 
module.exports = { 
    'GET /': fn_index, 
    'POST /signin': fn_signin 
};

这个index.js通过module.exports把两个URL处理函数暴露出来。

类似的,hello.js把一个URL处理函数暴露出来:

var fn_hello = async (ctx, next) => { 
    var name = ctx.params.name; 
    ctx.response.body = `<h1>Hello, ${name}!</h1>`; 
}; 
 
module.exports = { 
    'GET /hello/:name': fn_hello 
};

现在,我们修改app.js,让它自动扫描controllers目录,找到所有js文件,导入,然后注册每个URL:

// 先导入fs模块,然后用readdirSync列出文件 
// 这里可以用sync是因为启动时只运行一次,不存在性能问题: 
var files = fs.readdirSync(__dirname + '/controllers'); 
 
// 过滤出.js文件: 
var js_files = files.filter((f)=>{ 
    return f.endsWith('.js'); 
}); 
 
// 处理每个js文件: 
for (var f of js_files) { 
    console.log(`process controller: ${f}...`); 
    // 导入js文件: 
    let mapping = require(__dirname + '/controllers/' + f); 
    for (var url in mapping) { 
        if (url.startsWith('GET ')) { 
            // 如果url类似"GET xxx": 
            var path = url.substring(4); 
            router.get(path, mapping[url]); 
            console.log(`register URL mapping: GET ${path}`); 
        } else if (url.startsWith('POST ')) { 
            // 如果url类似"POST xxx": 
            var path = url.substring(5); 
            router.post(path, mapping[url]); 
            console.log(`register URL mapping: POST ${path}`); 
        } else { 
            // 无效的URL: 
            console.log(`invalid URL: ${url}`); 
        } 
    } 
}

如果上面的大段代码看起来还是有点费劲,那就把它拆成更小单元的函数:

function addMapping(router, mapping) { 
    for (var url in mapping) { 
        if (url.startsWith('GET ')) { 
            var path = url.substring(4); 
            router.get(path, mapping[url]); 
            console.log(`register URL mapping: GET ${path}`); 
        } else if (url.startsWith('POST ')) { 
            var path = url.substring(5); 
            router.post(path, mapping[url]); 
            console.log(`register URL mapping: POST ${path}`); 
        } else { 
            console.log(`invalid URL: ${url}`); 
        } 
    } 
} 
 
function addControllers(router) { 
    var files = fs.readdirSync(__dirname + '/controllers'); 
    var js_files = files.filter((f) => { 
        return f.endsWith('.js'); 
    }); 
 
    for (var f of js_files) { 
        console.log(`process controller: ${f}...`); 
        let mapping = require(__dirname + '/controllers/' + f); 
        addMapping(router, mapping); 
    } 
} 
 
addControllers(router);

确保每个函数功能非常简单,一眼能看明白,是代码可维护的关键。

Controller Middleware

最后,我们把扫描controllers目录和创建router的代码从app.js中提取出来,作为一个简单的middleware使用,命名为controller.js

const fs = require('fs'); 
 
function addMapping(router, mapping) { 
    ... 
} 
 
function addControllers(router, dir) { 
    ... 
} 
 
module.exports = function (dir) { 
    let 
        controllers_dir = dir || 'controllers', // 如果不传参数,扫描目录默认为'controllers' 
        router = require('koa-router')(); 
    addControllers(router, controllers_dir); 
    return router.routes(); 
};

这样一来,我们在app.js的代码又简化了:

... 
 
// 导入controller middleware: 
const controller = require('./controller'); 
 
... 
 
// 使用middleware: 
app.use(controller()); 
 
...

经过重新整理后的工程url2-koa目前具备非常好的模块化,所有处理URL的函数按功能组存放在controllers目录,今后我们也只需要不断往这个目录下加东西就可以了,app.js保持不变。


发布评论
IT序号网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

koa-router知识解答
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。