javascript 中的数据驱动页面模式

前段时间一直在想前端MVC的意义。这个话题仁者见仁,但是MVC的使用方法给我提了一个管理数据的有意思的想法--数据管理和数据驱动页面。我们以前的思路一直是事件驱动页面,事件驱动页面合乎逻辑而且节约代码。但是往往代码组织结构非常松散,这个松散并不是大家所期望的松耦合,而是一种乱七八糟的感觉,后来在一次code中,我尝试了一下用数据来驱动页面,觉得效果也不错,逻辑也比较简单。下面简单分享一下我的思路。

  1. 我有一个电子商店,我需要一个购物车功能。
  2. 我希望购物车能在前端处理相关逻辑。而后台只是保存用户订单。

下面是订单保存的数据格式。

var orderList = {
    0:{
        ‘id‘:‘12653‘,
        ‘productName‘:‘Kindle fire‘,
        ‘price‘:790,
        ‘amount‘:2,
        ‘discount‘:0.75
    },
    1:{
        ‘id‘:‘2653‘,
        ‘productName‘:‘iPad‘,
        ‘price‘:2790,
        ‘amount‘:10,
        ‘discount‘:0.70
    },
    2:{
        ‘id‘:‘653‘,
        ‘productName‘:‘Mac‘,
        ‘price‘:7900,
        ‘amount‘:1,
        ‘discount‘:0.95
    },
    length:3,
    subscriberId:‘254‘,
    totalPrice:0
}

首先我们使用一个数据管理器来维护用户的订单数据,我们把它设计为一个单体模式。

var shppingCar = function() {
    var orderList = {}
    this.add = function(obj){
        //添加一条购买数据
    }
    this.remove = function(obj){
        //删除一条购买数据
    }
    this.getTotilPrice = function(obj){
        //获取总价
    }
    this.update = function(obj){
        //更新购买数量
    }
    this.getOrder = function(){
        return orderList;
    }
}

这看起来数据结构清晰,代码组织似乎也不错。接下来涉及到我们DOM部分的操作了。

var order = new shppingCar();
orderList = order.getOrder();
var htmlManager = function(list){
    //用orderList数据渲染页面。
}
//第一次初始化数据
htmlManager();
//添加一条数据
orderList.add({});
orderList = order.getOrder();
htmlManager(orderList);
//删除一条数据
orderList.add(id);
orderList = order.getOrder();
htmlManager(orderList);
//更新一条数据
orderList.update(id);
orderList = order.getOrder();
htmlManager(orderList);

每做一次数据操作,我们都要更新一次数据。我们没有办法改变这个事实,因为事实就是数据改变,我们必然要修改页面。

或许你有更好的办法,那就是不用orderList渲染DOM,而是用一个回调函数来处理。那么代码变为

this.add = function(obj,fn){
    //添加一条购买数据
    if(fn){
        fn();
    }
}
你可以这样使用
orderList.add({},function(){
    //解析一次数据,生成一条DOM结构,插入
    //更改总价
});

这样也意味着你分别要为删除、添加、更新书写不同的回调函数,看起来也并不是一个非常好的办法。

回到前面的代码,我们只需要做一个小小的改变,就可以用数据的改变来驱动我们的页面更新,这也是一个伪观察者模式。其思想就是:数据更新了,我要重新渲染页面。

var shppingCar = function() {
    var orderList = {}

    //我们给shppingCar添加了一个私有方法,当数据改变时自动为我们来更新页面。
    var rander = function(){

    }
    this.add = function(obj){
        //添加一条购买数据
        rander();
    }
    this.remove = function(obj){
        //删除一条购买数据
        rander();
    }
    this.getTotilPrice = function(obj){
        //获取总价
        rander();
    }
    this.update = function(obj){
        //更新购买数量
        rander();
    }
    this.getOrder = function(){
        return orderList;
    }
}

这样我们使用的时候,就可以这样了

var orderList = new shppingCar();
//添加一条数据
orderList.add({});

我们只是把外部渲染函数改成了购物车对象的私有方法,然后在数据变动时调用这个私有方法,就可以省去了在外部每次更新数据都要再次调用一个更新页面的方法。虽然代码量减少的不是很多,但是将所有的内容封装起来外面调用看起来更是省心省力。

