65.9K
CodeProject 正在变化。 阅读更多。
Home

在 BackboneJs 上实现 RequireJs

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2012年5月3日

CPOL

2分钟阅读

viewsIcon

25708

downloadIcon

703

使用 RequireJS 在 BackboneJs 上更新 MVC 音乐商店应用。

引言

在本教程中,我们将会在我们的 BackboneJs应用 中实现 RequireJs 异步模块定义模式。   

背景

什么是AMD以及为什么使用AMD? 

视频比文字更能说明问题。以下链接将肯定能帮助你理解AMD和RequireJs。 

为什么使用AMD? - 第一部分 

为什么使用AMD? - 第二部分 

使用代码 

由于我们现有的代码 BackboneJs应用 将backbone模型、视图和集合放在一个脚本文件中,为了引入AMD,我们需要对其进行模块化。因此,显而易见的答案是将视图、模型和集合移动到不同的js文件中。为了更好地理解,让我们将javascript文件组织成如下文件夹:

1, Scripts/Model/GenreModel.js 

2, Scripts/Collection/GenreCollection.js

3, Scripts/View/StoreManager/GenreApplicationView.js 

4, Scripts/View/StoreManager/GenreView.js 

5, Scripts/View/StoreManager/GenreEditView.js 

6, Scripts/View/StoreManager/GenreDeleteView.js

如果你已经下载了源代码,你可能会想知道以下脚本是做什么的

1, Scripts/RequireUtil/RequireJquery.js 

2, Scripts/RequireUtil/RequireJqueryUI.js  

3, Scripts/RequireUtil/RequireUnderscore.js 

4, Scripts/RequireUtil/RequireBackbone.js  

这里的问题是,最新版本的jQuery、jQueryUI、Underscore和Backbone不再模块化,简单来说,它们不支持RequireJs或AMD。我们所做的是一个简单的变通方法,将jQuery等转换为命名模块,并将其作为依赖项传递给我们的其他模块。如果你想跳过此步骤,可以在Google上搜索模块化的Jquery、Underscore和Backbone.js。它们可以完美地工作。 

 
//RequireJquery.js
define(['order!../jquery'], function () {
    return $;
});

//RequireJqueryUI
define(['order!../jquery-ui'], function () {
    return $;
});

//RequireUnderscore
define(['order!../underscore'], function () {
    return _;
});

//RequireBackbone
define(['order!../backbone'], function () {
    _.noConflict();
    $.noConflict();
    return Backbone.noConflict();
});

//GenreApp - This is the main file we will be loading on the Genre.shtml
require.config({
    paths: {
        jQuery: '../RequireUtil/RequireJquery',
        jQueryUI: '../RequireUtil/RequireJqueryUI',
        order: "../RequireUtil/order",
        Underscore: '../RequireUtil/RequireUnderscore',
        Backbone: '../RequireUtil/RequireBackbone',
        GenreModel: '../Model/GenreModel',
        GenreCollection: '../Collection/GenreCollection',
        GenreApplicationView: '../View/StoreManager/GenreApplicationView',
        GenreView: '../View/StoreManager/GenreView',
        GenreDeleteView: '../View/StoreManager/GenreDeleteView',
        GenreEditView: '../View/StoreManager/GenreEditView'
    }
});

require(['order!jQuery', 'order!jQueryUI', 'GenreCollection', 'GenreApplicationView'], function ($, jqueryui, GenreCollection, AppView) {
    $(function () {
        $("#GenreEditDialog").dialog({
            autoOpen: false,
            show: "blind",
            hide: "explode",
            modal: true,
            height: 250,
            width: 600,
            open: function (event, ui) {
                $('.ui-dialog-titlebar', ui.dialog || ui).hide();
            }
        });

        var genres = new GenreCollection();
        var view = new AppView({ collection: genres });

        genres.fetch({ success: function () {
            view.render();
        } 
        });
    });
});

//GenreApplicationView
define(['order!jQuery', 'order!Underscore', 'order!Backbone', 'GenreView', 'GenreEditView', 'GenreModel', 'GenreCollection'],
function ($, _, Backbone, GenreView, EditView, Genre, GenreCollection) {
    // Actual App view
    var AppView = Backbone.View.extend({
        initialize: function () {
            this.collection.bind('add', this.AppendGenre, this);
        },
        el: '#Genre_Container',
        events: {
            "click #btnCreateNew": "AddNewGenre"
        },
        AddNewGenre: function () {
            var newGenre = new Genre();
            var editView = new EditView({ model: newGenre, collection: this.collection });
            editView.render();
        },
        AppendGenre: function (genre) {
            var genreView = new GenreView({ model: genre });
            $(this.el).find('#Genre_List').append(genreView.render().el);
        },
        render: function () {
            if (this.collection.length > 0) {
                this.collection.each(this.AppendGenre, this);
            }
            $("input:button", "#Genre_List").button();
        }
    });

    return AppView;
});

