var obj={
a:{
m:{
n:5
}
},
b:4
}
index.js
import defeineReactive from "./defeineReactive"
import Observer from "./Observer"
import observe from "./observe"
import Watcher from "./Watcher"
var obj = {
a: {
b: {
c: 5
}
},
b: [1, 2, 3, 4]
}
var obj2={
a:{
b:{
c:1212
}
}
}
// 创建observe函数,注意函数的名字没有r
observe(obj2)
new Watcher(obj2,'a.b.c',(val)=>{
console.log('星',val)
})
obj2.a.b.c = 88
observe.js (看obj身上有没有__ob__实例)
import Observer from "./Observer";
export default function observe(value) {
// 如果value不是对象,什么都不做
if(typeof value !== 'object') return value;
// 定义ob
var ob;
if(typeof value.__ob__!=='undefined'){
ob = value.__ob__;
}else{
ob = new Observer(value);
}
return ob;
}
Observer.js (将产生的实例添加到__ob__上),通过walk遍历obj对象,这样a,b就变成响应式
import { def } from './utils'
import defeineReactive from './defeineReactive'
import { arrayMethods } from './array'
import observe from './observe'
//Observer类的目的是:将一个正常的object转换为每个层级的属性都是响应式(可以被侦测的)
export default class Observer {
constructor(value) {
console.log('我是构造器', value)
// 给实例(this,一定要注意,构造函数中的this不是表示类本身,而是表示实例)添加了__ob__属性,值是这次new的实例
def(value, '__ob__', this, false)
if (Array.isArray(value)) {
// 如果是数组,要非常强行的蛮干:将这个数组的原型,指向arrayMethods
Object.setPrototypeOf(value, arrayMethods);
//让这个数组变的observe
this.observerArray(value)
} else {
this.walk(value)
}
}
// 遍历
walk (value) {
for (let k in value) {
defeineReactive(value, k)
}
}
// 数组的特殊遍历
observerArray (arr) {
for (let i = 0, l = arr.length; i < l; i++) {
// 逐渐进行observe(响应式)
observe(arr[i])
}
}
}
util.js (遍历工具函数)
// enumerable是否可枚举
// writable是否可写
//configurable是否可删除
export const def = function(obj,key,value,enumerable){
Object.defineProperty(obj,key,{
value,
enumerable,
writable:true,
configurable: true
})
}
obj ————对象
key————属性名
value————值
enumerable————是否可枚举
defineReactive.js
import observe from "./observe"
import Dep from './Dep'
export default function defeineReactive(data,key,value) {
if(arguments.length==2){
value = data[key]
}
const dep =new Dep();
// 子元素要进行observe,至此形成了递归,这个递归不是函数自己调用自己,而是多个函数,类循环调用
let childOb = observe(value)
Object.defineProperty(data,key,{
enumerable:true,
configurable:true,
get(){
console.log(Dep.target,"asdqwe")
// 如果现在处理依赖收集阶段
if(Dep.target){
dep.depend()
if(childOb.dep){
childOb.dep.depend()
console.log(childOb.dep,"获取")
}
}
return value
},
set(newValue){
if(newValue===value){
return;
}
value = newValue
childOb = observe(newValue)
dep.notify()
}
})
}
数组响应式:
array.js
import { def } from './utils'
import observerArray from './Observer'
import Dep from './Dep'
const arrayPrototype = Array.prototype
//以Array.prototype为原型创建arrayMethods对象
export const arrayMethods = Object.create(arrayPrototype)
//要被改写的7个数组方法
const methodsNeedChange = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
];
methodsNeedChange.forEach(methodName => {
//备份原来的方法,因为push、pop等7个函数的功能不能被剥夺
const orginal = arrayPrototype[methodName]
// 定义新的方法
def(arrayMethods, methodName, function () {
//把这个数组身上的__ob__取出来,__ob__已经被添加了,为什么已经被添加了?因为数组肯定不是最高层,比如obj.g属性是数组,obj不能是数组,第一次遍历obj这个对象的第一层的时候,已经给g属性(就是这个数组)添加了__ob__属性。
const ob = this.__ob__;
// 恢复原来的功能
const result = orginal.apply(this, arguments);
// 有三种方法push\unshift\splice能够插入新项,因为要将插入的新项也变成相应式的,现在要把插入的新项也要变为observe
let inserted = []
const args = [...arguments]
switch (methodName) {
case 'push':
case 'unshift':
inserted = args
break;
case 'splice':
//splice格式是splice(下标,数量,插入的新项)
inserted = args.splice(2)
break;
}
//判断有没有要插入的新项,让新项的每一项也变为响应的
if (inserted) {
ob.observerArray(inserted)
}
ob.dep.notify()
return result;
}, false)
})
具体代码如下:
Dep.js
var uid = 0
export default class Dep{
constructor(){
this.id = uid++
// 用数组存储存储自己的订阅者。subs是英语subscribes订阅者的意思
// 这里放的是Watcher的实例
this.subs = []
}
// 添加订阅
addSub(sub){
this.subs.push(sub)
}
// 添加依赖Watcher
depend(){
// Dep.target就是一个我们自己指定的全局的位置,你用window.target也行,只要是全局唯一的
if(Dep.target){
this.addSub(Dep.target)
}
}
// 通知更新
notify(){
console.log("通知")
// 浅客隆一份
const subs = this.subs.slice()
// 遍历
for(let i=0,l=subs.length;i<l;i++){
subs[i].update();
}
}
}
Watch.js
import Dep from "./Dep"
var uid= 0
export default class Watcher{
// 一个对象,一个表达式,一个回调函数
constructor(target,expression,callback){
console.log("我是watcher构造器")
this.id = uid++
this.target =target
this.getter = parsePath(expression) //获得器
this.callback = callback
this.value = this.get()
}
update(){
this.run()
}
get(){
// 进入依赖收集阶段,让全局的Dep.target设置为Watcher本身,那么就是进入依赖收集
Dep.target = this
const obj = this.target
var value
try {
value = this.getter(obj)
}finally {
Dep.target = null
}
return value
}
run(){
this.getAndInvoke(this.callback)
}
// 得到并唤起
getAndInvoke(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)
}
}
}
function parsePath(str){
var segments = str.split('.')
return (obj) =>{
for(let i=0;i<segments.length;i++){
if(!obj) return
obj = obj[segments[i]]
}
return obj
}
}
总结:
数据更新后会执行Object.definePropery()进行更新,然后调用Observer,每个Observer下面有dep,dep的subs负责收集依赖depend(其实就是Watch),当数据发生变化时,Dep就会通知Watch,然后对Watch进行更新操作。