zoukankan      html  css  js  c++  java
  • JS案例:用购物车理解前端MVC架构

    目录

    什么是MVC:

    MVC的作用:

    如何使用MVC架构:

    效果:

    以下是所有代码:

    后端(nodejs):

    server.js

    data.js(存放商品列表)

    前端

    shopCar.html(入口页面)

    shop.css

    JS文件夹:

    bussiness

    command

    components

    config

    controller

    event

    model

    utils

    view


    什么是MVC:

    Model View Controller即:模型-视图-控制器
    通俗来讲,在编程语言中,Model就是数据,可以理解为数据库,View就是显示数据的外观,Controller是用来连接前两者的行为,常见的Vue采用的是M-V-VM架构,与MVC类似,但是基于MVC

    MVC的作用:

    说到作用,就不得不提面向对象与面向过程的区别了

    面向过程就是,将解决问题的思路流程一步一步进行,紧扣在一起,最终达到结果

    面向对象,是将某个问题的解决方式剥离开,其目的不是为了完成某个步骤,而是将某个事物(对象)的角色(属性)和行为(方法)作为核心

    说了这些,到底MVC有什么好处呢?

    举个栗子:A是某公司的一位前端程序员,平时用面向过程进行编程,这天,好不容易完成了手头上的活,准备回家,这时,产品经理走过来,让他改个小地方,这下就完了,面向过程的思维使他的代码环环相扣,代码耦合性强,内聚性高,密不可分,改一个地方就要几乎全改

    A的哥哥也是一个前端程序员,平时用面向对象编程,产品经理让他改一个效果,由于用的面向对象,他的代码没有层次感,通用的方法全部提取出来,使得代码耦合性低,想改哪直接改相关的类或者方法就好了

    当然,在小型项目中无法体现它的优点,甚至会小题大做,大材小用,而在大型项目中,其耦合性低,代码复用性高,搭建相对较快

    如何使用MVC架构:

    又是这个购物车,业余时间用MVC做了一个简单的购物车:

    目录结构大致是这样

    购物车整体流程:

            目录结构将model view controller剥离开

            Modedl层:存储数据,显示数据

            View:根据Model数据渲染页面

            Controller:传递数据

            Command:操作数据,获取数据

            Event:事件总线,注册事件

            商品列表:

                初始化View层,建立Ajax获取数据,之后由controller触发事件至事件总线,然后再由注册的事件将ajax数据传至Model中完成商品列表初始化

                当model获取到商品列表数据时,通过代理set()  触发新建商品列表事件,通过command操作view达到新建列表目的

            购物车表格:

                当用户对view进行操作时,触发注册的事件,通过command修改Model中的数据(购物车列表)从而再由command驱动view中的刷新表格进行渲染

    效果:

    以下是所有代码:

    后端(nodejs):

    server.js

    /*
     *后端采用node+express搭建一个简单的接口,通过本地数据,将商品列表传至前端
     * 
     */
    const express = require('express');
    const path = require('path');
    const app = express();
    const shopData = require('./data/shopData.js')
    let serverToken = 'hello'
    app.all("*", function (req, res, next) { //跨域
        res.header("Access-Control-Allow-Origin", "*");
        res.header("Access-Control-Allow-Headers", "content-type");
        res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS");
        next();
    });
    app.use('/getShopList', function (req, res) {
        let data = req.query
        if (!checkToken(data.token)) { //简单获取前端token,校验
            res.send({
                result: 0,
                msg: 'token fail'
            })
            return
        }
        res.send({
            result: 1,
            msg: 'success',
            type: 'getShopList',
            shopData
        })
    })
    
    function checkToken(teken) {
        return teken == serverToken
    }
    app.use('/img', express.static(path.join(__dirname, './img'))); //后端目录静态化,用url+img访问文件夹
    app.use('/client', express.static(path.join(__dirname, '../client')));
    app.listen(1024, "127.0.0.1", function () {
        console.log("服务开启,开始侦听");
    });

    data.js(存放商品列表)

    module.exports = [{
            "select": false,
            "id": 1001,
            "icon": "img/1.png",
            "name": "餐饮0",
            "num": 0,
            "price": 10,
            "sum": 0,
            "delete": false
        },
        {
            "select": false,
            "id": 1002,
            "icon": "img/2.png",
            "name": "餐饮1",
            "num": 0,
            "price": 20,
            "sum": 0,
            "delete": false
        },
        {
            "select": false,
            "id": 1003,
            "icon": "img/3.png",
            "name": "餐饮2",
            "num": 0,
            "price": 30,
            "sum": 0,
            "delete": false
        },
        {
            "select": false,
            "id": 1004,
            "icon": "img/4.png",
            "name": "餐饮3",
            "num": 0,
            "price": 40,
            "sum": 0,
            "delete": false
        },
        {
            "select": false,
            "id": 1005,
            "icon": "img/5.png",
            "name": "餐饮4",
            "num": 0,
            "price": 50,
            "sum": 0,
            "delete": false
        },
        {
            "select": false,
            "id": 1006,
            "icon": "img/6.png",
            "name": "餐饮5",
            "num": 0,
            "price": 60,
            "sum": 0,
            "delete": false
        },
        {
            "select": false,
            "id": 1007,
            "icon": "img/7.png",
            "name": "餐饮6",
            "num": 0,
            "price": 70,
            "sum": 0,
            "delete": false
        },
        {
            "select": false,
            "id": 1008,
            "icon": "img/8.png",
            "name": "餐饮7",
            "num": 0,
            "price": 80,
            "sum": 0,
            "delete": false
        },
        {
            "select": false,
            "id": 1009,
            "icon": "img/9.png",
            "name": "餐饮8",
            "num": 0,
            "price": 90,
            "sum": 0,
            "delete": false
        },
        {
            "select": false,
            "id": 1010,
            "icon": "img/10.png",
            "name": "餐饮9",
            "num": 0,
            "price": 100,
            "sum": 0,
            "delete": false
        }
    ]

    前端

    shopCar.html(入口页面)

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
    	<meta charset="UTF-8">
    	<meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>shopCar</title>
    	<link rel="stylesheet" href="./src/style/shop.css">
    </head>
    
    <body>
    	<script type="module">
    		/*
    		购物车整体流程:
    		目录结构将model view controller剥离开
    		Modedl层:存储数据,显示数据
    		View:根据Model数据渲染页面
    		Controller:传递数据
    		Command:操作数据,获取数据
    		Event:事件总线,注册事件
    		商品列表:
    		 	初始化View层,建立Ajax获取数据,之后由controller触发事件至事件总线,然后再由注册的事件将ajax数据传至Model中完成商品列表初始化
    		 	当model获取到商品列表数据时,通过代理set()  触发新建商品列表事件,通过command操作view达到新建列表目的
    		购物车表格:
    		 	当用户对view进行操作时,触发注册的事件,通过command修改Model中的数据(购物车列表)从而再由command驱动view中的刷新表格进行渲染
    		*/
    		import ShopView from './src/js/view/ShopView.js'
    		// 实例化View层入口函数
    		new ShopView()
    	</script>
    </body>
    
    </html>

    shop.css

    * {
        margin: 0;
        padding: 0;
    }
    
    .shopBox {
        overflow: hidden;
         1000px;
        margin: 50px auto 0;
    }
    
    .liItem {
        float: left;
        list-style: none;
        padding: 10px;
         150px;
        height: 200px;
        text-align: center;
        border: 1px solid lightcoral;
    }
    
    .liItem img {
         100px;
        height: 100px;
    }
    
    .leftBtn,
    .rightBtn {
         30px;
        height: 30px;
        background: white;
        border: 1px solid black;
        font-size: 25px;
        line-height: 30px;
    }
    
    .text {
         50px;
        height: 26px;
        display: inline-block;
        vertical-align: bottom;
        text-align: center;
    }
    
    table {
        font-size: 30px;
         1200px;
        border: 1px solid lightcoral;
        border-collapse: collapse;
        margin: 50px auto;
    }
    
    .checkbox {
         30px;
        height: 30px;
    }
    
    td {
        border: 1px solid lightcoral;
        text-align: center;
        vertical-align: middle;
    }
    
    td button {
         150px;
        height: 60px;
    }
    
    .numBox {
         150px;
        height: 30px;
        margin: auto;
        position: relative;
    }
    
    .numBox>button {
         40px;
        height: 42px;
        background-color: white;
        border: 1px solid #000000;
    }
    
    .numBox>input {
         70px;
        height: 40px;
        border: 1px solid #000000;
        border-left: none;
        border-right: none;
        text-align: center;
    }

    JS文件夹:

    bussiness

    • Ajax.js
      import ShopEvent from '../event/ShopEvent.js'
      import Utils from '../utils/Utils.js'
      import Api from '../config/Api.js'
      import ShopController from '../controller/ShopController.js'
      export default class Ajax {//Ajax类,用于请求后端或本地数据
          // Ajax请求函数
          static AjaxTool(method = Api.GET, url, data) {
              let xhr;
              if (window.ActiveXObject) { //ie浏览器
                  xhr = new ActiveXObject("Microsoft.XMLHTTP");
              } else if (window.XMLHttpRequest) { //其他浏览器
                  xhr = new XMLHttpRequest();
              }
              url = Api.URL + Api.PORT + Api.PATH + url
              if (method !== Api.POST) {
                  method = Api.GET
                  url = Utils.urlJoin(url, data)
                  data = null
              } else {
                  method = Api.POST
              }
              xhr.open(method, url);
              xhr.send(data ? JSON.stringify(data) : '')
              xhr.addEventListener('load', Ajax.loadHandler) //Ajax类是静态类,无法使用this
          }
          static loadHandler(e) {
              //this指向xhr
              let xhr = e.currentTarget;
              if (xhr.readyState === 4 && xhr.status === 200) {
                  Ajax.data = xhr.response
              } else {
                  Ajax.data = 'error'
              }
      
          }
          static set data(value) { //使用set对象代理模式替代请求数据回调函数(只写set表示data只可写入,不可读取)
              let res = JSON.parse(value)
              switch (res.result) {
                  case 1:
                      console.log(res.msg)
                      ShopController.dispatch(ShopEvent.GET_DATA, res)//获取到数据后不做其他操作,将数据通过事件抛出至Event总线中
                      break;
                  case 0:
                      console.log('加载失败')
                      console.log(res.msg)
                      break;
                  default:
                      break;
              }
          }
      }

    command

    • MainCommand(command汇总)
      import GetDataCommand from '../command/GetDataCommand.js'
      import CreateListCommand from '../command/CreateListCommand.js'
      import CreateTableCommand from '../command/CreateTableCommand.js'
      import AddItemCommand from '../command/AddItemCommand.js'
      import DelItemCommand from '../command/DelItemCommand.js'
      import ReduceItemCommand from '../command/ReduceItemCommand.js'
      import ChangeItemCommand from '../command/ChangeItemCommand.js'
      import SelectItemCommand from '../command/SelectItemCommand.js'
      export default {
          GetDataCommand,
          CreateListCommand,
          CreateTableCommand,
          AddItemCommand,
          DelItemCommand,
          ReduceItemCommand,
          ChangeItemCommand,
          SelectItemCommand
      }
    • GetDataCommand(获取商品列表)
      import ShopModel from '../model/ShopModel.js'
      export default class GetDataCommand { //行为类,用于执行ctrl层通过事件的方式发来的指令
          constructor() {
      
          }
          eventHandler(e) { //使用动态方法,而不是static静态方法,因为该方法将被使用多次,使用new AddShopCommand实例化后调用
              let {
                  data
              } = e
              ShopModel.getInstance().shopList = data.shopData//将ajax获取的数据发送到Model
          }
      }
    • CreateListCommand(创建商品列表)
      import CreateList from '../view/CreateList.js'
      export default class CreateListCommand { //行为类,用于执行ctrl层通过事件的方式发来的指令
          constructor() {//创建商品列表
      
          }
          eventHandler(e) { //使用动态方法,而不是static静态方法,因为该方法将被使用多次,使用new AddShopCommand实例化后调用
              let {
                  data
              } = e
              for (let i = 0; i < data.length; i++) {
                  let createList = new CreateList(document.body)
                  createList.shopList = data[i]
              }
          }
      }
    • CreateTableCommand(创建购物车表格)
      import CreateTable from '../view/CreateTable.js'
      export default class ShopCommand { //行为类,用于执行ctrl层通过事件的方式发来的指令
          constructor() {//刷新购物车表格
      
          }
          eventHandler(e) { //使用动态方法,而不是static静态方法,因为该方法将被使用多次,使用new AddShopCommand实例化后调用
              let {
                  data
              } = e
              let createTable = new CreateTable(document.body)
              createTable.shoppingList = data
          }
      }
    • AddItemCommand(增加商品)
      import ShopModel from '../model/ShopModel.js'
      export default class AddItemCommand { //行为类,用于执行ctrl层通过事件的方式发来的指令
          constructor() { //新增商品
      
          }
          eventHandler(e) { //使用动态方法,而不是static静态方法,因为该方法将被使用多次,使用new AddShopCommand实例化后调用
              let {
                  data
              } = e
              AddItemCommand.addItem(ShopModel.getInstance().shoppingList, data)
          }
          static addItem(list, data) { //遍历查询某项商品增加或减少
              let arr = list.filter(function (item) {
                  return item.id === data.id;
              }); //若有返回值则对某项商品操作(在1-99区间,若为0则直接删除)
              if (arr.length == 0) {
                  data.num++;
                  data.sum = data.num * data.price;
                  list.push(data);
              } else if (arr[0].num < 99) {
                  arr[0].num++;
                  arr[0].sum = arr[0].num * arr[0].price;
              }
              ShopModel.getInstance().shoppingList = list
          }
      }
    • ReduceItemCommand(减少商品)
      import ShopModel from '../model/ShopModel.js'
      export default class ReduceItemCommand { //行为类,用于执行ctrl层通过事件的方式发来的指令
          constructor() { //减少商品
      
          }
          eventHandler(e) { //使用动态方法,而不是static静态方法,因为该方法将被使用多次,使用new AddShopCommand实例化后调用
              let {
                  data
              } = e
              ReduceItemCommand.reduceItem(ShopModel.getInstance().shoppingList, data)
          }
          static reduceItem(list, data) { //遍历查询某项商品增加或减少
              let arr = list.filter(function (item) {
                  return item.id === data.id;
              }); //若有返回值则对某项商品操作(在1-99区间,若为0则直接删除)
              if (arr[0].num > 1) {
                  arr[0].num--;
                  arr[0].sum = arr[0].num * arr[0].price;
              } else {
                  data.num = 0; //此处初始化model中的shopList,否则会假删除(删除栈中的数量)
                  list = list.filter(function (item) {
                      return item.id !== data.id;
                  });
              }
              ShopModel.getInstance().shoppingList = list
          }
      }
    • ChangeItemCommand(修改商品数量)
      import ShopModel from '../model/ShopModel.js'
      export default class ChangeItemCommand { //行为类,用于执行ctrl层通过事件的方式发来的指令
          constructor() { //修改商品数量
      
          }
          eventHandler(e) { //使用动态方法,而不是static静态方法,因为该方法将被使用多次,使用new AddShopCommand实例化后调用
              let {
                  data
              } = e
              ChangeItemCommand.changeItem(ShopModel.getInstance().shoppingList, data)
          }
          static changeItem(list, data) {
              let arr = list.filter(function (item) {
                  return item.id === data.id;
              });
              arr[0].sum = arr[0].num * arr[0].price;
              ShopModel.getInstance().shoppingList = list
          }
      }
    • DelItemCommand(删除商品)
      import ShopModel from '../model/ShopModel.js'
      export default class DelItemCommand { //行为类,用于执行ctrl层通过事件的方式发来的指令
          constructor() { //删除商品
      
          }
          eventHandler(e) { //使用动态方法,而不是static静态方法,因为该方法将被使用多次,使用new AddShopCommand实例化后调用
              let {
                  data
              } = e
              DelItemCommand.delItem(ShopModel.getInstance().shoppingList, data)
          }
          static delItem(list, data) { //遍历查询某项商品增加或减少
              data.num = 0; //此处初始化model中的shopList,否则会假删除(删除栈中的数量)
              data.select = false; //此处初始化model中的shopList,否则会假删除(删除栈中的数量)
              ShopModel.getInstance().shoppingList = list.filter(function (item) { //数组过滤函数,返回id属性不等于当前id的数组,即删除当前选中的对象,并重新赋值
                  return item.id !== data.id;
              });
          }
      }
    • SelectItemCommand(选中商品)
      import ShopModel from '../model/ShopModel.js'
      export default class SelectItemCommand { //行为类,用于执行ctrl层通过事件的方式发来的指令
          constructor() {
      
          }
          eventHandler(e) { //使用动态方法,而不是static静态方法,因为该方法将被使用多次,使用new AddShopCommand实例化后调用
              let {
                  data
              } = e
              SelectItemCommand.selItem(ShopModel.getInstance().shoppingList, data)
          }
          static selItem(list, data) { //遍历查询某项商品增加或减少
              if (!data) { //全选框
                  list.checkedAll = !list.checkedAll
                  list.map(function (item) {
                      item.select = list.checkedAll; //其他选项框与全选框状态一致
                  })
              } else { //单选框
                  list.checkedAll = 1 //计数器,用来查询是否为全选状态
                  list.map(function (item) { //单选,选中某一个(在表格初始化时执行checkAll判断是否全选)
                      if (item.id === data.id) {
                          item.select = !item.select
                      }
                      list.checkedAll *= item.select
                  })
              }
              ShopModel.getInstance().shoppingList = list
          }
      }

    components

    • Counter(计数器组件)
      import ShopEvent from '../event/ShopEvent.js'
      import ShopController from '../controller/ShopController.js'
      import Utils from '../utils/Utils.js'
      export default class Counter { //计数器组件
          constructor(_data, _parentEle) {
              this.data = _data
              this.parentEle = _parentEle
              this.ele = this.createCounter()
          }
          createCounter() { //创建数量计数器
              let div = Utils.createEle('div', {}, {
                  className: 'numBox'
              })
              this.parentEle.appendChild(div);
              let leftBtn = this.createMark(div, 'reduce') //减少商品按钮
              let input = Utils.createEle('input', {}, {
                  type: 'text',
                  value: this.data.num
              })
              div.appendChild(input);
              let rightBtn = this.createMark(div, 'add') //新增商品按钮
              leftBtn.addEventListener("click", this.reduceItemEvent);
              rightBtn.addEventListener("click", this.addItemEvent);
              input.addEventListener("input", Utils.throttle(this.changeItemEvent, 500)); // 节流
              return div;
          }
          createMark(parentEle, type) { //判断增加或减少键
              let markBtn = Utils.createEle('button', {}, {
                  textContent: type == "add" ? '+' : '-'
              })
              parentEle.appendChild(markBtn);
              return markBtn
          }
          addItemEvent = e => { //新增商品时,抛发事件至command控制model修改数据,刷新表格
              ShopController.dispatch(ShopEvent.ADD_SHOPIING_ITEM, this.data)
          }
          reduceItemEvent = e => { //减少商品
              ShopController.dispatch(ShopEvent.REDUCE_SHOPIING_ITEM, this.data)
          }
          changeItemEvent = e => { //修改商品
              e.target.value = this.data.num = this.checkNumber(e.target.value)
              ShopController.dispatch(ShopEvent.CHANGE_SHOPIING_ITEM, this.data)
          }
          checkNumber(value) { //过滤数据
              value = value.replace(/[^0-9]/g, ""); //只允许输入数字
              if (value === "0") { // 如果=0,就设为1
                  value = "1";
              }
              if (value.length > 2) { // 如果输入的内容大于2位,让这个值为99(最大99个)
                  value = "99";
              }
              if (value.length === 0) { //  如果什么都没有输入,也设为1
                  value = "1";
              }
              return value
          }
      }

    config

    • Api
      export default class Api {//接口配置类
          static URL = "http://127.0.0.1";
          static PORT = ":1024";
          static PATH = '/'
          static GET = "get";
          static POST = "post";
          static IMGPATH = Api.URL + Api.PORT + Api.PATH;
          static ServerApi = {
              getShopList: 'getShopList' //获取商品列表
          }
      }

    controller

    • ShopController(控制层,做事件传导,数据传输)
      export default class ShopController extends EventTarget { //控制层,处理用户交互,路由,输入,将model view controller剥离开,通过controller中的事件监听抛发进行路由传输数据
          constructor() { //继承事件对象,用于抛发自定义事件
              super();
          }
          static get instance() {  //单例写法与java中getinstance相似,new会生成一个新对象,分配内存,而这么写可以把一个已存在的引用给你使用,节省效能,若只使用get + 属性名而不用set产生只读属性,只允许调用,无法修改
              if (!ShopController._instance) {
                  Object.defineProperty(ShopController, "_instance", {
                      value: new ShopController()
                  })
              }
              return ShopController._instance;
          }
          static dispatch(type, data) { //抛发自定义事件,传递数据
              let event = new Event(type)
              event.data = data
              ShopController.instance.dispatchEvent(event)
          }
          static runCommand(type, Command) { //观察者模式,当自定义事件触发时调用其他类中的方法,与dispatch对应,类似于addEventlistener,只不过将回调函数换成类中的动态方法
              var command = new Command()
              ShopController.instance.addEventListener(type, command.eventHandler)
          }
      }

    event

    • ShopEvent
      export default class ShopEvent {
          constructor() {
      
          }
          // 所有自定义事件名称
          static GET_DATA = 'get_data'
          static GET_SHOP_LIST = 'get_shop_list'
          static GET_SHOPIING_LIST = 'get_shopping_list'
          static ADD_SHOPIING_ITEM = 'add_shopping_item'
          static DEL_SHOPIING_ITEM = 'del_shopping_item'
          static REDUCE_SHOPIING_ITEM = 'reduce_shopping_item'
          static CHANGE_SHOPIING_ITEM = 'change_shopping_item'
          static SELECT_SHOPIING_ITEM = 'select_shopping_item'
      }
    • EventGroup(事件总线)
      import ShopEvent from './ShopEvent.js'
      import ShopController from '../controller/ShopController.js'
      import MainCommand from '../command/MainCommand.js'
      let {
          GetDataCommand,
          CreateListCommand,
          CreateTableCommand,
          AddItemCommand,
          DelItemCommand,
          ReduceItemCommand,
          ChangeItemCommand,
          SelectItemCommand
      } = MainCommand
      export default class EventGroup { //事件总线,注册所有model层与其它层的业务逻辑,全程通过controller层中的事件机制进行通信
          constructor() {
              /*
              1.Ajax获取到数据后,触发GetDataCommand中的方法,用于传递数据至Model层中,然后通过Model层调用CreateListCommand创造商品列表
              2.当用户对商品做任何操作时,都会修改Model从而触发CreateTableCommand,以下操作会触发CreateTableCommand
              3.点击商品列表或点击商品加号按钮时触发AddItemCommand,通过AddItemCommand修改model中的数据,从而驱动CreateTableCommand
              4.点击商品减号按钮时触发ReduceItemCommand,修改model中的数据,从而驱动CreateTableCommand
              5.点击商品删除按钮时触发DelItemCommand,删除model中的数据,从而驱动CreateTableCommand
              6.修改商品数量时触发ChangeItemCommand,更新model中的数据,从而驱动CreateTableCommand
              7.选中商品时触发SelectItemCommand,更新model中的数据,从而驱动CreateTableCommand
               */
              ShopController.runCommand(ShopEvent.GET_DATA, GetDataCommand)//获取商品列表数据
              ShopController.runCommand(ShopEvent.GET_SHOP_LIST, CreateListCommand)//新建商品列表
              ShopController.runCommand(ShopEvent.GET_SHOPIING_LIST, CreateTableCommand)//刷新购物车表格
              ShopController.runCommand(ShopEvent.ADD_SHOPIING_ITEM, AddItemCommand)//商品新增或数量加一
              ShopController.runCommand(ShopEvent.DEL_SHOPIING_ITEM, DelItemCommand)//商品删除
              ShopController.runCommand(ShopEvent.REDUCE_SHOPIING_ITEM, ReduceItemCommand)//商品数量减一
              ShopController.runCommand(ShopEvent.CHANGE_SHOPIING_ITEM, ChangeItemCommand)//修改商品数量
              ShopController.runCommand(ShopEvent.SELECT_SHOPIING_ITEM, SelectItemCommand)//选择商品
          }
      }

    model

    • ShopModel(模型层,用于数据存放及数据逻辑)
      import ShopEvent from '../event/ShopEvent.js'
      import ShopController from '../controller/ShopController.js'
      export default class ShopModel { //模型层,用于数据存放及数据逻辑,通过事件处理机制(controller)传递数据,再由command进行对数据操作
          constructor() {
              this._shopList = null
              this._shoppingList = []
          }
      
          static getInstance() { //单例写法与java中getinstance相似,new会生成一个新对象,分配内存,而这么写可以把一个已存在的引用给你使用,节省效能,若只使用get + 属性名而不用set产生只读属性,只允许调用,无法修改
              if (!ShopModel._instance) {
                  Object.defineProperty(ShopModel, "_instance", {
                      value: new ShopModel()
                  })
              }
              return ShopModel._instance;
          }
          set shopList(value) {//设置商品列表
              this._shopList = value;
              ShopController.dispatch(ShopEvent.GET_SHOP_LIST, value)
          }
          get shopList() {
              return this._shopList
          }
          set shoppingList(value) {//数据修改时,驱动view进行表格刷新
              this._shoppingList = value;
              ShopController.dispatch(ShopEvent.GET_SHOPIING_LIST, value)
          }
          get shoppingList() {
              return this._shoppingList
          }
      }

    utils

    • Utils(工具类)
      export default class Utils { //工具类
      	//将对象拼接到url中
      	static urlJoin(url, obj) {
      		var list = []
      		for (var key in obj) {
      			if (obj.hasOwnProperty(key)) {
      				list.push(`${key}=${obj[key]}`)
      			}
      		}
      		return `${url}?${list.join('&')}`
      	}
      	static createEle(ele, style, attribute) { //新增标签,设置属性及样式
      		let element = document.createElement(ele)
      		if (style) {
      			for (let key in style) {
      				element.style[key] = style[key];
      			}
      		}
      		if (attribute) {
      			for (let key in attribute) {
      				element[key] = attribute[key];
      			}
      		}
      		return element
      	}
      	// 函数节流
      	static throttle(fn, time) {
      		let _timer = null
      		return function () {
      			if (_timer) {
      				clearTimeout(_timer)
      				_timer = null
      			}
      			_timer = setTimeout(fn.bind(this, ...arguments), time)
      		}
      	}
      }

    view

    • ShopView(视图层,用于元素渲染,显示数据)
      import Api from '../config/Api.js'
      import AJAX from '../bussiness/Ajax.js'
      import EventGroup from '../event/EventGroup.js'
      export default class ShopView { //视图层,用于元素渲染,显示数据,依据(model)模型数据创建
          constructor() {
              new EventGroup() //注册所有自定义事件,用于数据传输
              AJAX.AjaxTool(Api.GET, Api.ServerApi.getShopList, { //请求服务端购物车列表
                  token: 'hello'//发送从后端获取的token用于验证,此处未做获取,直接用一个字符代替
              })
          }
      }
    • CreateList(列表视图)
      import Api from '../config/Api.js'
      import Utils from '../utils/Utils.js'
      import ShopEvent from '../event/ShopEvent.js'
      import ShopController from '../controller/ShopController.js'
      
      export default class CreateList { //视图层,用于元素渲染,显示数据,依据(model)模型数据创建
          constructor(parentEle) {
              this.parentEle = parentEle
              this._shopList = null
          }
          set shopList(value) { //使用对象代理,每当数据发生更改时渲染商品列表
              if (this._shopList) {
                  this.createListEle(this._shopList, this.parentEle)
                  return;
              }
              this._shopList = value
              this.createListEle(value, this.parentEle)
          }
          get shopList() {
              return this._shopList
          }
          createListEle(data, parentEle) {
              let li = Utils.createEle('li', {}, {
                  'className': 'liItem'
              })
              let img = Utils.createEle('img', {}, {
                  'src': Api.IMGPATH + data.icon
              })
              let title = Utils.createEle('div', {}, {
                  'textContent': data.name
              })
              let price = Utils.createEle('span', {}, {
                  'textContent': data.price + "元"
              })
              li.appendChild(img);
              li.appendChild(title);
              li.appendChild(price);
              li.addEventListener('click', this.addItemEvent)
              parentEle.appendChild(li);
          }
          addItemEvent = e => { //当用户点击添加商品时,将数据通过controller层发送至事件总线,再驱动model层并修改数据,后续由Model提供数据刷新表格
              ShopController.dispatch(ShopEvent.ADD_SHOPIING_ITEM, this.shopList)
          }
      }
    • CreateTable(购物车表格视图)
      import Api from '../config/Api.js'
      import Utils from '../utils/Utils.js'
      import Counter from '../components/Counter.js'
      import ShopEvent from '../event/ShopEvent.js'
      import ShopController from '../controller/ShopController.js'
      
      export default class CreateTable { //视图层,用于元素渲染,显示数据,依据(model)模型数据创建
          constructor(parentEle) {
              this.parentEle = parentEle
              this._shoppingList = null
              this._table = null
          }
          set shoppingList(value) { //对象代理,若model数据改变时,刷新表格
              let table = document.getElementById('table')
              if (table) { //初始化表格,若有则删除
                  table.remove()
                  table = null;
              }
              this._shoppingList = value || []
              this.createTab(value, this.parentEle)
          }
          get shoppingList() {
              return this._shoppingList
          }
          createTab(data, parentEle) {
              this._table = Utils.createEle('table', {}, {
                  id: "table"
              })
              let thr = Utils.createEle('tr')
              for (let prop in data[0]) { //创建表头,如果属性名是select,就创建全选按钮
                  let th = Utils.createEle('th')
                  if (prop === "select") {
                      let input = Utils.createEle('input', {}, {
                          type: "checkbox",
                          className: "checkbox",
                          checked: this.checkedAll() //查询是否全选
                      })
                      input.addEventListener("change", this.seleteEvent);
                      th.appendChild(input);
                  } else {
                      th.textContent = prop;
                  }
                  thr.appendChild(th)
              }
              this._table.appendChild(thr)
              for (let i = 0; i < data.length; i++) {
                  let tr = Utils.createEle('tr')
                  this._table.appendChild(tr);
                  for (let str in this.shoppingList[i]) {
                      let td = document.createElement("td");
                      this.selectTdType(td, this.shoppingList[i], str);
                      tr.appendChild(td);
                  }
              }
              parentEle.appendChild(this._table)
          }
          selectTdType(td, data, type) {
              switch (type) {
                  case 'select':
                      let input = Utils.createEle('input', {}, {
                          type: 'checkbox',
                          className: 'checkbox',
                          checked: data['select'],
                          data
                      })
                      input.addEventListener("change", this.seleteEvent);
                      td.appendChild(input);
                      break;
                  case 'icon':
                      let img = Utils.createEle('img', {}, {
                          'src': Api.IMGPATH + data.icon
                      })
                      td.appendChild(img);
                      break;
                  case 'num':
                      new Counter(data, td) //实例化计数器组件
                      break;
                  case 'delete':
                      let btn = Utils.createEle('button', {}, {
                          textContent: '删除',
                          data
                      })
                      td.appendChild(btn);
                      btn.addEventListener("click", this.deleteItemEvent);
                      break;
                  default:
                      td.textContent = data[type];
                      break;
              }
          }
          deleteItemEvent = e => { //触发删除事件,通过command删除后通知model驱动view进行刷新
              ShopController.dispatch(ShopEvent.DEL_SHOPIING_ITEM, e.target.data)
          }
          seleteEvent = e => { //触发选择商品事件,通过command删除后通知model驱动view进行刷新
              ShopController.dispatch(ShopEvent.SELECT_SHOPIING_ITEM, e.target.data)
          }
          checkedAll() {
              let count = 1
              this.shoppingList.map((item) => {
                  count *= item.select
              })
              return count
          }
      }
  • 相关阅读:
    mysql u root p 等这些常用的参数
    关于类对象大小的 sizeof 计算问题
    sql like用法
    链表队列
    Zend Studio安装教程
    #define和inline 的区别
    ERROR 1045 (28000): Access denied for user root@localhost (using password: NO)
    Ubuntu 10.04下LAMP环境的配置与使用教程
    win7保护视力的电脑设置
    sql between and 用法
  • 原文地址:https://www.cnblogs.com/HelloWorld-Yu/p/12482604.html
Copyright © 2011-2022 走看看