JavaScript中的模型 – 视图 – 控制器

Model-View-Controller in JavaScript

tl;dr:如何以一种干净的方式在javascript中实现MVC?

我正在尝试用JavaScript实现MVC。我用我的代码在谷歌上搜索和重新组织了无数次,但没有找到合适的解决方案。(代码只是"感觉不好"。)

这就是我现在的情况。它非常复杂,使用起来很痛苦(但仍然比以前的代码要好)。它有着丑陋的解决办法,这有点破坏了MVC的目的。

如果你真的很勇敢的话,你会发现这一团糟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// Create a"main model"
var main = Model0();

function Model0() {
    // Create an associated view and store its methods in"view"
    var view = View0();

    // Create a submodel and pass it a function
    // that will"subviewify" the submodel's view
    var model1 = Model1(function (subview) {
        view.subviewify(subview);
    });

    // Return model methods that can be used by
    // the controller (the onchange handlers)
    return {
        'updateModel1': function (newValue) {
            model1.update(newValue);
        }
    };
}

function Model1(makeSubView) {
    var info = '';

    // Make an associated view and attach the view
    // to the parent view using the passed function
    var view = View1();
    makeSubView(view.__view); // Dirty dirty

    // Return model methods that can be used by
    // the parent model (and so the controller)
    return {
        'update': function (newValue) {
            info = newValue;

            // Notify the view of the new information
            view.events.value(info);
        }
    };
}

function View0() {
    var thing = document.getElementById('theDiv');
    var input = document.getElementById('theInput');

    // This is the"controller", bear with me
    input.onchange = function () {
        // Ugly, uses a global to contact the model
        main.updateModel1(this.value);
    };

    return {
        'events': {},

        // Adds a subview to this view.
        'subviewify': function (subview) {
            thing.appendChild(subview);
        }
    };
}

// This is a subview.
function View1() {

    var element = document.createElement('div');
    return {
        'events': {
            // When the value changes this is
            // called so the view can be updated
            'value': function (newValue) {
                element.innerHTML = newValue;
            }
        },

        // ..Expose the DOM representation of the subview
        // so it can be attached to a parent view
        '__view': element
    };
}

如何以更清晰的方式在JavaScript中实现MVC?我该如何改进这个系统?或者这是完全错误的方式,我应该遵循另一种模式吗?


对于JavaScriptJavascriptmvc和pureMVC,至少有两个已经建立并可用的MVC框架。可能还有更多。我已经使用javascriptmvc来开发基于浏览器和空中应用程序,并且一直在使用它——它有问题,但我发现它非常有用。还有其他的解决办法,看看萨米,我听说了一件新的好事。我没用过自己,但打算很快试试。我对它的了解还不足以正确地描述它,但在我看来,它就像一个前端控制器,可以在路由、模板系统和RESTful数据存储上工作。我不确定它是不是MVC,但它有相似的成分。

我不同意姆韦的回答。MVC在JavaScript中的实现可能有点不同,但它的好处对于组织这种混乱非常重要。通常与OO语言相关联的设计模式不会因为JS不是基于类的而退出窗口。

我会说,MVC比基于请求的(服务器端)应用程序更适合于JavaScript应用程序。这些对象可以在一个单页的JavaScript应用程序中停留一段时间——如果不是几小时,则是几分钟——并且有一种组织良好的交互方式,可以使代码更加健壮,易于处理。有关于这个问题的书。

关于您发布的代码的其他几点。

  • 视图对象负责将事件侦听器应用于DOM元素。这是管制员的工作。视图只呈现HTML——控制器监听事件并相应地执行操作。
  • 你的模特似乎知道你的观点。模型层应该对视图层了解最少(可能注册为观察员)。保持您的模型整洁,我的意思是业务点-业务逻辑。在JS应用程序中,您可能只是代理一个服务器端模型层,但保持模型符合业务逻辑而不是其他逻辑对于您的理智很重要。应用程序逻辑是控制器的工作

老实说,MVC不太适合JavaScript。它可以支持设计的基本原理,当然,您可以创建伪类作为控制器或模型,支持基本继承,并且您可以让它操纵或创建任意数量的DOM元素,但是您要为此付出代价——开销、可访问性和可用性。

在我看来,我认为javascript更像是一种增强——亲吻心理的存在有一个很好的理由。如果您对更好地组织代码的方法感兴趣,那么总是可以选择将相关功能打包成模块(sic)并根据需要抽象出部分。例如,创建一个工厂来执行更复杂的Ajax请求管理,或者创建一个伪类来处理类似类型的数据。使用控制器的标准基本函数、模型的另一个基本函数等作为这些对象的新实例的原型可以实现类似的功能…但同样,这有点违背了JavaScript的本质。

但是,如果您仅仅为了结构而坚持MVC思想,请考虑如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
;(function(window, $) {
    /**
     * Event Object
     * A quick description goes here.
     **/

    var Events = window.Events = {
        'bindTrackables': function() {
            $('a.trackable').live('click', function() {
                if(!_gaq)
                    _gaq = [];
                _gaq.push(['_trackPageview', '/ajax/foobar']);
            });
        },
        'bindSomeEvent': function() {
            // etc
        }
    };

    /**
     * Data Cache
     * I'll need to remember stuff later, so I store it here
     **/

    var Cache = window.Cache = {
        'data': {},
        'store': function(key, value) {
            Cache.data[key] = value;
        },
        'fetch': function(key) {
            return Cache.data[key];
        }
    };

    /**
     * Request Object
     * Stores native AJAX requests for later use
     **/

    var Request = window.Request = {
        'current_requests': [],
        'send': function(url, type, data, callback) {
            Request.current_requests.push($.ajax({
                'url': url,
                'type': type,
                'data': data,
                'callback': callback
            }));
        },
    }

    // add some private logic here
})(window, jQuery);

这是非常基本的,但你明白了。模块化代码是关键…在JS中,这比强制应用程序(或语言)适应某种风格更重要。