【MVC】用low代码写四个模块+MVC优化

249 阅读6分钟

所有代码请康我的GitHub项目MVC-demo-1

最终代码请康开源项目

效果图

  • 第一块:实现加减乘除
  • 第二块:点1就显示content1;点2就显示content2
  • 第三块:鼠标点击就会移动变形
  • 第四块:鼠标悬浮就会颜色渐变
  • 需要数据储存,刷新后还在刷新前的位置
  • 模块化,最小知识原则,jquery、css、html都要由main.js引入

烂代码

初始化

把四块的位置都定好

写app1

  1. 监听每个按钮的点击事件
  2. 需要储存变化后的数字

写app2

  1. 监听点击事件
  2. 背景色转换
  3. 内容转换

写app3

  1. 监听点击事件
  2. 有class就删掉,没class(移到右边)就加上

写app4

  1. 监听鼠标进入出来事件
  2. 进入就加上class(颜色动画),出来就删掉class

MVC优化

先给烂代码每一步加上注释

数据放在m,视图放在v,其他放在c

//数据放在m
const m = {
 //1.有个数据本数
  data: {},
  //2.可以对数据增删改查
  create() {},
  delete() {},
  update(data) {},
  get() {}
}
//视图放在v
const v = {
  //1、一个空容器,以后就是装html的容器
  el: null,
  //2、要添加的html
  html: ,
  //3、初始化容器函数,参数是我们给的要当容器的元素(应该是index.html里就有的元素)
  init(container) {v.el = $(container);},
  //4、渲染函数,参数将是数据。也就是视图全都是对数据渲染 view = render(data)
  render(x) {}
}
//其他放在c
const c = {
  //1.总初始化函数,参数是我们给的要当容器的元素
  init(container) {},
  //2、自动绑定事件
  //(1)把所有事件写成哈希表
  events: {},
  //(2)每个事件要执行的函数写出来
  add() {},
  minus() {},
  mul() {},
  div() {},
  //(3)自动绑定事件
  autoBindEvents() {}
}

修改形式,因为放在对象里

  • 修改成对象里的属性
  • 变量名前要加上对象.变量名

原来注释的地方还是要执行的

关于指定index.html中一个元素为容器

  1. 首次渲染时,我们指定index.html里的一个元素为container。把html放入这个container里面
  2. 每次更新,container都是不会变的,但是container里的html会变(但是id不会变,变的是数据)
  3. 那用事件委托,把事件绑定在container的用id选出来的子元素上,那事件也不会变。就不用每次都重新绑定事件了

所有视图都应该是渲染数据的结果!view = render(data)

表驱动编程:自动绑定事件

  1. 表指的是哈希表
  2. 表驱动编程可以减少重复代码,只讲重要的信息放在表里,然后利用表来编程
  3. 利用哈希表,把每一个的事件,元素,函数列出来
  4. 把每一个的函数定义出来
  5. 自动绑定事件:利用遍历哈希表,把元素绑定上事件。

eventBus对象间通信

  1. const eventBus = $(window)

  2. eventBus 提供了 on、off 和 trigger 等 API,on 用于监听事件,trigger 用域触发事件

  3. eventBus 主要用于对象间通信

  4. 使用 eventBus 可以满足最小知识原则,m 和 v 互相不知道对方的细节,

  5. 数据更新,就重新渲染一次。那怎么知道数据更新了?在数据更新函数加上触发eventBus的xxx事件,那么监听eventBus的xxx事件,要是触发了就重新用新数据渲染一次。

  6. 数据更新 → 触发eventBus的xxx事件 → 执行xxx事件监听函数:用新数据再次渲染

所有代码

import "./app1.css";
import $ from "jquery";

