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
          }
      }
  • 相关阅读:
    python模块--time模块
    python模块--如何相互调用自己写的模块
    Animating Views Using Scenes and Transitions
    fragment 切换
    android textview 设置text 字体
    android intent 5.1
    android EditView ime
    animation of android (4)
    animation of android (3)
    animation of android (2)
  • 原文地址:https://www.cnblogs.com/HelloWorld-Yu/p/12482604.html
Copyright © 2011-2022 走看看