博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Backbone实例todos分析
阅读量:5362 次
发布时间:2019-06-15

本文共 20227 字,大约阅读时间需要 67 分钟。

源码来自:http://todomvc.com/examples/backbone/

这是一个用Backbone.js完成的待办事项实例,精简但完善,可以帮助很好的帮助理解Backbone的API,MVC框架和写webApp的基本思路。

下面让我们一步步分析,

准备工作:

首先是html页面,新建index.html,并保存以下代码:

            
Backbone.js • TodoMVC
    View Code

    这是webapp的主页面,它引入的文件有样式表base.css;js框架jquery.js,underscore.js,backbone.js,backbone.localStorage.js,请大家自行引入。然后是即将利用backbone编写的js代码.为了方便理解,原作者将这些JS代码分成了5个JS文件,我们将深入分析。

    先简单介绍一下这些js框架和我们页面的关系:

    1.jquery.js:我们将利用jquery操作dom,

    2.underscore.js:backbone的依赖库,提供一下方便的方法,和模板的操作。

    3.backbone.js:我们主要使用的MVC框架

    4.backbone.localStorage.js:backbone的拓展库,用来操作HTML5新加入的本地存储

    页面的样式表base.css

    html,body {
    margin: 0; padding: 0;}button {
    margin: 0; padding: 0; border: 0; background: none; font-size: 100%; vertical-align: baseline; font-family: inherit; color: inherit; -webkit-appearance: none; -ms-appearance: none; -o-appearance: none; appearance: none;}body {
    font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; line-height: 1.4em; background: #eaeaea url('bg.png'); color: #4d4d4d; width: 550px; margin: 0 auto; -webkit-font-smoothing: antialiased; -moz-font-smoothing: antialiased; -ms-font-smoothing: antialiased; -o-font-smoothing: antialiased; font-smoothing: antialiased;}button,input[type="checkbox"] {
    outline: none;}#todoapp {
    background: #fff; background: rgba(255, 255, 255, 0.9); margin: 130px 0 40px 0; border: 1px solid #ccc; position: relative; border-top-left-radius: 2px; border-top-right-radius: 2px; box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.15);}#todoapp:before {
    content: ''; border-left: 1px solid #f5d6d6; border-right: 1px solid #f5d6d6; width: 2px; position: absolute; top: 0; left: 40px; height: 100%;}#todoapp input::-webkit-input-placeholder {
    font-style: italic;}#todoapp input::-moz-placeholder {
    font-style: italic; color: #a9a9a9;}#todoapp h1 {
    position: absolute; top: -120px; width: 100%; font-size: 70px; font-weight: bold; text-align: center; color: #b3b3b3; color: rgba(255, 255, 255, 0.3); text-shadow: -1px -1px rgba(0, 0, 0, 0.2); -webkit-text-rendering: optimizeLegibility; -moz-text-rendering: optimizeLegibility; -ms-text-rendering: optimizeLegibility; -o-text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;}#header {
    padding-top: 15px; border-radius: inherit;}#header:before {
    content: ''; position: absolute; top: 0; right: 0; left: 0; height: 15px; z-index: 2; border-bottom: 1px solid #6c615c; background: #8d7d77; background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8))); background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670'); border-top-left-radius: 1px; border-top-right-radius: 1px;}#new-todo,.edit {
    position: relative; margin: 0; width: 100%; font-size: 24px; font-family: inherit; line-height: 1.4em; border: 0; outline: none; color: inherit; padding: 6px; border: 1px solid #999; box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); -moz-box-sizing: border-box; -ms-box-sizing: border-box; -o-box-sizing: border-box; box-sizing: border-box; -webkit-font-smoothing: antialiased; -moz-font-smoothing: antialiased; -ms-font-smoothing: antialiased; -o-font-smoothing: antialiased; font-smoothing: antialiased;}#new-todo {
    padding: 16px 16px 16px 60px; border: none; background: rgba(0, 0, 0, 0.02); z-index: 2; box-shadow: none;}#main {
    position: relative; z-index: 2; border-top: 1px dotted #adadad;}label[for='toggle-all'] {
    display: none;}#toggle-all {
    position: absolute; top: -42px; left: -4px; width: 40px; text-align: center; /* Mobile Safari */ border: none;}#toggle-all:before {
    content: '»'; font-size: 28px; color: #d9d9d9; padding: 0 25px 7px;}#toggle-all:checked:before {
    color: #737373;}#todo-list {
    margin: 0; padding: 0; list-style: none;}#todo-list li {
    position: relative; font-size: 24px; border-bottom: 1px dotted #ccc;}#todo-list li:last-child {
    border-bottom: none;}#todo-list li.editing {
    border-bottom: none; padding: 0;}#todo-list li.editing .edit {
    display: block; width: 506px; padding: 13px 17px 12px 17px; margin: 0 0 0 43px;}#todo-list li.editing .view {
    display: none;}#todo-list li .toggle {
    text-align: center; width: 40px; /* auto, since non-WebKit browsers doesn't support input styling */ height: auto; position: absolute; top: 0; bottom: 0; margin: auto 0; /* Mobile Safari */ border: none; -webkit-appearance: none; -ms-appearance: none; -o-appearance: none; appearance: none;}#todo-list li .toggle:after {
    content: '✔'; /* 40 + a couple of pixels visual adjustment */ line-height: 43px; font-size: 20px; color: #d9d9d9; text-shadow: 0 -1px 0 #bfbfbf;}#todo-list li .toggle:checked:after {
    color: #85ada7; text-shadow: 0 1px 0 #669991; bottom: 1px; position: relative;}#todo-list li label {
    white-space: pre; word-break: break-word; padding: 15px 60px 15px 15px; margin-left: 45px; display: block; line-height: 1.2; -webkit-transition: color 0.4s; transition: color 0.4s;}#todo-list li.completed label {
    color: #a9a9a9; text-decoration: line-through;}#todo-list li .destroy {
    display: none; position: absolute; top: 0; right: 10px; bottom: 0; width: 40px; height: 40px; margin: auto 0; font-size: 22px; color: #a88a8a; -webkit-transition: all 0.2s; transition: all 0.2s;}#todo-list li .destroy:hover {
    text-shadow: 0 0 1px #000, 0 0 10px rgba(199, 107, 107, 0.8); -webkit-transform: scale(1.3); transform: scale(1.3);}#todo-list li .destroy:after {
    content: '✖';}#todo-list li:hover .destroy {
    display: block;}#todo-list li .edit {
    display: none;}#todo-list li.editing:last-child {
    margin-bottom: -1px;}#footer {
    color: #777; padding: 0 15px; position: absolute; right: 0; bottom: -31px; left: 0; height: 20px; z-index: 1; text-align: center;}#footer:before {
    content: ''; position: absolute; right: 0; bottom: 31px; left: 0; height: 50px; z-index: -1; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), 0 6px 0 -3px rgba(255, 255, 255, 0.8), 0 7px 1px -3px rgba(0, 0, 0, 0.3), 0 43px 0 -6px rgba(255, 255, 255, 0.8), 0 44px 2px -6px rgba(0, 0, 0, 0.2);}#todo-count {
    float: left; text-align: left;}#filters {
    margin: 0; padding: 0; list-style: none; position: absolute; right: 0; left: 0;}#filters li {
    display: inline;}#filters li a {
    color: #83756f; margin: 2px; text-decoration: none;}#filters li a.selected {
    font-weight: bold;}#clear-completed {
    float: right; position: relative; line-height: 20px; text-decoration: none; background: rgba(0, 0, 0, 0.1); font-size: 11px; padding: 0 10px; border-radius: 3px; box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2);}#clear-completed:hover {
    background: rgba(0, 0, 0, 0.15); box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3);}#info {
    margin: 65px auto 0; color: #a6a6a6; font-size: 12px; text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); text-align: center;}#info a {
    color: inherit;}/* Hack to remove background from Mobile Safari. Can't use it globally since it destroys checkboxes in Firefox and Opera*/@media screen and (-webkit-min-device-pixel-ratio:0) {
    #toggle-all, #todo-list li .toggle { background: none; } #todo-list li .toggle {
    height: 40px; } #toggle-all {
    top: -56px; left: -15px; width: 65px; height: 41px; -webkit-transform: rotate(90deg); transform: rotate(90deg); -webkit-appearance: none; appearance: none; }}.hidden {
    display: none;}hr {
    margin: 20px 0; border: 0; border-top: 1px dashed #C5C5C5; border-bottom: 1px dashed #F7F7F7;}.learn a {
    font-weight: normal; text-decoration: none; color: #b83f45;}.learn a:hover {
    text-decoration: underline; color: #787e7e;}.learn h3,.learn h4,.learn h5 {
    margin: 10px 0; font-weight: 500; line-height: 1.2; color: #000;}.learn h3 {
    font-size: 24px;}.learn h4 {
    font-size: 18px;}.learn h5 {
    margin-bottom: 0; font-size: 14px;}.learn ul {
    padding: 0; margin: 0 0 30px 25px;}.learn li {
    line-height: 20px;}.learn p {
    font-size: 15px; font-weight: 300; line-height: 1.3; margin-top: 0; margin-bottom: 0;}.quote {
    border: none; margin: 20px 0 60px 0;}.quote p {
    font-style: italic;}.quote p:before {
    content: '“'; font-size: 50px; opacity: .15; position: absolute; top: -20px; left: 3px;}.quote p:after {
    content: '”'; font-size: 50px; opacity: .15; position: absolute; bottom: -42px; right: 3px;}.quote footer {
    position: absolute; bottom: -40px; right: 0;}.quote footer img {
    border-radius: 3px;}.quote footer a {
    margin-left: 5px; vertical-align: middle;}.speech-bubble {
    position: relative; padding: 10px; background: rgba(0, 0, 0, .04); border-radius: 5px;}.speech-bubble:after {
    content: ''; position: absolute; top: 100%; right: 30px; border: 13px solid transparent; border-top-color: rgba(0, 0, 0, .04);}.learn-bar > .learn {
    position: absolute; width: 272px; top: 8px; left: -300px; padding: 10px; border-radius: 5px; background-color: rgba(255, 255, 255, .6); -webkit-transition-property: left; transition-property: left; -webkit-transition-duration: 500ms; transition-duration: 500ms;}@media (min-width: 899px) {
    .learn-bar { width: auto; margin: 0 0 0 300px; } .learn-bar > .learn {
    left: 8px; } .learn-bar #todoapp {
    width: 550px; margin: 130px auto 40px auto; }}
    View Code

    代码分析:

    todo.js

    //app对象var app = app || {};(function () {    'use strict';    // Todo 模型    // ----------        //我们基本的Todo模型有`title`,`order`,`completed`,属性    app.Todo = Backbone.Model.extend({        //todo默认属性,确保每个对象都拥有`title` and `completed`.        defaults: {            title: '',            completed: false        },        //改变todo中`completed`属性为反向状态         toggle: function () {            this.save({                completed: !this.get('completed')            });        }    });})();

    在todo app我们实现要实现的功能中,一个基本待办事项就是最基本的数据模型,它拥有title,completed,order,三个属性,分别表示显示的标题,是否完成和顺序,其中title,completed为默认属性。

    模型有一个改变状态的方法。save方法除了将数据保存到模型中,还将向服务器发送request请求,如果成功,触发sync事件。简言之,与数据存储方面配合,利用save不单是将数据存储到我们定义的模型里,还将保存到服务器上。

    todos.js

    //app对象var app = app || {};(function () {    'use strict';    // Todo 集合    // ---------------    // 这里数据集合将存储到本地存储,替代服务器存储    var Todos = Backbone.Collection.extend({        //引用这个集合的模型         model: app.Todo,        // 集合中所有的todo 模型都将存在本地        localStorage: new Backbone.LocalStorage('todos-backbone'),        // 过滤为所有完成的todo模型        completed: function () {            return this.where({completed: true});        },        // 过滤为所有为完成的todo模型        remaining: function () {            return this.where({completed: false});        },        // 我们将保持Todos有序,不管是不是被无序存储        // 生成下个todo模型的顺序ID        nextOrder: function () {            return this.length ? this.last().get('order') + 1 : 1;        },        // 所有的todo将以最初插入的顺序保存到Todos当中。        comparator: 'order'    });    // 创建一个todos集合。    app.todos = new Todos();})();

    这是数据模型的集合。

    todo-view.js

    var app = app || {};(function ($) {    'use strict';    // todo 单个模型的视图    // --------------    // 一个todo模型的DOM元素    app.TodoView = Backbone.View.extend({        // 整个视图在一个li标签中        tagName:  'li',        // 缓存单个模型的模板函数        template: _.template($('#item-template').html()),        // 定义单个模型页面视图中指定DOM元素触发事件时调用的函数        events: {            'click .toggle': 'toggleCompleted',            'dblclick label': 'edit',            'click .destroy': 'clear',            'keypress .edit': 'updateOnEnter',            'keydown .edit': 'revertOnEscape',            'blur .edit': 'close'        },        // 监听其对应模型的事件,这是一对一的,这个视图,监听其模型。        initialize: function () {            this.listenTo(this.model, 'change', this.render);            this.listenTo(this.model, 'destroy', this.remove);            this.listenTo(this.model, 'visible', this.toggleVisible);        },        // 生成模型的标题。        render: function () {            //Backbone LocalStorage保存模型时,会增加一个ID属性,这将引起render事件再次发生。我们想过滤由此引起的第二次render,因此增加了一段功能。            // 这是localStorage.js的一个BUG,相关信息可以查看 https://github.com/tastejs/todomvc/issues/469            if (this.model.changed.id !== undefined) {                return;            }            this.$el.html(this.template(this.model.toJSON()));            this.$el.toggleClass('completed', this.model.get('completed'));            this.toggleVisible();            this.$input = this.$('.edit');            return this;        },        toggleVisible: function () {            this.$el.toggleClass('hidden', this.isHidden());        },        isHidden: function () {            return this.model.get('completed') ?                app.TodoFilter === 'active' :                app.TodoFilter === 'completed';        },        // 改变模型completed状态        toggleCompleted: function () {            this.model.toggle();        },        // 转到编辑状态        edit: function () {            this.$el.addClass('editing');            this.$input.focus();        },        // 关闭编辑模式,存储对模型的改变        close: function () {            var value = this.$input.val();            var trimmedValue = value.trim();            // 在非编辑状态下直接返回            if (!this.$el.hasClass('editing')) {                return;            }            if (trimmedValue) {                this.model.save({ title: trimmedValue });                if (value !== trimmedValue) {                    // 只增加了两边的空格的更改并不会存入服务器中,在此情况下,我们重新渲染视图,                    // 达到显示和后台统一的目的。                    this.model.trigger('change');                }            } else {                this.clear();            }            this.$el.removeClass('editing');        },        // 如果你按下回车键,将跳到编辑状态        updateOnEnter: function (e) {            if (e.which === ENTER_KEY) {                this.close();            }        },        // 如果你按下esc键,将恢复视图为模型的数据,并离开编辑状态        revertOnEscape: function (e) {            if (e.which === ESC_KEY) {                this.$el.removeClass('editing');                // 重置输入框为模型中的数据                this.$input.val(this.model.get('title'));            }        },        // 删除条目,并从localStorage中销毁,并删除视图(事件监听destroy)        clear: function () {            this.model.destroy();        }    });})(jQuery);

     这里是单个条目的视图,它主要负责对单个条目的操作,如删除,更改等每个条目都有的功能。

    app-view.js

    var app = app || {};(function ($) {    'use strict';    // 程序    // ---------------    // 整体的程序视图    app.AppView = Backbone.View.extend({        // 视图绑定为已经存在的页面节点        el: '#todoapp',        // 缓存状态模板        statsTemplate: _.template($('#stats-template').html()),        events: {            'keypress #new-todo': 'createOnEnter',            'click #clear-completed': 'clearCompleted',            'click #toggle-all': 'toggleAllComplete'        },        // 在初始化时,我们绑定相关的事件到集合实例上,        // 重置时,重新添加所有模型。        initialize: function () {            this.allCheckbox = this.$('#toggle-all')[0];            this.$input = this.$('#new-todo');            this.$footer = this.$('#footer');            this.$main = this.$('#main');            this.$list = $('#todo-list');            this.listenTo(app.todos, 'add', this.addOne);            this.listenTo(app.todos, 'reset', this.addAll);            this.listenTo(app.todos, 'change:completed', this.filterOne);            this.listenTo(app.todos, 'filter', this.filterAll);            this.listenTo(app.todos, 'all', this.render);            // 抑制由于每个add事件引起的视图生成。从本地存储获取到模型时,触发reset事件,添加所有的模型到集合中。            app.todos.fetch({reset: true});        },        // reset并不会引起视图的生成。        render: function () {            var completed = app.todos.completed().length;            var remaining = app.todos.remaining().length;            if (app.todos.length) {                this.$main.show();                this.$footer.show();                this.$footer.html(this.statsTemplate({                    completed: completed,                    remaining: remaining                }));                this.$('#filters li a')                    .removeClass('selected')                    .filter('[href="#/' + (app.TodoFilter || '') + '"]')                    .addClass('selected');            } else {                this.$main.hide();                this.$footer.hide();            }            this.allCheckbox.checked = !remaining;        },        // 为新增的模型创建视图,添加到页面中去        addOne: function (todo) {            var view = new app.TodoView({ model: todo });            this.$list.append(view.render().el);        },        // 一次添加所有模型视图        addAll: function () {            this.$list.html('');            app.todos.each(this.addOne, this);        },        filterOne: function (todo) {            todo.trigger('visible');        },        filterAll: function () {            app.todos.each(this.filterOne, this);        },        // 生成新模型的属性        newAttributes: function () {            return {                title: this.$input.val().trim(),                order: app.todos.nextOrder(),                completed: false            };        },        // 回车键保存模型,且保存到localStorage        createOnEnter: function (e) {            if (e.which === ENTER_KEY && this.$input.val().trim()) {                app.todos.create(this.newAttributes());                this.$input.val('');            }        },        // 清除所有完成的模型,        clearCompleted: function () {            _.invoke(app.todos.completed(), 'destroy');            return false;        },        toggleAllComplete: function () {            var completed = this.allCheckbox.checked;            app.todos.each(function (todo) {                todo.save({                    completed: completed                });            });        }    });})(jQuery);

    主视图,主程序,前面所有的模型,集合,视图,都由他来管理。渲染整个视图,监听新增事件,添加条目到集合中等全面的问题处理。

    route.js

    var app = app || {};(function () {    'use strict';    // Todo 路由    // ----------    var TodoRouter = Backbone.Router.extend({        routes: {            '*filter': 'setFilter'        },        setFilter: function (param) {            // 存储目前的过滤参数。            app.TodoFilter = param || '';            // 触发过滤事件,改变视图的隐藏显示。            app.todos.trigger('filter');        }    });    app.TodoRouter = new TodoRouter();    Backbone.history.start();})();

    利用route,保存参数到变量中,供程序使用

    app.js

    var app = app || {};var ENTER_KEY = 13;var ESC_KEY = 27;$(function () {    'use strict';    // 创建App实例,初始化所有功能    new app.AppView();});

    初始化真个程序。

    转载于:https://www.cnblogs.com/winderby/p/4111425.html

    你可能感兴趣的文章
    ajax如何向后台传递数组,在后台该如何接收的问题(项目积累)
    查看>>
    Solr之java实现增删查操作
    查看>>
    httpClient连接工具类实测可用
    查看>>
    CDOJ 1965 连通域统计【DFS】
    查看>>
    飞机大战3-我的飞机
    查看>>
    c#接口
    查看>>
    MyEclipse部署Jboss出现java.lang.OutOfMemoryError: PermGen space
    查看>>
    ZOJ 1133
    查看>>
    HIVE和HADOOP的一些东西
    查看>>
    alibaba / zeus 安装 图解
    查看>>
    Planned Delivery Time as Work Days (SCN discussion)
    查看>>
    Ubuntu:让桌面显示回收站
    查看>>
    Android上传头像代码,相机,相册,裁剪
    查看>>
    git 安装体验
    查看>>
    Oracle 给已创建的表增加自增长列
    查看>>
    《DSP using MATLAB》Problem 2.17
    查看>>
    if 循环
    查看>>
    uva 111 History Grading(lcs)
    查看>>
    Python学习week2-python介绍与pyenv安装
    查看>>
    php判断网页是否gzip压缩
    查看>>