JavaScript中的代码组织:MVC?自渲染组件?

JavaScript中的代码组织:MVC?自渲染组件?,javascript,jquery,model-view-controller,components,code-organization,Javascript,Jquery,Model View Controller,Components,Code Organization,我正在构建一个非常重JS的web应用程序。我说它很重JS,因为绝大多数正在进行的工作都是在客户机上完成的,尽管有一些使用AJAX和XMPP来回同步到服务器 这是我第一次使用jQuery在纯JS中构建这种规模的东西,所以我开始以模仿Rails的方式使用MVC组织代码。例如,如果用户单击按钮,则在控制器对象中调用一个方法,该方法从模型中提取一些数据,然后将数据传递给视图函数。我这样做几乎是为了所有的事情,甚至是像显示一个小弹出窗口这样的琐碎动作 几个月后,我觉得我没有充分利用这门语言。我真的应该将我

我正在构建一个非常重JS的web应用程序。我说它很重JS,因为绝大多数正在进行的工作都是在客户机上完成的,尽管有一些使用AJAX和XMPP来回同步到服务器

这是我第一次使用jQuery在纯JS中构建这种规模的东西,所以我开始以模仿Rails的方式使用MVC组织代码。例如,如果用户单击按钮,则在控制器对象中调用一个方法,该方法从模型中提取一些数据,然后将数据传递给视图函数。我这样做几乎是为了所有的事情,甚至是像显示一个小弹出窗口这样的琐碎动作

几个月后,我觉得我没有充分利用这门语言。我真的应该将我的视图呈现为网页吗

将视图分解为组件似乎更有意义,这些组件将是Javascript对象/函数的实例。例如,而不是

var itemListHTML = '<ul id="item-list"><li>item1</li><li>item2</li></ul>';
jQuery('#container').html(itemListHTML);
这里只有一个叫做itemList的组件。由于所有数据都存储在客户机上,这些组件可以立即访问创建和管理自己所需的所有数据。我想我仍然会使用MVC,但不需要负责整个视图的控制器操作。如果我想刷新UI的一部分,我只需调用whateverComponentControlsThatArea.redraw,它就会重新呈现自己


我肯定以前有人做过这件事。这种类型的代码组织有名称吗?有关于如何实现它的指南或最佳实践吗?

现在有许多javascript MVC框架可用 例如 视图或子视图的渲染通常通过某种模板引擎来处理。 JavaScriptMVC有一个基于ERB的模块,我发现它非常有用。模板可以编译成函数,以加快生产速度。 还有其他模板解决方案,例如John和


如果还不太晚,我建议您使用这些框架之一。我现在已经在一些项目中使用了JavaScriptMVC,我可以向大家推荐,文档需要一些时间才能习惯,不过你真的应该看看jquery.tmpl,它可以创建快速的javascript模板,这是构建视图渲染的理想之选。

晚会有点晚了,但我这里有0.02美元,我不知道该怎么办

暂时忘记典型的Web MVC,例如RailsMVC等等:

考虑到JavaScript都可以驻留在同一个地方,并且您不必担心路由和类实例化,除非您真的想这样做

理想情况下,从软件的角度来看,您希望MVC做的是将用户的行为与基于动作的内容、基于动作的视图进行区分

没有什么可以说您不能有多个视图,甚至您不能有一个包含多个视图的视图,这些视图可能使用模板,或者从功能上生成,并附加到一个DOM节点,这两者都是有效的

在一个简单的伪示例中,而不是具有如下流:

操作加载控制器->控制器加载模型->控制器查询模型->模型应答控制器->控制器加载视图->控制器向视图提供数据->视图向控制器发送页面->控制器输出页面

为什么不这样做呢:

控制器侦听操作addEventListener->Controller将操作转换为状态逻辑->控制器通知模型观察者或模型轮询控制器->模型基于逻辑更改状态,并收集/排序所有数据->模型通知视图观察者或视图轮询模型->视图基于逻辑更改状态->视图渲染所有组件,基于数据+ViewState innerHTML或documentFragment

它看起来有点长,但实际上,一切都是好的和独立的。 使用视图或视图管理器,或者您想怎么想,它会指示页面上有哪些窗口,每个窗口是如何组成的,以及数据的去向,在每篇文章的内部,在每个窗口中

