Name: Password: Sign in
玩具 · 实验 中的文章
  • 使用 Node 进行理想服务器架构的实验(坑)
  • Graphviz 测试
  • 一个 JavaScript 模板引擎的实现
  • 本文在 署名-非商业性使用-相同方式共享 3.0 版权协议下发布, 转载请注明出自 kyleslight.net

    使用 Node 进行理想服务器架构的实验(坑)

    1 基本想法

    request - handler - template - view 作为一条映射,映射的规则可以不同,但是尽可能保证映射的环节中尽可能少的出现人工干预(只保留必要的),减少开发中人的失误导致的问题。

    一个简单的例子,例如我们想引用模块 A ,如果引用 A 的路径在多处写死,那么一旦修改了 A 的位置就要人手工维护一大串路径(即使路径是相对的也一样)。

    当然这个例子可能不妥(现实中模块路径的改变并不那么频繁),不过减少模块与模块间的依赖确实是一个正确的思路。理想的状态是

    1. 任何独立模块间改动对其他模块都没有影响
    2. 任何存在引用与继承关系的模块需要聚合在一起,且发生改变之后所有的地方都会联动改变

    2 依赖项

    server 端
    服务器框架: Express
    模板引擎: Numjucks
    自动化工具: Grunt
    CSS预处理: Less
    数据库: MongoDB

    client 端
    模块化:Requirejs
    数据绑定:AngularJS
    响应式: Bootstrap

    3 项目结构

    - Project
        | - index.js // 项目入口
        | - route.js // 请求分发
        | - handler // 请求处理器集合
            | - BaseHandler.js // 基础处理器,用于构造其他处理器
            | - TemplateHandler1.js // 处理器1
            | - TemplateHandler2.js // 处理器2
            ...
        | - component // 用于自己开发的独立模块存放
            | - component1
            | - component2
            ...
        | - data // 用于给 handler 提供数据库接口,对模型进行封装
            | - BaseDataHandler.js // 基础Data处理器
            | - DataHandler1.js
            | - DataHandler2.js
            ...
        | - public // client 端
            | - source // 编译源文件,例如各种 less 文件
            | - static // 静态文件
                | - common // 封装所有页面公用的静态资源
                | - static1 // 用于template1;注意由于 AngularJS 的使用,该静态目录主要用于存放样式,字体与图片
                | - static2 // 用于template2
            | - template // 模板文件
                | - base.html // 基础模板,定义好了页面的公共元素,例如 nav 与 footer,用于构造其他模板
                | - template1.html
                | - template2.html
                ...
            | - view // 视图,用于分发给 AngularJS,相当于二级 template
                | - view1
                    | - app.js // 用于 AngularJS 路由配置
                    | - widget // 相当于三级 template, 如果二级够用了就不用
                        | - widget1
                            | - widget1.html
                            | - widget1Controller.js
                        | - widget2
                            | - widget2.html
                            | - widget2Controller.js
                        ...
                    | - resource
                | - view2
                ...
    

    4 实验

    我们将 server 的监听与 route 的映射表分开(这两个可以独立),接下来我们把问题收敛到当一个请求过来的时候,我们通过映射表直接找到某个 handler ,在 handler 中进行处理

    var TemplateHandler1 = require('./handler/TemplateHandler1');
    var TemplateHandler2 = require('./handler/TemplateHandler2');
    
    module.exports = function (app) {
        var nunjucks = require('nunjucks');
    
        nunjucks.configure('public', {
            autoescape: true,
            express: app,
            watch: true
        });
    
        var urlMap = {
            'get': {
                '/url1': TemplateHandler1.getPage,
                '/url2': TemplateHandler2.getPage
            }
        };
    
        for (var method in urlMap) {
            for (var url in urlMap[method]) {
                if (urlMap[method].hasOwnProperty(url)) {
                    app[method](url, urlMap[method][url]);
                }
            }
        }
    };
    

    注意需要手工维护的地方,一个是

    var TemplateHandler1 = require('./handler/TemplateHandler1');
    var TemplateHandler2 = require('./handler/TemplateHandler2');
    ...
    

    另一个是

    var urlMap = {
        'get': {
            '/url1': TemplateHandler1.getPage,
            '/url2': TemplateHandler2.getPage
        }
    };
    

    考虑到引入的 handler 是可数的,所以维护成本并不大,项目规模确定之后几乎不用改变

    请求的 动词 映射已经由 urlMap 做了处理,真正需要维护的只有这一个表。当然,它的存在不可避免。

    在这个过程之后就可以安心地写 handler 了(其实也有不少坑)

    var BaseHandler = require('./BaseHandler');
    var TemplateHandler1 = new BaseHandler();
    
    var self = TemplateHandler1;
    
    // handle methods in customized way
    TemplateHandler1.getPage = function (req, res) {
        var books = [
            {
                title: 'a',
                description: 'about a'
            },{
                title: 'b',
                description: 'about b'
            }
        ];
        res.render(self.templatePath + 'template1.html', {books: books});
    };
    
    module.exports = TemplateHandler1;
    

    该项目结构目前已经可以使用,但仍然有许多地方需要调。

    5 存在的问题

    • Grunt 还没有找到很好的从 somepath/(**)/*.less -> anotherpath/$1.css 的方式,这意味着编译文件需要维护
    • Javascript 的继承很弱,目前依然是引用模块提供独立服务的思路
    Copyright (c) 2014-2016 Kyles Light.
    Powered by Tornado.
    鄂 ICP 备 15003296 号