const eventBus = $(window); //这个dom元素也叫事件公交车,有个on和trigger属性,可以监听和触发任何事件,这样就可以对象间通信了
// 一、数据相关都放到m
const m = {
  //1.有个数据本数
  data: {
    n: parseInt(localStorage.getItem("n"))
  },
  //2.可以对数据增删改查
  create() {},
  delete() {},
  //本模块只需要对数据修改。update函数①把老数据替换成参数变成新数据,②触发eventBus的m:updated事件,③把新数据储存
  update(data) {
    Object.assign(m.data, data);
    eventBus.trigger("m:updated");
    localStorage.setItem("n", m.data.n);
  },
  get() {}
};
// 二、视图相关都放到v
const v = {
  //1、一个空容器,以后就是装html的容器
  el: null,
  //2、要添加的html
  html: `
  <div>
    <div class="output">
      <span id="number">{{n}}</span>
    </div>
    <div class="actions">
      <button id="add1">+1</button>
      <button id="minus1">-1</button>
      <button id="mul2">*2</button>
      <button id="divide2">÷2</button>
    </div>
  </div>
`,
  //3、初始化容器函数,参数是我们给的要当容器的元素(应该是index.html里就有的元素)
  init(container) {
    v.el = $(container);
  },
  //4、渲染函数,参数将是数据。也就是视图全都是对数据渲染 view = render(data)
  render(x) {
    if (v.el.children.length !== 0) v.el.empty(); //如果容器里有东西,就全删掉
    $(v.html.replace("{{n}}", x)).appendTo(v.el); //把html里的占位符替换成x,再加入容器中
  }
};
// 三、其他都c
const c = {
  //1.总初始化函数,参数是我们给的要当容器的元素
  init(container) {
    v.init(container); //①首先初始化容器
    v.render(m.data.n); // ②把html里的占位符替换成数据,然后全部渲染出来。view = render(data)
    c.autoBindEvents(); //③执行自动绑定函数
    eventBus.on("m:updated", () => {
      //④监听m:updated事件,每次触发就重新用新数据渲染一遍
      console.log("here");
      v.render(m.data.n);
    });
  },
  //2、自动绑定事件
  //(1)把所有事件写成哈希表
  events: {
    "click #add1": "add",
    "click #minus1": "minus",
    "click #mul2": "mul",
    "click #divide2": "div"
  },
  //(2)每个事件要执行的函数写出来
  add() {
    m.update({ n: m.data.n + 1 }); //每次点击就做数据的修改函数
  },
  minus() {
    m.update({ n: m.data.n - 1 });
  },
  mul() {
    m.update({ n: m.data.n * 2 });
  },
  div() {
    m.update({ n: m.data.n / 2 });
  },
  //(3)自动绑定事件
  autoBindEvents() {
    for (let key in c.events) {
      //对事件哈希表里每一个事件
      const value = c[c.events[key]]; //要执行的函数  如add
      const spaceIndex = key.indexOf(" ");
      const part1 = key.slice(0, spaceIndex); //事件名part1  如click
      const part2 = key.slice(spaceIndex + 1); //实际监听元素part2   如#add1元素
      v.el.on(part1, part2, value); //用事件委托,监听容器元素的part1事件,其实是监听他的子元素part2的part1事件,执行函数。
      //这里是用容器元素的子元素的id来选出子元素然后绑定事件给这些子元素,每次渲染子元素的id不会变的。所以用子元素id选出子元素在绑定事件,一劳永逸!
    }
  }
};

//必须把总初始化函数导出来使用,所以把整个c导出来。在main.js里使用总初始化函数。
export default c;

//监听m-updated事件,对数据修改的同时还会触发该事件,触发了就用新数据再渲染一次html

//思考过程看ipad笔记

app2的优化

用了DOM的data-*做标记

import "./app2.css";
import $ from "jquery";

const eventBus = $(window);

const localKey = "app2.index";

// 一、数据相关都放到m
const m = {
  //1.有个数据本数
  data: {
    index: parseInt(localStorage.getItem(localKey)) || 0
  },
  //2.可以对数据增删改查
  create() {},
  delete() {},
  //本模块只需要对数据修改。update函数①把老数据替换成参数变成新数据,②触发eventBus的m:updated事件,③把新数据储存
  update(data) {
    Object.assign(m.data, data);
    eventBus.trigger("m:updated");
    localStorage.setItem("index", m.data.index);
  },
  get() {}
}

// 二、视图相关都放到v
const v = {
    //1、一个空容器,以后就是装html的容器

  el: null,
   //2、要添加的html(对数据渲染,所以想想如何在html里利用数据)
       //这个html是个函数,必须把数据给它当参数,把字符串里的东西替换了,然后他才会返回html字符串
       //如果数据(下标、参数)是0,那就说明点到第一个按钮,给第一个按钮和第一个内容加class,
       //如果数据(下标、参数)是1,那就说明点到第二个按钮,给第一个按钮和第二个内容加class
       //用data-*做标记方法,把li的下标记下来出来。
  html: index => {
    return `
    <div>
      <ol class="tab-bar">
        <li class="${ 
          index === 0 ? "selected" : ""
        }" data-index="0"><span>1111</span></li>
        <li class="${
          index === 1 ? "selected" : ""
        }" data-index="1"><span>2222</span></li>
      </ol>
      <ol class="tab-content">
        <li class="${index === 0 ? "active" : ""}">内容1</li>
        <li class="${index === 1 ? "active" : ""}">内容2</li>
      </ol>
    </div>
`;
  },
  //3、初始化容器函数,参数是我们给的要当容器的元素(应该是index.html里就有的元素)
  init(container) {
    v.el = $(container);
  },
  //4、渲染函数,参数将是数据。也就是视图全都是对数据渲染 view = render(data)
  render(index) {
    if (v.el.children.length !== 0) v.el.empty();
    $(v.html(index)).appendTo(v.el);
  }
};

// 三、其他都c
const c = {
  //1.总初始化函数,参数是我们给的要当容器的元素

  init(container) {
    v.init(container);
    v.render(m.data.index); // view = render(data)
    c.autoBindEvents();
    eventBus.on("m:updated", () => {
      v.render(m.data.index);
    });
  },
//2、自动绑定事件
  //(1)把所有事件写成哈希表
  events: {
    "click .tab-bar li": "x"
  },
  //(2)每个事件要执行的函数写出来
  //每次点击那个li,取出我们在html里加好的他的data-index,这样我们就知道下标了,也就是数据。用dataset.index取出来!
  x(e) {
    const index = parseInt(e.currentTarget.dataset.index);
    m.update({ index: index });
  },
  //(3)自动绑定事件
  autoBindEvents() {
    for (let key in c.events) {
      const value = c[c.events[key]];
      const spaceIndex = key.indexOf(" ");
      const part1 = key.slice(0, spaceIndex);
      const part2 = key.slice(spaceIndex + 1);
      v.el.on(part1, part2, value);
    }
  }
};

//必须把总初始化函数导出来使用,所以把整个c导出来。在main.js里使用总初始化函数。
export default c;