这个版本的TodoMVC中的视图组织划分比较细,更加易于理解,这也得益于Marionette为我们带来了丰富的视图选择,原生的backbone只有views,而Marionette则有itemview, collectionview, compositeview 和layoutview.

js/templates.js

/*global define */
define(function (require) {//这里用了简写,因为require.js是CommonJS 模块,相当于 define(["require"],function(require){'use strict';return {todoItemView: require('tpl!templates/todoItemView.tmpl'),//tpl是requireJS的text.js插件的扩展,!符号把templates/todoItemView.tmpl文件的url传给tpl处理返回string内容,详细可以查看js/lib/tpl.js定义,tpl.js是个模版引擎,可以对模板文件进行去空格,去注释,去xml的meta信息等等todosCompositeView: require('tpl!templates/todoListCompositeView.tmpl'),//同上footer: require('tpl!templates/footer.tmpl'),//同上header: require('tpl!templates/header.tmpl')//同上
    };
});

js/templates/head.tmpl

<h1>todos</h1>
<input id="new-todo" placeholder="What needs to be done?" autofocus>

js/views/head.js  这个view是用来实现 输入创建新的todo的view视图

/*global define */
define(['marionette','templates'
], function (Marionette, templates) {'use strict';return Marionette.ItemView.extend({template: templates.header,//参考templates.js里面的模板的定义,通过key取值返回value
ui: {input: '#new-todo'//在Merionette中ItemView可以用ui来组织dom元素方便使用,返回的是jQuery对象
        },events: {'keypress #new-todo': 'onInputKeypress'//原生的backbone的view dom事件绑定,监听Enter键触发下面的新建一个新的todo事件
        },onInputKeypress: function (event) {var ENTER_KEY = 13;var todoText = this.ui.input.val().trim();if (event.which === ENTER_KEY && todoText) {this.collection.create({title: todoText});this.ui.input.val('');}}});
});

js/templates/todoListCompositeView.tmpl

<input id="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list"></ul>

js/views/TodoListCompositeView.js

