1、Object 数据监测
1、如何追踪变化 ?
vue 2.x 使用 Object.defineProperty vue 3.x 使用 Proxy
function defineReaction(data, key, val) {
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
// 从data的key读取数据 get触发
get: function () {
return val
},
//从data的key设置数据 set 触发
set: function (newValue) {
if (val === newValue) {
return
}
val = newValue
}
})
}
2、如何收集依赖 ?
//需要知道那些地方使用了数据 使用getter收集依赖
//数据更改时 向使用数据的地方通知数据改变 setter触发依赖
// 先通知到 数据对应的组件 组件内部 使用 虚拟dom 重新渲染
//
3、收集依赖放在哪里?
// 放在数组 dep中 存储被收集的依赖
//收集依赖
export default class Dep {
constructor() {
this.subs = []
}
addSub(sub) {
this.subs.push(sub)
}
removeSub(sub) {
remove(this.subs, sub)
}
depend() {
if (window.target) {
this.addSub(window.target)
}
}
notify() {
const subs = this.subs.slice()
for (let i = 0; l = subs.lengthli < l; i++) {
subs[i].update()
}
}
}
function remove(arr, item) {
if (arr.length) {
const index = arr.indexOf(item)
if (index > -1) {
return arr.splice(index, 1)
}
}
}
// 有了 Dep类 改造一下之前的代码
function defineReaction(data, key, val) {
let dep = new Dep() //修改
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
// 从data的key读取数据 get触发
get: function () {
dep.depend() //修改
return val
},
//从data的key设置数据 set 触发
set: function (newValue) {
if (val === newValue) {
return
}
val = newValue
dep.notify() //新增
}
})
}
2、收集
1、收集谁 ? Watcher
//先说明原理
先把自己设置到全局唯一指定位置 这里设置的是window.target,读取数据,
读取之后触发这个数据的getter,getter找到全局唯一指定位置读取正在读数据的
watcher,把这个watcher收集到 dep中,watcher实现主动订阅数据变化
// 首先说明 思想是这样的 将收集到的依赖封装到一个类的实例
通知时 只通知这一个 再由它通知其他地方
// 为什么要这样设计 ? 数据使用的地方很多 可能是模版也可能写在 watch中
//简化模型 data.a.b.c属性改变时 触发第二个回调函数
vm.$watch('a.b.c',function(newVal,oldVal){
// 做点事情
})
// 将watcher实例 添加到data.a.b.c属性到Dep中就行
当data.a.b.c值变化时 通知 watcher 再执行回调函数
export default class Watcher{
constructor(vm,ex,cb){
this.vm = vm
// 执行 this.getter() 可读取data.a.b.c内容
this.getter = parsePath(ex)
this.cb = cb
this.value = this.get()
}
get(){
window.target = this
let value = this.getter.call(this.vm,this.vm)
window.target = undefined
return value
}
update(){
const oldValue = this.value
this.value = this.get()
this.cb.call(this.vm,this.value,oldValue)
}
}
3、递归
1、貌似缺了东西,这个是监听某一个属性 ,我想实现监听所有的属性
//需要使用递归来监听所有的key
// 递归侦测 所有key
export class Observer {
constructor(value) {
this.value = value;
if (!Array.isArray(value)) {
this.walk(value)
}
}
// walk将每个属性转换成 getter/setter形式来侦测变化
// 只有当数据类型为 Object时 被调用
walk(obj) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i], obj[keys[i]])
}
}
}
function defineReaction(data, key, val) {
//新增递归子属性
if (typeof val === 'object') {
new Observer(val)
}
let dep = new Dep() //修改
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
// 从data的key读取数据 get触发
get: function () {
dep.depend() //修改
return val
},
//从data的key设置数据 set 触发
set: function (newValue) {
if (val === newValue) {
return
}
val = newValue
dep.notify() //新增
}
})
}
4、Object的问题
//添加属性
//删除属性 也不能监测到
// 有没有 尝试想一下 为什么?
vue.js Oject.defineProperty 将对象到key 转换成
getter/setter形式追踪变化(等下 我们再展开说一下 这个事情)
,但是getter/setter只能追踪一个数据是否被修改,新增和删除无法追踪到(是不是就无解了?)
1、将对象到key 转换成 getter/setter形式追踪变化 回想之前到 get 和 set 函数 ,dep数组
再到后来到 watch 函数 是怎么起作用到
2、新增和删除无法追踪怎么解决?
vuejs 未使用Proxy之前 有 vm.$set vm.$delete 这两个API
本节完,希望有收获