// Genre View - el returns the template enclosed within a tr
define(['order!jQuery', 'order!Underscore', 'order!Backbone', 'order!GenreEditView', 'order!GenreDeleteView'], function ($, _, Backbone, EditView, DeleteView) {
    var GenreView = Backbone.View.extend({
        tagName: "tr",
        initialize: function () {
            this.template = _.template($('#Genre-Template').html());
            this.model.bind('change', this.render, this);
            this.model.bind('remove', this.unrender, this);
        },
        render: function () {
            $(this.el).html(this.template(this.model.toJSON()));
            return this;
        },
        unrender: function () {
            $(this.el).remove();
            return this;
        },
        events: {
            "click .Edit": 'EditGenre',
            "click .Delete": 'DeleteGenre'
        },
        EditGenre: function () {
            var editView = new EditView({ model: this.model });
            editView.render();
        },
        DeleteGenre: function () {
            var deleteView = new DeleteView({ model: this.model });
            deleteView.render();
        }
    });

    return GenreView;
});

//Genre-Edit view
define(['order!jQuery', 'order!jQueryUI', 'order!Underscore', 'order!Backbone'], function ($, jqueryui, _, Backbone) {
    var GenreEditView = Backbone.View.extend({
        tagName: "table",
        container: "#Genre_Edit",
        initialize: function () {
            this.template = _.template($('#Genre-Edit-Template').html())
        },
        events: {
            "change .gName": 'NameChange',
            "change .gDesc": 'DescChange',
            "click .Update": 'UpdateGenre',
            "click .Cancel": 'CancelGenre'
        },
        NameChange: function () {
            // Directly bind the name change to the model, best use of back bone.
            // Otherwise set the data during save which ever you prefer
            this.model.set({ Name: $(".gName").val() });
        },
        DescChange: function () {
            this.model.set({ Description: $(".gDesc").val() });
        },
        render: function () {
            $(this.container).append($(this.el).html(this.template(this.model.toJSON())));
            $("input:button", $(this.el)).button();
            $("#GenreEditDialog").dialog("open");
            var title = 'Create Genre';
            if (this.collection == null)
                title = 'Edit Genre - ' + this.model.get('Name');
            $(this.container).find('legend').html(title);
            $('.ui-dialog-titlebar').hide();
            $('#valSum').hide("slow");
            return this;
        },
        unrender: function () {
            $(this.el).remove();
            return this;
        },
        UpdateGenre: function () {
            if (this.model.validateModel()) {
                var self = this;
                if (this.collection != null) {
                    this.collection.add(this.model);
                }
                this.model.save(this.model, { success: function () {
                    self.unrender();
                    $("#GenreEditDialog").dialog("close");
                    var genreId = self.model.get('GenreId');
                    $("input:button", '#Genre_List').button();
                }
                });
            }
            $("input:button", '#Genre_List').button();
        },
        CancelGenre: function () {
            this.model.fetch({ success: function () {
                $("input:button", '#Genre_List').button();
            }
            });
            this.unrender();
            $("#GenreEditDialog").dialog("close");
        }
    });

    return GenreEditView;
});

// Genre Delete View
define(['order!jQuery', 'order!jQueryUI', 'order!Underscore', 'order!Backbone'], function ($, jqueryui, _, Backbone) {
    var GenreDeleteView = Backbone.View.extend({
        container: "#Genre_Edit",
        initialize: function () {
            this.template = _.template($('#Genre-Delete-Template').html());
        },
        events: {
            "click .Update": 'DeleteGenre',
            "click .Cancel": 'CancelGenre'
        },
        render: function () {
            $(this.container).append($(this.el).html(this.template(this.model.toJSON())));
            $("input:button", $(this.el)).button();
            $("#GenreEditDialog").dialog("open");
            $('.ui-dialog-titlebar').hide();
            var title = 'Delete Genre - ' + this.model.get('Name');
            $(this.container).find('legend').html(title);
            return this;
        },
        unrender: function () {
            $(this.el).remove();
            return this;
        },
        CancelGenre: function () {
            this.unrender();
            $("#GenreEditDialog").dialog("close");
        },
        DeleteGenre: function () {
            var self = this;
            this.model.destroy({ success: function () {
                self.unrender();
                $("#GenreEditDialog").dialog("close");
            }
            });
        }
    });

    return GenreDeleteView;
});

// Genre Model
define(['jQuery', 'Underscore', 'Backbone'], function ($, _, Backbone) {
    var GenreModel = Backbone.Model.extend({
        url: function () {
            var base = '/StoreManager/GenreList/';
            if (this.isNew())
                return base;
            return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + this.id;
        },
        initialize: function () {
            this.bind("change:GenreId", function () {
                var genreId = this.get('GenreId');
                this.set({ id: genreId });
            });
        },
        defaults: {
            GenreId: 0,
            Name: '',
            Description: ''
        },
        validateModel: function () {
            var validate = true;
            var divHtml = "<div class='ui-state-error ui-corner-all' style='display: block; padding-top: 0px; padding-bottom: 0px; width: 500px; padding-left: 0.7em; padding-right: 0.7em;'>";
            divHtml += "<p><span class='ui-icon ui-icon-alert' style='float: left; margin-right: .3em;'></span><strong>Please correct these Errors</strong> </p><div class='validation-summary-errors' data-valmsg-summary='true'>";
            divHtml += "</div><p></p></div>";
            var ulHtml = "<ul></ul>";
            var div = $(divHtml);
            var ul = $(ulHtml);
            if (this.get('Name') == "") {
                ul.append("<li>Name is Mandatory</li>");
                validate = false;
            }
            if (this.get('Description') == "") {
                ul.append("<li>Description is Mandatory</li>");
                validate = false;
            }
            div.find(".validation-summary-errors").append(ul);

            if (!validate) {
                $('#valSum').show("slow");
                $('#valSum').html(div);
            }
            else {
                $('#valSum').hide("slow");
                $('#valSum').html("");
            }

            return validate;
        }
    });

    return GenreModel;
});