/*global define */
define(['marionette','templates','views/TodoItemView'
], function (Marionette, templates, ItemView) {'use strict';return Marionette.CompositeView.extend({//compositeView是一种混合view,里面可以包含有itemviewtemplate: templates.todosCompositeView,//在js/templates.js里面定义
itemView: ItemView,//指定itemview为views/TodoItemView
itemViewContainer: '#todo-list',//itemview的container或者说外面的dom节点是#todo-list,注意
ui: {toggle: '#toggle-all'},events: {'click #toggle-all': 'onToggleAllClick'//把全部标记为已完成的ui,及定义相应的事件
        },initialize: function () {this.listenTo(this.collection, 'all', this.updateToggleCheckbox, this);//设置监听collection的任意变动都会触发更新checkbox事件
        },onRender: function () {//render时也触发this.updateToggleCheckbox();},updateToggleCheckbox: function () {var allCompleted = this.collection.reduce(function (lastModel, thisModel) {//调用underscore库的reduce函数把collection中所有的model的completed值循环作一次"与"运算得出有没有没完成,返回true/falsereturn lastModel && thisModel.get('completed');}, true);this.ui.toggle.prop('checked', allCompleted);//如果是allcompleted是true则显示为checked状态
        },onToggleAllClick: function (event) {//点击事件触发把所有的item标为completed或者反选var isChecked = event.currentTarget.checked;this.collection.each(function (todo) {todo.save({ completed: isChecked });});}});
});

js/templates/todoItemView.tmpl

<div class="view"><input class="toggle" type="checkbox"<% if (completed) { %> checked<% } %>><label><%= title %></label><button class="destroy"></button>
</div>
<input class="edit" value="<%= title %>">

js/views/TodoItemView.js

/*global define */
define(['marionette','templates'
], function (Marionette, templates) {'use strict';var ENTER_KEY = 13;var ESCAPE_KEY = 27;return Marionette.CompositeView.extend({//这个view也相对比较复杂,功能也比较多,所以用了compositeviewtagName: 'li',template: templates.todoItemView,value: '',//下面会用到
ui: {edit: '.edit'},events: {//绑定相应的事件'click .toggle': 'toggle','click .destroy': 'destroy','dblclick label': 'onEditDblclick','keydown .edit': 'onEditKeyDown','blur .edit': 'onEditBlur'},initialize: function () {//初始化时执行this.value = this.model.get('title');this.listenTo(this.model, 'change', this.render, this);},onRender: function () {//每次render时执行this.$el.removeClass('active completed').addClass(this.model.get('completed') ? 'completed' : 'active');},destroy: function () {this.model.destroy();},toggle: function () {this.model.toggle().save();},toggleEditingMode: function () {this.$el.toggleClass('editing');},onEditDblclick: function () {this.toggleEditingMode();this.ui.edit.focus().val(this.value);},onEditKeyDown: function (event) {if (event.which === ENTER_KEY) {this.ui.edit.trigger('blur');}if (event.which === ESCAPE_KEY) {this.ui.edit.val(this.model.get('title'));this.ui.edit.trigger('blur');}},onEditBlur: function (event) {this.value = event.target.value.trim();if (this.value) {this.model.set('title', this.value).save();} else {this.destroy();}this.toggleEditingMode();}});
});

js/templates/footer.tmpl

<span id="todo-count"><strong>0</strong> items left</span>
<ul id="filters"><li><a href="#/">All</a></li><li><a href="#/active">Active</a></li><li><a href="#/completed">Completed</a></li>
</ul>
<button id="clear-completed"></button>

js/views/Footer.js

/*global define */
define(['marionette','templates','views/ActiveCount','views/CompletedCount'
], function (Marionette, templates, ActiveCount, CompletedCount) {'use strict';return Marionette.Layout.extend({//footer功能点比较多,有a热点,也有统计用的view如activeCount或者completedCount,所以可以用Layoutview
        template: templates.footer,regions: {activeCount: '#todo-count',//划分regions来管理completedCount: '#clear-completed'},ui: {filters: '#filters a'},events: {'click #clear-completed' : 'onClearClick'},onRender: function () {this.activeCount.show(new ActiveCount({ collection: this.collection }));//调相应的子ActiveCount来显示this.completedCount.show(new CompletedCount({ collection: this.collection }));//调相应的子ActiveCount来显示
        },updateFilterSelection: function (filter) {//在app.js外面被调用this.ui.filters.removeClass('selected').filter('[href="#/' + filter + '"]').addClass('selected');//纯UI控制显示或者隐藏相应的item
        },onClearClick: function () {window.app.vent.trigger('todoList:clear:completed');//调外部在app.js定义的函数
        }});
});

js/views/ActiveCount.js

/*global define */
define(['marionette','jquery'
], function (Marionette, $) {'use strict';return Marionette.View.extend({//用最简单的viewinitialize: function () {this.listenTo(this.collection, 'all', this.render, this);},render: function () {//直接render UI控制显示,计算显示还有多少todo没complete然后显示this.$el = $('#todo-count');var itemsLeft = this.collection.getActive().length;var itemsWord = itemsLeft < 1 || itemsLeft > 1 ? 'items' : 'item';this.$el.html('<strong>' + itemsLeft + '</strong> ' + itemsWord + ' left');}});
});

js/views/CompletedCount.js

/*global define */
define(['marionette','jquery'
], function (Marionette, $) {'use strict';return Marionette.View.extend({//用最简单的viewinitialize: function () {this.listenTo(this.collection, 'all', this.render, this);},render: function () {//直接render UI控制显示,计算有多少todo已经complete然后显示相应的clear completed的菜单this.$el = $('#clear-completed');var completedTodos = this.collection.getCompleted();this.$el.toggle(completedTodos.length > 0).html('Clear completed (' + completedTodos.length + ')');}});
});

总的来说,统一处理是在app.js里面,然后各个子模块提供服务

最后是tpl模板引擎 js/lib/tpl.js 原生js编写,符合AMD规范的模块

/*** Adapted from the official plugin text.js** Uses UnderscoreJS micro-templates : http://documentcloud.github.com/underscore/#template* @author Julien Caban猫s <julien@zeeagency.com>* @version 0.2** @license RequireJS text 0.24.0 Copyright (c) 2010-2011, The Dojo Foundation All Rights Reserved.* Available via the MIT or new BSD license.* see: http://github.com/jrburke/requirejs for details*/
/*jslint regexp: false, nomen: false, plusplus: false, strict: false */
/*global require: false, XMLHttpRequest: false, ActiveXObject: false,define: false, window: false, process: false, Packages: false,java: false */(function () {
//>>excludeStart('excludeTpl', pragmas.excludeTpl)var progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im,bodyRegExp = /<body[^>]*>\s*([\s\S]+)\s*<\/body>/im,buildMap = [],templateSettings = {evaluate    : /<%([\s\S]+?)%>/g,interpolate : /<%=([\s\S]+?)%>/g},/*** JavaScript micro-templating, similar to John Resig's implementation.* Underscore templating handles arbitrary delimiters, preserves whitespace,* and correctly escapes quotes within interpolated code.*/template = function(str, data) {var c  = templateSettings;var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +'with(obj||{}){__p.push(\'' +str.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(c.interpolate, function(match, code) {return "'," + code.replace(/\\'/g, "'") + ",'";}).replace(c.evaluate || null, function(match, code) {return "');" + code.replace(/\\'/g, "'").replace(/[\r\n\t]/g, ' ') + "; __p.push('";}).replace(/\r/g, '').replace(/\n/g, '').replace(/\t/g, '')+ "');}return __p.join('');";return tmpl;/** /var func = new Function('obj', tmpl);return data ? func(data) : func;/**/};
//>>excludeEnd('excludeTpl')
define(function () {
//>>excludeStart('excludeTpl', pragmas.excludeTpl)var tpl;var get, fs;if (typeof window !== "undefined" && window.navigator && window.document) {get = function (url, callback) {var xhr = tpl.createXhr();xhr.open('GET', url, true);xhr.onreadystatechange = function (evt) {//Do not explicitly handle errors, those should be//visible via console output in the browser.if (xhr.readyState === 4) {callback(xhr.responseText);}};xhr.send(null);};} else if (typeof process !== "undefined" &&process.versions &&!!process.versions.node) {//Using special require.nodeRequire, something added by r.js.fs = require.nodeRequire('fs');get = function (url, callback) {callback(fs.readFileSync(url, 'utf8'));};}return tpl = {version: '0.24.0',strip: function (content) {//Strips <?xml ...?> declarations so that external SVG and XML//documents can be added to a document without worry. Also, if the string//is an HTML document, only the part inside the body tag is returned.if (content) {content = content.replace(xmlRegExp, "");var matches = content.match(bodyRegExp);if (matches) {content = matches[1];}} else {content = "";}return content;},jsEscape: function (content) {return content.replace(/(['\\])/g, '\\$1').replace(/[\f]/g, "\\f").replace(/[\b]/g, "\\b").replace(/[\n]/g, "").replace(/[\t]/g, "").replace(/[\r]/g, "");},createXhr: function () {//Would love to dump the ActiveX crap in here. Need IE 6 to die first.var xhr, i, progId;if (typeof XMLHttpRequest !== "undefined") {return new XMLHttpRequest();} else {for (i = 0; i < 3; i++) {progId = progIds[i];try {xhr = new ActiveXObject(progId);} catch (e) {}if (xhr) {progIds = [progId];  // so faster next timebreak;}}}if (!xhr) {throw new Error("require.getXhr(): XMLHttpRequest not available");}return xhr;},get: get,load: function (name, req, onLoad, config) {//Name has format: some.module.filext!strip//The strip part is optional.//if strip is present, then that means only get the string contents//inside a body tag in an HTML string. For XML/SVG content it means//removing the <?xml ...?> declarations so the content can be inserted//into the current doc without problems.var strip = false, url, index = name.indexOf("."),modName = name.substring(0, index),ext = name.substring(index + 1, name.length);index = ext.indexOf("!");if (index !== -1) {//Pull off the strip arg.strip = ext.substring(index + 1, ext.length);strip = strip === "strip";ext = ext.substring(0, index);}//Load the tpl.url = 'nameToUrl' in req ? req.nameToUrl(modName, "." + ext) : req.toUrl(modName + "." + ext);tpl.get(url, function (content) {content = template(content);if(!config.isBuild) {//if(typeof window !== "undefined" && window.navigator && window.document) {content = new Function('obj', content);}content = strip ? tpl.strip(content) : content;if (config.isBuild && config.inlineText) {buildMap[name] = content;}onLoad(content);});},write: function (pluginName, moduleName, write) {if (moduleName in buildMap) {var content = tpl.jsEscape(buildMap[moduleName]);write("define('" + pluginName + "!" + moduleName  +"', function() {return function(obj) { " +content.replace(/(\\')/g, "'").replace(/(\\\\)/g, "\\")+"}});\n");}}};
//>>excludeEnd('excludeTpl')return function() {};});
//>>excludeEnd('excludeTpl')
}());

转载于:https://www.cnblogs.com/fastmover/p/4288583.html

TodoMVC中的Backbone+MarionetteJS+RequireJS例子源码分析之三 Views相关推荐

  1. NameValueCollection类总结和一个例子源码

    1.NameValueCollection类集合是基于 NameObjectCollectionBase 类.但与 NameObjectCollectionBase 不同, 该类在一个键下存储多个字符 ...

  2. android监控电话录音,Android例子源码实现电话录音监听的安卓例子

    技术qq:838341952 本例子是一个基于安卓的通话监听例子源码,可以实现简单的通话录音,下面是简单的实现步骤. 1.首先新建一个SystemService继承Service 2.拿到Teleph ...

  3. 易语言大漠插件破解版注册例子源码

    易语言大漠插件破解版注册例子源码

  4. Android例子源码类似58同城的通过滑屏控制引导页

    Android例子源码类似58同城的通过滑屏控制引导页 运行效果图如下,向右滑动屏幕,小人的小腿在跑呀跑呀! 附源码: csdn下载地址:http://download.csdn.net/detail ...

  5. Linux中mknod命令实现原理以及源码分析

    本篇文章以mknod创建字符设备文件进行讲解 字符设备驱动的Demo例子可参考该篇文章 Linux 编写简单驱动并测试 1. mknod 命令 mknod /dev/hello c 520 0 该命令 ...

  6. java中的==、equals()、hashCode()源码分析(转载)

    在java编程或者面试中经常会遇到 == .equals()的比较.自己看了看源码,结合实际的编程总结一下. 1. ==  java中的==是比较两个对象在JVM中的地址.比较好理解.看下面的代码: ...

  7. Java并发包中Semaphore的工作原理、源码分析及使用示例

    简介: 在多线程程序设计中有三个同步工具需要我们掌握,分别是Semaphore(信号量),countDownLatch(倒计数门闸锁),CyclicBarrier(可重用栅栏) 欢迎探讨,如有错误敬请 ...

  8. Android研发中对String的思考(源码分析)

    1.常用创建方式思考: String text = "this is a test text "; 上面这一句话实际上是执行了三件事  1.声明变量 String text; 2. ...

  9. 【DPDK】dpdk样例源码解析之三:dpdk-l3fwd_001

    本篇文章主要介绍dpdk-l3fwd实例源码,通过分析代码逻辑,学习DPDK中几个API接口作用以及如何使用? 操作系统版本:CentOS 8.4 DPDK版本:dpdk-20.11.3 如何单独创建 ...

最新文章

  1. android mp4宽高,Android:MediaPlayer视频宽高比问题
  2. HoloLens开发手记 - 语音输入 Voice input
  3. 为什么选择格鲁圣教之Go程序版
  4. python读取数据库导出文件_Python 获取 datax 执行结果保存到数据库的方法
  5. leetcode 1838. 最高频元素的频数
  6. php软件开发--php进阶
  7. 解决IntelliJ IDEA控制台乱码问题[包含程序运行时的log4j日志以及tomcat日志乱码]
  8. php结束外部程序,PHP执行外部程序的方法
  9. 天勤数据结构代码——递归
  10. 每日算法(5)——正整数分解质因数
  11. 中华流传十大吉祥图解
  12. Windows快捷键(三)—— 触摸板手势
  13. 嵌入式方向如何转行?
  14. Quartz配置资源介绍
  15. 【20221220】Windows通过网线共享网络及小猫咪局域网共享
  16. POJ 1066 Treasure Hunt 解题报告
  17. SWPUCTF2019web题复现
  18. Java 程序员 5 家大厂的面试总结(已拿Offer)
  19. Unity3D中实现物体内部的碰撞检测(流体在容器内飞溅)
  20. 2.1 reverse

热门文章

  1. 用fuser或者lsof解决无法umount问题(device is busy)
  2. python3 slice
  3. jsoup解析HTML及简单实例
  4. Mounting A Windows Share On AIX Using CIFS
  5. C#多线程|匿名委托传参数|测试您的网站能承受的压力|附源代码
  6. 【原创】构建高性能ASP.NET站点 第五章—性能调优综述(后篇)
  7. linux下设置程序后台运行,linux中如何让进程在后台运行
  8. c语言 一元多项式乘法,[内附完整源码和文档] 基于C语言实现的一元多项式的计算...
  9. 002A demo of the Spectral Co-Clustering algorithm
  10. 常用概率论矩阵论公式