Open Samgao0312 opened 2 years ago
Vue 数据双向绑定是通过数据劫持 + 发布者-订阅者模式的方式来实现的。
怎么实现数据劫持❓
Object.defineProperty( )是用来做什么的❓
var Book = {} var name = ''; Object.defineProperty(Book, 'name', { set: function (value) { name = value; console.log('你取了一个书名叫做' + value); }, get: function () { return '《' + name + '》' } }) Book.name = 'vue权威指南'; // 你取了一个书名叫做vue权威指南 console.log(Book.name); // 《vue权威指南》
vue采用的是MVVM这种架构模式,实现mvvm主要包含两个方面,(1)数据变化更新视图,(2)视图变化更新数据:
(1)视图变化更新数据: 关键点在于data如何更新view,因为view更新data其实可以通过事件监听即可。比如input标签监听 'input' 事件就可以实现了。所以我们着重来分析下,当数据改变,如何更新视图的。
(2)数据变化更新视图 data更新View的重点是怎么知道数据变了,只要知道数据变了,那么接下去的事都好处理。Vue2中监听数据变更就是通过Object.defineProperty( ) 对属性设置一个 set 函数,当数据改变了就会来触发这个函数 在函数中执行相应的逻辑。所以我们只要将一些需要更新的方法放在这里面就可以实现data更新view了。
Object.defineProperty( )
set
思路:核心方法就是前文所说的 Object.defineProperty( ), 如果要对所有属性都进行监听的话,那么可以通过递归方法遍历所有属性值,并对其进行 Object.defineProperty( ) 处理。
订阅者Watcher在初始化的时候需要将自己添加进 订阅器Dep 中,那该如何添加呢?我们已经知道 监听器Observer 是在get函数执行了 添加订阅者Wather 的操作的,所以我们只要在 订阅者Watcher 初始化的时候出发对应的 get函数 去执行添加订阅者操作即可,那要如何触发 get 的函数,再简单不过了,只要获取对应的属性值就可以触发了,核心原因就是因为我们使用了Object.defineProperty( )进行数据监听。
订阅器Dep
监听器Observer
添加订阅者Wather
订阅者Watcher
get函数
这里还有一个细节点需要处理,我们只要在 订阅者Watcher 初始化的时候才需要添加订阅者,所以需要做一个判断操作,因此可以在订阅器上做一下手脚:在 Dep.target 上缓存下订阅者,添加成功后再将其去掉就可以了。订阅者Watcher的实现如下:
Dep.target
虽然上面已经实现了一个双向数据绑定的例子,但是整个过程都没有去解析dom节点,而是直接固定某个节点进行替换数据的,所以接下来需要实现一个解析器Compile来做解析和绑定工作。解析器Compile实现步骤:
为了解析模板,首先需要获取到 dom 元素,然后对含有 dom元素上含有指令的节点进行处理,因此这个环节需要对dom操作比较频繁,所有可以先建一个fragment片段,将需要解析的dom节点存入fragment片段里再进行处理:
Vue 2.0的响应式主要用到了Object.defineProperty,我们先来说说这个方法。 Object.defineProperty(obj, prop, descriptor)是用来定义属性描述符的, 它接收三个参数:
Object.defineProperty
Object.defineProperty(obj, prop, descriptor)
{ value: undefined, // 属性的值 get: undefined, // 获取属性值时触发的方法 set: undefined, // 设置属性值时触发的方法 writable: false, // 属性值是否可修改,false不可改 enumerable: false, // 属性是否可以用for...in 和 Object.keys()枚举 configurable: false // 该属性是否可以用delete删除,false不可删除,为false时也不能再修改该参数 }
Vue 3.0的响应式原理跟2.0类似,也是在 get 的时候收集依赖,在 set 的时候更新视图。 但是3.0使用了 es6 的新API Proxy和Reflect,使用 Proxy 相对于 Object.defineProperty 有如下好处:
get
newValue
new Proxy()
Proxy
Vue 数据双向绑定是通过数据劫持 + 发布者-订阅者模式的方式来实现的。
怎么实现数据劫持❓
Object.defineProperty( )是用来做什么的❓
实现思路
vue采用的是MVVM这种架构模式,实现mvvm主要包含两个方面,(1)数据变化更新视图,(2)视图变化更新数据:![image](https://user-images.githubusercontent.com/18101244/177703076-5be66086-1f4e-4555-88d2-579bbc385868.png)
(1)视图变化更新数据: 关键点在于data如何更新view,因为view更新data其实可以通过事件监听即可。比如input标签监听 'input' 事件就可以实现了。所以我们着重来分析下,当数据改变,如何更新视图的。
(2)数据变化更新视图 data更新View的重点是怎么知道数据变了,只要知道数据变了,那么接下去的事都好处理。Vue2中监听数据变更就是通过
Object.defineProperty( )
对属性设置一个set
函数,当数据改变了就会来触发这个函数 在函数中执行相应的逻辑。所以我们只要将一些需要更新的方法放在这里面就可以实现data更新view了。实现过程
实现一个监听器(Observer)
思路:核心方法就是前文所说的
Object.defineProperty( )
, 如果要对所有属性都进行监听的话,那么可以通过递归方法遍历所有属性值,并对其进行Object.defineProperty( )
处理。实现订阅者(watcher)
订阅者Watcher在初始化的时候需要将自己添加进
订阅器Dep
中,那该如何添加呢?我们已经知道监听器Observer
是在get函数执行了添加订阅者Wather
的操作的,所以我们只要在订阅者Watcher
初始化的时候出发对应的get函数
去执行添加订阅者操作即可,那要如何触发 get 的函数,再简单不过了,只要获取对应的属性值就可以触发了,核心原因就是因为我们使用了Object.defineProperty( )进行数据监听。这里还有一个细节点需要处理,我们只要在
订阅者Watcher
初始化的时候才需要添加订阅者,所以需要做一个判断操作,因此可以在订阅器上做一下手脚:在Dep.target
上缓存下订阅者,添加成功后再将其去掉就可以了。订阅者Watcher的实现如下:实现Compile
虽然上面已经实现了一个双向数据绑定的例子,但是整个过程都没有去解析dom节点,而是直接固定某个节点进行替换数据的,所以接下来需要实现一个解析器Compile来做解析和绑定工作。解析器Compile实现步骤:
为了解析模板,首先需要获取到 dom 元素,然后对含有 dom元素上含有指令的节点进行处理,因此这个环节需要对dom操作比较频繁,所有可以先建一个fragment片段,将需要解析的dom节点存入fragment片段里再进行处理:
Vue2.0 的响应式
Object.defineProperty
Vue 2.0的响应式主要用到了
Object.defineProperty
,我们先来说说这个方法。Object.defineProperty(obj, prop, descriptor)
是用来定义属性描述符的, 它接收三个参数:Vue3.0 的响应式
Vue 3.0的响应式原理跟2.0类似,也是在 get 的时候收集依赖,在 set 的时候更新视图。 但是3.0使用了 es6 的新API Proxy和Reflect,使用 Proxy 相对于 Object.defineProperty 有如下好处:
Object.defineProperty
需要指定对象和属性,对于多层嵌套的对象需要递归监听,Proxy可以直接监听整个对象,不需要递归;Object.defineProperty
的get
方法没有传入参数,如果我们需要返回原值,需要在外部缓存一遍之前的值,Proxy的 get 方法会传入对象和属性,可以直接在函数内部操作,不需要外部变量;Object.defineProperty
的 set 方法传入参数只有newValue
,也需要手动将 newValue 赋给外部变量,Proxy 的 set 也会传入对象和属性,可以直接在函数内部操作;new Proxy()
会返回一个新对象,不会污染源原对象Proxy
可以监听数组,不用单独处理数组参考阅读