至于删除数据和更新数据,我们甚至不需要在外部定义,直接在渲染页面的时候把事件绑定到元素之后即可(下面的示例代码我实现了一个删除绑定,修改商品个数的功能大家有兴趣可以自己实现。)

我不知道这样做是否有意义,希望大家拍。

附测试代码如下。

var shppingCar = function() {
    //我们把数据设计为这样的格式
    var orderList = {
        length:0,
        subscriberId:‘254‘,
        totalPrice:0
    }
    //一些工具方法
    //通过图书id获取当前是第几条数据
    var getItemById = function(id){
        for (var i = 0; i < orderList.length; i++) {
            if(orderList[i].id == id) {
                return i;
            }
        }
    }
    //重新整理数据成为标准格式
    var refreshData = function(){
        var  o = {},n = [];
        for (var key in orderList) {
            var k = Number(key);
            if(!isNaN(k)){
                n.push(orderList[key]);
            }else{
                o[key] = orderList[key];
            }
        }
        for (var i = 0; i < n.length; i++) {
            o[i] = n[i];
        }
        orderList = o;
    }
    //计算总价
    var updateTotilPrice = function() {
        var totalprice = 0;
        for (var i = 0; i < orderList.length; i++) {
            totalprice +=orderList[i].price*orderList[i].discount*orderList[i].amount;
        }
        return totalprice;
    };
    //渲染页面
    var htmlManager = function () {
        var items = "<ul>";
        for (var i=0;i<orderList.length;i++) {
            items += "<li><span>商品编号:"+orderList[i].id
                    +"</span> <span>商品名字:"+orderList[i].productName
                    +"</span> <span>商品价格:"+orderList[i].price
                    +"</span> <span>订购数量:"+orderList[i].amount
                    +"</span> <span>商品折扣:"+orderList[i].discount+"</span>"
                    +"<a data-id="+orderList[i].id+" href=‘###‘>删除</a></li>"
        }
        items += "</ul>";
        items+="商品总价格为"+ orderList.totalPrice +"元";
        document.getElementsByTagName("body")[0].innerHTML = (items);

        //绑定删除事件
        var delBtns = document.getElementsByTagName("a");
        for (var j = 0; j < delBtns.length; j++) {
            (function(k){
                delBtns[k].onclick = function(){
                    remove(delBtns[k].getAttribute(‘data-id‘));
                    return false;
                }
            })(j)
        }
        //绑定修改个数事件
    };
    //删除一条数据
    var remove = function(id){
        var item = getItemById(id);
        delete orderList[item];
        orderList.length-=1;
        refreshData();
        orderList.totalPrice = updateTotilPrice();
        htmlManager();
    }
    //更新商品个数
    var update = function(id,amount){
        //TODO:更新购买数量
        orderList.totalPrice = updateTotilPrice();
        htmlManager();
    }
    //对外俩个接口方法,一个可以添加一条购买数据,一个为获取当前购物车的所有数据
    this.add = function(obj){
        //TODO:验证传入的数据是否合法
        //TODO:此处判断是否已经存在该商品,如果存在,则调用updata方法。
        orderList[orderList.length] = obj;
        if(orderList[orderList.length]){
            orderList.length +=1;
        }
        orderList.totalPrice = updateTotilPrice();
        htmlManager();
    }

    this.getOrder = function() {
        return orderList;
    };
};
//使用方法:
var orderList = new shppingCar();
orderList.add({
    ‘id‘:‘6530‘,
    ‘productName‘:‘Mac mini-0‘,
    ‘price‘:4900,
    ‘amount‘:4,
    ‘discount‘:0.90
})
orderList.add({
    ‘id‘:‘65301‘,
    ‘productName‘:‘Mac mini-1‘,
    ‘price‘:5000,
    ‘amount‘:4,
    ‘discount‘:0.90
})

document.onclick = function() {
    console.log(orderList.getOrder());
};
时间: 2024-08-11 21:08:46

javascript 中的数据驱动页面模式的相关文章

js架构设计模式——理解javascript中的MVVM开发模式

理解javascript中的MVVM开发模式 http://blog.csdn.net/slalx/article/details/7856769 MVVM的全称是Model View ViewModel,这种架构模式最初是由微软的MartinFowler作为微软软件的展现层设计模式的规范提出,它是MVC模式的衍生物,MVVM模式的关注点在能够支持事件驱动的UI开发平台,例如HTML5,[2][3] WindowsPresentation Foundation (WPF), Silverligh

