想要真正的明白MVC的框架是需要大量的实战的. 而MVC框架是MVVC框架的前置知识. 而MVC框架带的前置知识是整个JavaScript知识体系.
MVC是 Model
、View
、Controller
三个的缩写合并而成.
今天的我的目标是要创造一个能够自加自减的工具. 点击 + 可以增加岁数, 减就减少对应的岁数.如图所示:
接下来进入code缓解, 现在需要创建一个容器来承载接下来的内容
<body>
<div id="app"></div>
</body>
还需要创建对应的三个对象来分别对应MVC三部分:
;(function(){
const Model = {}
const View = {}
const Controller = {}
}())
三者各司其职, 现在添加一些默认相上去,以使它们能够启动.
;(function(){
init()
const Model = {
data: {},
init() {}
}
const View = {
render() {}
}
const Controller = {
init() {}
}
function init() {
Model.init()
View.render()
Controller.init()
}
}())
这里面的知识点涉及到了[[IIFE]] 立即执行函数是我们在没有模块化管理工具之前,进行插件开发的一个必要的工具
基本的代码结构已经完成了.
我们先对Model
部分进行详细的code
const Model = {
data: {
name: '橘子哥哥',
age: 18
},
init() {
for (let key in this.data) {
Object.defineProperty(this, key, {
get() {
return this.data[key]
},
set(newValue) {
this.data[key] = newValue
// View.render({[key]: newValue})
}
})
}
}
}
我们对Model
来进行初始化,就是为了让外面的可以直接通过Model.name
就能够访问到内容的data对象中定义的值. 而多出一个data来包裹着变量, 主要是为了增加代码的可读性.让数据和方法分离.
我们的目的是对数据进行响应式的封装.所以说在set
的时候,进行View.render
的调用.一旦变量改变了就调用View
模块渲染部分逻辑.
传入的参数是当前改变的值,将来是要根据这个值来改变对应内容的重新渲染的.
这里涉及到了 [[数据劫持]] ,Flutter的Getx框架就显行的使用数据劫持来进行数据的处理.在JS中属于[[对象的访问器属性]]的知识
数据已经处理好了,我们来处理界面渲染部分的逻辑,我们逻辑的最终的出口:
const View = {
el: '#app',
template: `
<div class="box">
<div class="cal-name">{{ name }}</div>
<div>,今年</div>
<div class="cal-age">{{ age }}</div>
<div>岁</div>
<div class="splice"></div>
<button class="cal-button add">+</button>
<button class="cal-button reduce">-</button>
</div>
`,
reg: /\{\{(.*?)\}\}/g,
render(mutedData) {
if (!mutedData) {
this.template = this.template.replace(this.reg, (node, key) => {
return model[key.trim()];
})
const container = document.createElement('div')
container.innerHTML = this.template
document.querySelector(this.el).appendChild(container);
} else {
for (let key in mutedData) {
document.querySelector(`.cal-${key}`).textContent = mutedData[key]
}
}
}
}
el
的命名和作用和Vue
框架中的保持一致.目的也一目了然.就是把我们要渲染的内容都挂载在id为app
的标签中.
template
就对应Vue
中的模版,或者说是JSX
.
render
部分涉及到了[[正则表达式的基础]],以及如何使用正则来实现简单的模版替换. 通过判断,如果没有参数传进来,即mutedData
为undefined
的话,就是初始化渲染全部内容. 而有值传进来就表示值有变化,就直接改变对应的地方渲染就可以了.
此时可以查看界面,以及出现了我们一开始的目标样子.这个时候我们还需要加上我的操作逻辑.
const Controller = {
init() {
const oCalButtons = document.querySelectorAll(".cal-button");
for (let i = 0; i < oCalButtons.length; i++) {
btnItem = oCalButtons[i];
btnItem.addEventListener("click", this.handleBtnClick, false);
}
},
handleBtnClick(e) {
switch (e.target.textContent) {
case "+":
model.age++;
break;
case "-":
model.age--;
break;
default:
break;
}
},
}
-
init
绑定上点击事件 -
handleBtnClick
处理具体的事件
最后我们来看一下整体的代码:
(function () {
function init() {
model.init();
view.render();
controller.init();
}
const model = {
data: {
name: "橘子哥哥",
age: 19,
},
init() {
for (let key in this.data) {
Object.defineProperty(this, key, {
get() {
return this.data[key];
},
set(newValue) {
this.data[key] = newValue;
view.render({ [key]: newValue }); // ❌
},
});
}
},
};
const view = {
el: "#app",
template: `
<div class="box">
<div class="cal-name">{{ name }}</div>
<div>,今年</div>
<div class="cal-age">{{ age }}</div>
<div>岁</div>
<div class="splice"></div>
<button class="cal-button add">+</button>
<button class="cal-button reduce">-</button>
</div>
`,
reg: /\{\{(.*?)\}\}/g,
render(mutedData) {
if (!mutedData) {
this.template = this.template.replace(this.reg, (node, key) => {
return model[key.trim()];
});
const container = document.createElement("div");
container.innerHTML = this.template;
document.querySelector(this.el).appendChild(container);
} else {
for (const key in mutedData) {
document.querySelector(`.cal-${key}`).textContent =
mutedData[key];
}
}
},
};
const controller = {
init() {
const oCalButtons = document.querySelectorAll(".cal-button");
for (let i = 0; i < oCalButtons.length; i++) {
btnItem = oCalButtons[i];
btnItem.addEventListener("click", this.handleBtnClick, false);
}
},
handleBtnClick(e) {
switch (e.target.textContent) {
case "+":
model.age++;
break;
case "-":
model.age--;
break;
default:
break;
}
},
};
init();
})();
整个代码就是很典型的MVC结构的代码,在Model
中定义参数之余,还要监听参数的变化,来进行界面的渲染.根据代码可以得出这样的一张图:
- 我们喜欢的是三者能够完全的独立,但是MVC没有给我们带来. 每一个模块之间还是有所牵连.所以我们需要改变.这就有了
MVVM
的出现. MVVM的出现解决了MVC
的一部分问题. [[手写MVVM框架]] - 缺点:
- 驱动被MVC三个部分分离了.而我们的MVVM就是把驱动的封装起来