所有代码请康我的GitHub项目MVC-demo-1
最终代码请康开源项目
效果图

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




把四块的位置都定好
写app1
- 监听每个按钮的点击事件
- 需要储存变化后的数字
写app2
- 监听点击事件
- 背景色转换
- 内容转换
写app3
- 监听点击事件
- 有class就删掉,没class(移到右边)就加上
写app4
- 监听鼠标进入出来事件
- 进入就加上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中一个元素为容器
- 首次渲染时,我们指定index.html里的一个元素为container。把html放入这个container里面
- 每次更新,container都是不会变的,但是container里的html会变(但是id不会变,变的是数据)
- 那用事件委托,把事件绑定在container的用id选出来的子元素上,那事件也不会变。就不用每次都重新绑定事件了
所有视图都应该是渲染数据的结果!view = render(data)
表驱动编程:自动绑定事件
- 表指的是哈希表
- 表驱动编程可以减少重复代码,只讲重要的信息放在表里,然后利用表来编程
- 利用哈希表,把每一个的事件,元素,函数列出来
- 把每一个的函数定义出来
- 自动绑定事件:利用遍历哈希表,把元素绑定上事件。
eventBus对象间通信
-
const eventBus = $(window) -
eventBus 提供了 on、off 和 trigger 等 API,on 用于监听事件,trigger 用域触发事件
-
eventBus 主要用于对象间通信
-
使用 eventBus 可以满足最小知识原则,m 和 v 互相不知道对方的细节,
-
数据更新,就重新渲染一次。那怎么知道数据更新了?在数据更新函数加上触发eventBus的xxx事件,那么监听eventBus的xxx事件,要是触发了就重新用新数据渲染一次。
-
数据更新 → 触发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;