实现一个简单的MVVM框架
在实现mvvm之前我们需要先来认识下什么是mvvm?
# 什么是MVVM ?
MVVM(Model–view–viewmodel)是一种软件架构模式 (opens new window);MVVM有助于将图形用户界面 (opens new window)的开发与业务逻辑 (opens new window)或后端 (opens new window)逻辑(数据模型)的开发分离 (opens new window)开来,这是通过置标语言 (opens new window)或GUI代码实现的。
- MVC:
Model
数据 →View
视图 →Controller
控制器;
- MVP:
Model
模型→View
视图 →Presenter
表现层;- 由MVC模式进化而来;
- MVVM:
Model
模型 →View
视图 →ViewModel
视图模型;- 三者的关系:
view
可以通过事件绑定的方式影响model
,model
可以通过数据绑定的形式影响到view
,viewModel
是把model
和view
连起来的连接器;
# MVVM
模式的组成部分
模型:是指代表真实状态内容的领域模型 (opens new window)(面向对象),或指代表内容的数据访问层 (opens new window)(以数据为中心)。
视图:就像在MVC (opens new window)和MVP (opens new window)模式中一样,视图是用户在屏幕上看到的结构、布局和外观(UI)。
视图模型:视图模型是暴露公共属性和命令的视图的抽象。
提示
MVVM没有MVC模式的控制器,也没有MVP模式的presenter,有的是一个绑定器。在视图模型中,绑定器在视图和数据绑定器 (opens new window)之间进行通信。
# MVVM
框架的三大要素
- 数据响应式:监听数据变化并在视图中更新。
- Object.defineProperty()
- Proxy
- 模板引擎:提供描述视图的模版语法
- 插值:{{}}
- 指令:v-bind,v-on,v-model,v-for,v-if
- 渲染:如何将模板转换为html
- 模板 => vdom => dom
# 编写MVVM框架
# 数据响应式
const obj = {}
// 封装响应式函数
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log(`get ${key}:${val}`); // get name:小明
return val
},
set(newVal) {
if (newVal !== val) {
console.log(`set ${key}:${newVal}`); // set name:小红
val = newVal
}
},
})
}
// 把 obj 转化为 响应式;
defineReactive(obj, 'name', '小明');
obj.name
obj.name = '小红'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 结合视图
<!DOCTYPE html>
<html lang="en">
<body>
<h1 id="#app"></h1>
</body>
<script>
const obj = {}
const appEl = document.getElementById('#app')
function render(val) {
appEl.innerText = val;
}
// 封装响应式函数
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
return val
},
set(newVal) {
if (newVal !== val) {
// 数据变化,更新视图
render(newVal)
val = newVal
}
},
})
}
// 把 obj 转化为 响应式;
defineReactive(obj, 'date', '');
// 每秒更新数据
setInterval(() => {
obj.date = new Date().toLocaleTimeString();
}, 1000)
</script>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
以上代码可以在浏览器中直接运行,可以看到实现响应式的大致原理;
# 为所有属性添加响应式
const obj = {
eat: {
panda: '熊猫'
},
drink: '喝'
}
// 封装响应式函数
function defineReactive(obj, key, val) {
+ observe(val);
Object.defineProperty(obj, key, {
get() {
console.log(`get ${key}:${val}`);
return val
},
set(newVal) {
if (newVal !== val) {
console.log(`set ${key}:${newVal}`);
val = newVal
}
},
})
}
// 遍历观察者为所有对象添加响应式;
function observe (obj) {
if (!(obj instanceof Object) || obj === null || obj === undefined) return;
for (const key in obj) {
defineReactive(obj, key, obj[key]);
}
}
observe(obj);
obj.eat.panda
obj.drink
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
新增加方法 observe 遍历对象中的所有属性并转化为响应式;
# 解决新添加属性
function set(obj, key, val) {
defineReactive(obj, key, val);
}
// 设置新属性
set(obj, 'beverages', '饮料');
2
3
4
5
6
defineProperty无法观察到添加或删除属性的变化,所以我们需要自己触发响应式;
提示
defineProperty() 不支持数组,所以数组的监听需要其他处理方式;