依赖
需要用到数据库的地方,称为依赖 vue 1 用到dom 是依赖 vue 2 用到数据是的依赖 在getter中收集依赖,在setter中触发依赖
逻辑
数据 => Observe(拆分对象,对数据每个子属性进行观测),每个__ob__实例会有个dep => Dep负责收集依赖和通知全部依赖组件更新,每个引用了此对象的component就是对此数据依赖,每次数据被 【使用-get】 就会 【收集依赖】 , 每次数据被改变,便会通过收到依赖数组subs, 通知notify全部Watcher => Watcher 是个中间媒介,通过Watcher更新组件**【component】**
Observer
拆分对象,对数据每个子属性进行观测(数组单独处理) 每个Observer实例上都要有dep实例
- defineReactive中--创建dep实例
Dep类
专门用来管理依赖,使用发布订阅模式,当数据发生变化时,会循环依赖列表,把所有的Watcher都通知一遍 每一个Observer(用于拆分对象,和observe循环调用)实例都会有一个dep所以,每个对象的每个层级都会有dep(常量没有) ---具体实现
- 设置DEP 实例ID
- 存储当前Watcher的依赖
- 添加订阅-方法
- 添加依赖-方法
- 通知全部Watcher去更新全部-component
Watcher
Watcher是一个中介,data(数据)变化时通过Watcher中转,通知component 保存着Watch内容的回调函数 ---具体实现
初始化
- 设置watcher的id
- 存储传入对象的target,作为系统的targe
- 根据表达式存储当前路径
- 保存回调函数
- 根据表达式[获取数据方法]value
数据更新 获取数据方法
- 调整target指向(全局只有一个)
- 获取数据
- 最终得到数据,并释放出 全局target
其他逻辑处理
- 增强数组方法中处理中,增加对ob的dep 发布订阅(触发依赖)
- defineReactive中get中(收集依赖),判断是否处于数据收集阶段,如果是则 调用当前实例dep 给当前target添加依赖,子元素同样添加依赖
- defineReactive中set中(触发依赖),调用当前实例dep增加发布订阅
源码
// 入口
import observe from './observe'
import Watcher from './Watcher'
/* const obj = {
a: {
b: {
m:5
},
c: 5
},
n: 4,
g: [33,22,55]
} */
const obj = {
a: {
m: {
n: 5
}
},
b: 10,
c: {
d: {
e: {
f: 6666
}
}
},
g: [22, 33, 44, 55]
};
observe(obj)
// obj.a.c = 10
new Watcher(obj, 'a.m.n', (val)=> {
console.log('★★★★★', val)
})
obj.a.m.n = 33
// obj.g.push([33,44])
// console.log(obj.g, '--------pushed-------')
console.log(obj);
// dep.js
let uid = 0;
export default class Dep {
constructor () {
console.log('来自DEP类的构造器')
this.id = uid++;
// 用数组存储自己的订阅者。subs是英语subscribes订阅者的意思。
// 这个数组里面放的是Watcher的实例
this.subs = [];
}
// add添加订阅
addSub(sub) {
this.subs.push(sub)
}
// 添加依赖
depend() {
// Dep.target就是一个我们自己指定的全局的位置,你用window.target也行,只要是全剧唯一,没有歧义就行
if (Dep.target) {
this.addSub(Dep.target);
}
}
// 通知更新
notify() {
console.log('来自DEP-Notify')
// 浅克隆
const subs = this.subs.slice();
// 遍历
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update();
}
}
}
// defineReactive.js
import observe from './observe'
import Dep from './Dep.js'
export default function defineReactive(obj, key, val) {
const dep = new Dep()
if (arguments.length == 2) {
val = obj[key]
}
// 子元素observe,形成多文件互相调用成为递归
let childOb = observe(val);
Object.defineProperty(obj, key, {
// 可枚举
configurable: true,
// 可以被配置,比如可以被delete
enumerable: true,
// getter
get() {
console.log('-----------------访问属性', key, ':', val)
console.log(Dep.target,' ----------------------')
// 如果现在处于依赖收集阶段
if (Dep.target) {
dep.depend();
if (childOb) {
childOb.dep.depend();
}
}
return val
},
// setter
set(newData) {
if (val === newData) {
return
}
console.log('-----------------设置属性', key, ':', newData)
val = newData
// 当设置了新值,这个值页要被observe,防止新值又是个对象
childOb = observe(newData)
// 发布订阅模式,通知dep
dep.notify();
}
});
}
// array.js
import { def } from "./utils"
const arrayPrototype = Array.prototype
// 以Array.prototype 为原型创建对象
export const arrayMethods = Object.create(arrayPrototype)
// 要被改写的7个方法,vue只有此7个列表方法数据会更新 单独改一个值不会出发数据更新
const methodsNeedTochange = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsNeedTochange.forEach(methodName => {
// 备份原有方法
const original = arrayPrototype[methodName];
// 定义新方法
def(arrayMethods, methodName, function () {
// 恢复原来的功能,才能使用 原有方法
const result = original.apply(this, arguments);
// 把类数组对象变为数组
const args = [...arguments];
// 把这个数组身上的__ob__取出来,__ob__已经被添加了,为什么已经被添加了?因为数组肯定不是最高层,比如obj.g属性是数组,obj不能是数组,第一次遍历obj这个对象的第一层的时候,已经给g属性(就是这个数组)添加了__ob__属性。
const ob = this.__ob__;
// 有三种方法push\unshift\splice能够插入新项,现在要把插入的新项也要变为observe的
let inserted = [];
switch (methodName) {
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice':
// splice格式是splice(下标, 数量, 插入的新项)
inserted = args.slice(2);
break;
}
// 判断有没有要插入的新项,让新项也变为响应的
if (inserted) {
// 因为是整个数组已经 实例化过了 拥有了walk,和observeArray 才可以调用
ob.observeArray(inserted);
}
// 发布订阅模式,通知dep
ob.dep.notify();
// pop等方法 要返回值
return result;
}, false)
})
// Watcher.js
import Dep from "./Dep";
let uid = 0;
const parsePath = function (str) {
const segments = str.split('.');
return (obj) => {
// 因为有循环所以会一直查到 传进来表达式的最后一层
for (let i = 0; i < segments.length; i++) {
if (!obj) return;
// console.log(obj, segments, segments[i])
obj = obj[segments[i]]
}
return obj
}
}
export default class Watcher {
constructor (target, expression, callback) {
console.log('Watcher类的构造器')
this.id = uid++;
console.log(target, 'target--------------')
this.target = target;
// 根据表达式,或得器getter 获取到值
this.getter = parsePath(expression)// 此处返回的是 parsePath 返回的函数
this.callback = callback;
this.value = this.get()
}
update() {
this.run();
}
get() {
// 进入依赖收集阶段,让全局Dep.target设置为Watch本身,那么久进入了依赖收集阶段
console.log(this, '--------------this----------')
Dep.target = this;
const obj = this.target
let value;
// 此处返回的是 parsePath 返回的函数,将exp字符串传入 找到值
// 一直找,最终让出target给别的Watcher
try {
value = this.getter(obj)
} finally {
Dep.target = null
}
return value
}
// 数据更新时,调用run
run() {
console.log('watch---------update-------run------')
this.getAndInvoker(this.callback)
}
// 得到并唤起
getAndInvoker(cb){
const value = this.get()
if (value !== this.value || typeof value == 'object') {
const oldValue = this.value
this.value = value
cb.call(this.target, value, oldValue)
}
}
}
// Observer.js
import { def } from './utils'
import defineReactive from './defineReactive'
import { arrayMethods } from './array'
import observe from './observe'
import Dep from './Dep'
export default class Observer {
constructor(value) {
// 每一个Observer的实例身上都要有dep
this.dep = new Dep()
// 给实例增加了一个不可枚举的__ob__
def(value, '__ob__', this, false)
// console.log('Observer构造器', value)
// Observer类的目的是:将一个正常的object转换为每个层级的属性都是响应式(可以被侦测的)的object
if (Array.isArray(value)) {
// 将数组的原型,指向arrayMethods
Object.setPrototypeOf(value, arrayMethods)
// 数组逐项observe,非数组,子元素已经在defineReactive,中处理了子项目,而数组,则需单独处理
this.observeArray(value);
} else {
this.walk(value)
}
}
// 遍历
walk(value) {
for (let i in value) {
defineReactive(value, i)
}
}
observeArray(arr) {
for (let i = 0, l = arr.length; i < l; i++) {
// 逐个进行observe
observe(arr[i])
}
}
}
// observe.js
import Observer from './Observer'
export default function (value) {
// 如果不是对象 返回
if (typeof value != 'object') return
// 定义ob
let ob;
if ('__ob__' in value) {
ob = value.__ob__;
} else {
ob = new Observer(value)
}
return ob
}
// utils.js
export function def(obj, key, value, enumerable) {
Object.defineProperty(obj, key, {
configurable: true,
enumerable,
writable: true,
value
});
}