// GenreCollection
define(['jQuery', 'Underscore', 'Backbone', 'GenreModel'], function ($, _, Backbone, Genre) {
    var GenreCollection = Backbone.Collection.extend({
        model: Genre,
        url: '/StoreManager/GenreList/'
    });
    return GenreCollection;
});

请记住,以上代码不在单个js文件中,它们位于不同的js文件中。现在我们需要在我们的Genre.shtml中引用Require.js,并使其加载GenreApp.js(注意脚本标签上的data-main属性)及其依赖项。 

@{
    ViewBag.Title = "Genres";
}
<script data-main="@Url.Content("~/Scripts/App/GenreApp")" src="@Url.Content("~/Scripts/require.js")" type="text/javascript"></script>
<div class="styler">
    <fieldset class="ui-widget">
        <legend class="ui-state-legend-default ui-corner-top ui-corner-bottom">Genre List -
            Using Backbone</legend>
        <div id="Genre_Container">
            <input type="button" value="Create New" class="Add" id="btnCreateNew" />
            <table id="Genre_List">
                <tr>
                    <th>
                        Name
                    </th>
                    <th>
                        Description
                    </th>
                    <th>
                    </th>
                </tr>
            </table>
            <div id="GenreEditDialog" style="height: 350">
                <div class="styler">
                    <fieldset class="ui-widget" id="Genre_Edit" style="height: 350">
                        <legend class="ui-state-legend-default ui-corner-top ui-corner-bottom"></legend>
                        <br />
                        <div id="valSum" >
                        </div>
                    </fieldset>
                </div>
            </div>
        </div>
        <script id='Genre-Template' type='text/template'>
                <td><%=Name%></td> <td><%=Description%></td>
                <td><input type="button" value= "Edit" class="Edit" /> | <input type="button" value= "Delete" class="Delete" /> </td>
        </script>
        <script id='Genre-Edit-Template' type='text/template'>
               <tr>
                <td>
                    <div class="editor-label">
                        Name</div>
                    <div class="editor-field">
                        <input type="text" class="gName" value='<%=Name%>' />
                    </div>
                </td>
                <td>
                    <div class="editor-label">
                        Description</div>
                    <div class="editor-field">
                        <input type="text" class="gDesc" value='<%=Description%>' />
                    </div>
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <input type="button" value="Save" class="Update" /> | <input type="button" id="btnCancel" value="Cancel" class="Cancel" />
                </td>
            </tr> 
        </script>
        <script id='Genre-Delete-Template' type='text/template'>
            <div class="ui-state-highlight ui-corner-all" style="margin-top: 20px; padding: 0 .7em;
            width: 500px">
                <p>
                <span class="ui-icon ui-icon-info" style="float: left; margin-right: .3em;"></span>
                <strong>Confirm</strong><br />
                Are you sure you want to delete this Album - <%=Name%> ?</p>
            </div><br/>
            <table>
                <tr>
                    <td>
                        <div class="editor-label">
                            Name</div>
                        <div class="editor-field">
                            <%=Name%>
                        </div>
                    </td>
                    <td>
                        <div class="editor-label">
                            Description</div>
                        <div class="editor-field">
                            <%=Description%>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td colspan="2">
                        <input type="button" value="Delete" class="Update" /> | <input type="button" id="btnCancel" value="Cancel" class="Cancel" />
                    </td>
                </tr>
            </table>
        </script>
    </fieldset>
</div>

关注点

要体会RequireJs的重要性及其优势,必须在你的代码中大量使用它。此外,当你的项目不断增长时,RequireJs的优势会变得非常明显,管理依赖关系可能会成为一个难题。  此外,RequireJs将帮助你更有效地测试你的javascript。我们将在后续教程中看到这一点,例如实现jasmine或qunit。 在此之前,尝试一下RequireJs。以下是一些我希望你感兴趣的链接。 

https://requirejs.node.org.cn/docs/jquery.html 

http://developer.teradata.com/blog/jasonstrimpel/2011/12/part-1-backbone-js-require-js

http://www.bennadel.com/blog/2287-Using-jQuery-As-A-Named-Module-In-RequireJS.htm

http://www.davidgranado.com/2012/01/getting-jquery-1-7-andor-backbone-to-play-nicely-with-requirejs/ 

 -- 匿名用户  

© . All rights reserved.