转 。。理解javascript中的MVVM开发模式

MVVM的全称是Model View ViewModel,这种架构模式最初是由微软的MartinFowler作为微软软件的展现层设计模式的规范提出,它是MVC模式的衍生物,MVVM模式的关注点在能够支持事件驱动的UI开发平台,例如HTML5,[2][3] WindowsPresentation Foundation (WPF), Silverlight 和 t ZK framework,Adobe Flex. 对这种模式的实现,大部分都是通过在view层声明数据绑定来和其他层分离的,这样就方便了

Javascript中理解发布--订阅模式

阅读目录 发布订阅模式介绍 如何实现发布--订阅模式? 发布---订阅模式的代码封装 如何取消订阅事件? 全局--发布订阅对象代码封装 理解模块间通信 回到顶部 发布订阅模式介绍 发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知. 现实生活中的发布-订阅模式: 比如小红最近在淘宝网上看上一双鞋子,但是呢 联系到卖家后,才发现这双鞋卖光了,但是小红对这双鞋又非常喜欢,所以呢联系卖家,问卖

Node中EventEmitter以及如何实现JavaScript中的订阅/发布模式

1.EventEmitter Node中很多模块都能够使用EventEmitter,有了EventEmitter才能方便的进行事件的监听.下面看一下Node.js中的EventEmitter如何使用. (1)基本使用 EventEmitter是对事件触发和事件监听功能的封装,在node.js中的event模块中,event模块只有一个对象就是EventEmitter,下面是一个最基本的使用方法: var EventEmitter = require('events').EventEmitter;

typeof操作符在javascript中运用时时页面上的操作数显示

typeof可以告诉我们它的操作数是一个字符串(string).数值(number).函数(function).布尔值(boolean)或对象(object). 1.字符串(string) alert(typeof("asss")).alert(typeof("123"))等. 此时警告框显示的是string. 注:字符串需要加双引号. 2.数值(number) alert(typeof(123)). 此时警告框显示的是number. 注:typeof中只能含数字,

在JavaScript中实现异步编程模式的方法

本文总结了”异步模式”编程的4种方法,理解它们可以让你写出结构更合理.性能更出色.维护更方便的Javascript程序. 一.回调函数 这是异步编程最基本的方法. 假定有两个函数f1和f2,后者等待前者的执行结果. f1(); f2(); 如果f1是一个很耗时的任务,可以考虑改写f1,把f2写成f1的回调函数. function f1(callback){ setTimeout(function () { // f1的任务代码 callback(); }, 1000); } 执行代码就变成下面这

【干货理解】理解javascript中实现MVC的原理

理解javascript中的MVC MVC模式是软件工程中一种软件架构模式,一般把软件模式分为三部分,模型(Model)+视图(View)+控制器(Controller); 模型:模型用于封装与应用程序的业务逻辑相关的数据以及对数据处理的方法.模型有对数据直接访问的权利.模型不依赖 "视图" 和 "控制器", 也就是说 模型它不关心页面如何显示及如何被操作. 视图:视图层最主要的是监听模型层上的数据改变,并且实时的更新html页面.当然也包括一些事件的注册或者aja

JavaScript 中的继承

继承是 OO 语言中的一个最为人津津乐道的概念.许多 OO 语言都支持两种继承方式:接口继承和实现继承.接口继承只继承方法签名,而实现继承则继承实际的方法.由于函数没有签名,在 ECMAScript 中无法实现接口继承. ECMAScript 只支持实现继承,而且其实现继承主要是依靠原型链来实现的. 组合继承时 JavaScript 中常用的继承模式,而寄生组合式继承克服了组合继承的缺点,被认为是引用类型最理想的继承范式. 以下是<JavaScript高级程序设计>中介绍的继承方式:

this详解:JAVASCRIPT中的this到底是谁?

语法 this 全局对象 在全局执行上下文(函数之外),this引用的是全局对象. console.log(this.document === document); // true // In web browsers, the window object is also the global object: console.log(this === window); // true this.a = 37; console.log(window.a); // 37 函数 在函数里面,this这