…现在您有了MVC模式,但您还可以说:

View["mainpage"] = {

    data : { tweets : [{id:...}...]/* et cetera - pushed by Model, not Controller */ },
    layout : [ "Header", "Carousel", "Articles", "TwitterFeed", "RSSFeed", "Footer" ],

    // if your system is this clean, you could even prototype the content-builders
    // rather than defining them in each ViewState - you'd just need layout, then
    buildPage : function () {
        var page = document.createDocumentFragment();
        for (/* everything in this.layout */) {
            View.build[this.layout[i]](this.data, page);
        }
        document.body.appendChild(page);
    },

    cleanUp : function () { /* fancy or simple DOM cleaning for state-change */ },

    // grabs SubView by expected handle (id="tweetfeed" or whatever) and replaces it
    // observer functionality, for views to automatically update as data changes
    updateView : function (view, newData) { ... },

    addData : function (data) { this.data = data; }, // for Observer

    /* Observer - if you want to run the WHOLE site with AJAX from index.html
     * clean up old (ex:"main") page and build and/or transition new (ex:"media") page
     * could be unique for each page, for custom transitions, or just prototype it */
    changeState : function (newState, newData) {
        View["mainpage"].cleanUp();
        View[newState].addData( newData );
        View[newState].buildPage();
    }
}
现在您已经定义了整个主页。 当然,不仅要维护主页的定义,还要维护每个页面/部分的定义,这将是额外的工作。 …然后,您需要创建将创建每个子视图的函数-但基于组件的设计正是您所要求的。 本质上,对于每个小部件,它都有自己的构建逻辑。 视图就是这样做的——它们要么有一个可重用的单独模板,要么有一个每次都以相同方式工作的函数

原子视图是一个函数,它构建1个条目1个Tweet,1个Post,1个Store条目,并为此构建视图。 拥有一条Tweet视图,在页面上发布一条Tweet,20次 连续运行对于JS性能来说是个坏主意

但是,拥有一个Tweet视图将Tweet按顺序推送到tweets数组中,然后你的Twitter提要视图将其放在站点上,这是一个不错的选择

更好的是,当您的页面是视图,由视图的小部件组成,由视图的原子单元组成时。 当所有这些都发生在DOM上时,那么innerHTML或documentFragment

最好的框架是这样定义页面,但也可以根据模型中的数据推送随时更新单个小部件

这就是AJAX的美妙之处——老式的MVC可以实现,没有一个无所不知的控制器

控制器只保存一系列按下的键、鼠标坐标、单击按钮的意图。。。 它告诉模型发生了什么。 模型执行与状态/数据/数据操作排序相关的所有操作,并将新的状态或数据提供给视图,视图将按照您指定的顺序将其全部获取并放入HTML中


差不多了。

你看过backbone.js吗?现在读这篇文章,看起来很棒,所以很难说出你真正的问题是什么
View["mainpage"] = {

    data : { tweets : [{id:...}...]/* et cetera - pushed by Model, not Controller */ },
    layout : [ "Header", "Carousel", "Articles", "TwitterFeed", "RSSFeed", "Footer" ],

    // if your system is this clean, you could even prototype the content-builders
    // rather than defining them in each ViewState - you'd just need layout, then
    buildPage : function () {
        var page = document.createDocumentFragment();
        for (/* everything in this.layout */) {
            View.build[this.layout[i]](this.data, page);
        }
        document.body.appendChild(page);
    },

    cleanUp : function () { /* fancy or simple DOM cleaning for state-change */ },

    // grabs SubView by expected handle (id="tweetfeed" or whatever) and replaces it
    // observer functionality, for views to automatically update as data changes
    updateView : function (view, newData) { ... },

    addData : function (data) { this.data = data; }, // for Observer

    /* Observer - if you want to run the WHOLE site with AJAX from index.html
     * clean up old (ex:"main") page and build and/or transition new (ex:"media") page
     * could be unique for each page, for custom transitions, or just prototype it */
    changeState : function (newState, newData) {
        View["mainpage"].cleanUp();
        View[newState].addData( newData );
        View[newState].buildPage();
    }
}