(更新中)
数据响应式原理
将数据变为响应式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
export default function defineReactive(data, key, val) { if (arguments.length === 2) { val = data[key] } Object.defineProperty(data, key, { enumerable: true, configurable: true, get() { console.log('访问 obj 的' + key + '属性') return val }, set(newValue) { console.log('改变 obj 的' + key + '属性', newValue) if (val === newValue) return val = newValue } }) }
|
递归侦测对象全部属性
若对象层级深,深层对象并不是响应式的,需要递归对象的全部属性
创建 Observer 类 将一个 object 转换为每个层级的属性都是响应式的
入口文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import observe from './observe.js'
let obj = { a: 1, b: { c: 2 } }
observe(obj)
obj.b.c = 5 console.log(obj.b.c)
|
observe 辅助函数
作用: 1.判断递归出口 2.避免循环引用反复创建 Observer 实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
import Observer from "./Observer"
export default function (value) { if (typeof value !== 'object') return let ob if (typeof value.__ob__ !== 'undefined') { ob = value.__ob__ } else { ob = new Observer(value) } return ob }
|
Observer 类
给 value __ob__ 属性, 遍历所有属性并调用 defineReactive 使它们变为响应式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
import { def } from './utils.js' import defineReactive from './defineReactive'
export default class Observer { constructor(value) { def(value, '__ob__', this, false) console.log('Observer 构造器', value) this.walk(value) }
walk(value) { for (let k in value) { defineReactive(value, k) } } }
|
defineReactive 函数
让 data 变为响应式, 对每个 val 调用 observe, 开启递归
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
import observe from './observe.js'
export default function defineReactive(data, key, val) { console.log('我是 defineReactive', data, key) if (arguments.length === 2) { val = data[key] }
let childOb = observe(val)
Object.defineProperty(data, key, { enumerable: true, configurable: true, get() { console.log('访问' + key + '属性') return val }, set(newValue) { console.log('改变' + key + '属性', newValue) if (val === newValue) return val = newValue childOb = observe(newValue) } }) }
|
辅助函数
重写下 Object.defineProperty, 将 enumerable 变为参数
1 2 3 4 5 6 7 8 9 10 11
|
export const def = function(obj, key, value, enumerable) { Object.defineProperty(obj, key, { value, enumerable, writable: true, configurable: true }) }
|
整个流程
1 2 3 4 5 6 7 8 9 10 11 12 13
| 执行observe(obj) ├── new Observer(obj),并执行this.walk()遍历obj的属性,执行defineReactive() ├── defineReactive(obj, a) ├── 执行observe(obj.a) 发现obj.a不是对象,直接返回 ├── 执行defineReactive(obj, a) 的剩余代码(定义obj的a属性的响应式) ├── defineReactive(obj, b) ├── 执行observe(obj.b) 发现obj.b是对象 ├── 执行 new Observer(obj.b),遍历obj.b的属性,执行defineReactive() ├── 执行defineReactive(obj.b, c) ├── 执行observe(obj.b.c) 发现obj.b.c不是对象,直接返回 ├── 执行defineReactive(obj.b, c)的剩余代码(定义obj.b的c属性的响应式) ├── 执行defineReactive(obj, b)的剩余代码(定义obj的b属性的响应式) 代码执行结束
|
递归
不是自己调用自己的递归,还是三个函数相互调用的循环
当传入 observe 的值不是对象时跳出递归
数组的响应式处理
为什么需要处理数组的响应式
正因为我们可以通过Array原型上的方法来改变数组的内容,所以 object 那种通过getter/setter的实现方式就行不通了。
ES6 之前没有提供可以拦截原型方法的能力,我们可以用自定义的方法去覆盖原生的原型方法。
Vue 是通过改写数组的七个方法(可以改变数组自身内容的方法)来实现对数组的响应式处理
这些方法分别是:push、pop、shift、unshift、splice、sort、reverse
处理思路
这七个方法都是定义在 Array.prototype上,要保留方法的功能,同时增加数据劫持的代码
思路就是 以 Array.prototype为原型,创建一个新对象 arrayMethods
然后在新对象arrayMethods上定义(改写)这些方法
定义 数组 的原型指向 arrayMethods
这就相当于用一个拦截器覆盖 Array.prototype,每当使用Array原型上的方法操作数组时,其实执行的是拦截器中提供的方法。在拦截器中使用原生Array的原型方法去操作数组。
重写数组方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| import { def } from './utils.js'
const arrayPrototype = Array.prototype
export const arrayMethods = Object.create(arrayPrototype)
const methodsNeedChange = [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ]
methodsNeedChange.forEach(methodName => { const original = arrayPrototype[methodName]
def(arrayMethods, methodName, function() { const result = original.apply(this, arguments)
const ob = this.__ob__
let inserted = [] switch(methodName) { case 'push': case 'unshift': inserted = arguments break case 'splice': inserted = [...arguments].slice(2) break }
if (inserted.length) { ob.observeArray(inserted) }
console.log('啦啦啦') return result }, false) })
|
observeArray
如果是数组的话走 observeArray 来遍历所有键;否则走 walk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import { def } from './utils.js' import defineReactive from './defineReactive.js' import { arrayMethods } from './array.js' import observe from './observe.js'
export default class Observer { constructor(value) { def(value, '__ob__', this, false)
if (Array.isArray(value)) { Object.setPrototypeOf(value, arrayMethods) this.observeArray(value) } else{ this.walk(value) } }
walk(value) { for (let k in value) { defineReactive(value, k) } }
observeArray(arr) { for (let i = 0, l = arr.length; i < l; i++) { observe(arr[i]) } } }
|
依赖
Dep 类
依赖收集,用来管理依赖
每个 Observer 的实例成员中都有一个 Dep 的实例
Watcher 类
中介,数据发生
参考
https://www.bilibili.com/video/BV1G54y1s7xV/?spm_id_from=333.788.recommend_more_video.0&vd_source=1616b746cefe2e7615c229563ba38eb4
https://juejin.cn/post/6932659815424458760
https://blog.csdn.net/weixin_44972008/article/details/115922118