MVVM框架介绍
介绍
- M :Modal 模型层
- V :View 视图层
- VM : ViewModal 视图模型,V和M的桥梁
MVVM框架实现了数据双向绑定
- 当M层数据进行修改时,VM层会检测到变化,并且通知V层进行相应的修改;
- 修改V层则会通知M层数据进行修改
- MVVM框架实现了视图和模型层的相互解耦
几种双向数据绑定的方式
发布-订阅者模式(backbone.js)
一般通过pub、sub的方式来实现数据和视图的绑定,但是使用比较麻烦脏值检查(anjular.js)
angular.js是通过脏值检测的方式对数据是否有变更,来决定是否更新视图。类似于通过定时器轮训检测数据是否发生了改变;数据劫持
vue.js是通过数据劫持结合发布者-订阅者的方式。通过Object.defineProperty()来劫持各个属性的setter、getter,在数据发生变动的时候发布消息给订阅者,触发相应的监听回调
Vue实现思路
- 实现一个Compile模板解析器,能够对模板中的指令和插值表达式进行解析,并且赋予不同的操作
- 实现一个Observe数据监听器,能够对数据对象的所有属性进行监听
- 实现一个Watcher观察者,将Compile的解析结果,与Observer所观察的对象链接起来,建立关系,在Observer观察对象到对象数据变化时,接受通知,同时更新DOM
- 创建一个公共的入口对象,接受初始化的配置并且协调上面三个模块,也就是Vue
Compile实现逻辑
创建文件
- 需要基本的html文件,vue.js文件,并且引入并且实例化vue对象
1 | <body> |
- 创建vue对象
1 |
|
- 创建Compile模板解析器、
1 |
|
创建文本碎片
DocumentFragments 是DOM节点。它们不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有的子元素所代替。
因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面回流(对元素位置和几何上的计算)。因此,使用文档片段通常会带来更好的性能。
1 |
|
根据类型解析节点
- 区分是文本节点还是元素节点来分别解析
- 使用递归层层解析
1 | /** |
解析元素节点
- 需要分别解析v-text v-html v-modal指令
- v-on指令因为类型原因需要特别解析
1 | /** |
解析文本节点
1 |
|
Observer实现逻辑
关于数据劫持
configurable
当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
enumerable
当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false。
get
一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。
默认为 undefined。
set
一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。
默认为 undefined。
简单案例
1 | var obj = { |
observer实现
- 观察者目的是使用数据劫持检测$data下的所有数据(为什么天赐还没成大佬)
1 | new Observer(this.vm.$data); |
使用Object.keys(data)方法获取所有key,并通过Object.defineProperty为所有数据添加绑定(为什么天赐还没成大佬)
为了绑定复杂数据,需要进行递归操作(为什么天赐还没成大佬)
在set函数中,设置了新的数据,也需要进行监控(为什么天赐还没成大佬)
1 |
|
Wacher实现逻辑
已经实现了页面渲染compile以及数据监听observer,接下来是将数据和页面做一个连接,即是,数据发生改变之后通知compile重新渲染,compile发生改变之后通知observer更改数据,接下来的watcher作为一个连接中心,实现这一部分的功能;
创建watcher对象
初始化需要传递三个变量
- vm:vue对象实例
- expr:数据对象
- cb:callback回调函数
1 | constructor(vm, expr, cb) { |
创建更新数据函数
1 |
|
实例化watcher对象
1 | //解析v-text指令 |
获取新值调用update
1 | set(newvalue) { |
使用订阅发布者模式检测数值改变
- 声明发布者构造函数
- 订阅检测对象
- 实例化检测data中的每一个属性
1 |
|
1 |
|
实现双向数据绑定
1 | node.addEventListener('input', function() { |
将数据挂载到vue对象中
1 | class Vue { |