2020年11月20日
响应式原理(1)
组件初始化的时候,先给每一个Data属性都注册getter,setter,也就是reactive化。然后再new 一个自己的Watcher对象,此时watcher会立即调用组件的render函数去生成虚拟DOM。在调用render的时候,就会需要用到data的属性值,此时会触发getter函数,将当前的Watcher函数注册进sub里
当data属性发生改变之后,就会遍历sub里所有的watcher对象,通知它们去重新渲染组件。

- Data属性通过Object.defineProperty注册getter,setter
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性。其中包含三个参数:
obj
要定义属性的对象(数据)。prop
要定义或修改的属性的名称或Symbol
。descriptor
要定义或修改的属性描述符。
descriptor属性描述符为对象可选键值如下:
- configurable:该属性的
configurable
键值为true
时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除 - enumerable:当该属性的
enumerable
键值为true
时,该属性才会出现在对象的枚举属性中(可以理解为使用for…in遍历是是否能访问到键) - Writable:设置属性是否是可写的。Writable和set、get不可同时使用
- get:当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入
this
对象(由于继承关系,这里的this
并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。 - set:当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的
this
对象。
var data = { name:'张三', age:12, code:[ {name:'语文'}, {name:'数学'} ] } // 简化版本 function defineReactive(target, key, value, enumerable){ // value 为函数内部局部作用域(闭包,解决数据访问安全问题) if(typeof value === 'object' && value!= null && !Array.isArray(value)){ reactify(value) } Object.defineProperty(target, key, { configurable: true,//设置属性是否可以被删除,属性是否可更改 // Writable:设置属性是否是可写的。Writable和set、get同时使用 enumerable: !!enumerable,//是否可枚举(使用for...in遍历是是否能访问到键) set(newValue){ value = newValue; }, get(){ return value; } }) } function reactify(data){ let keys = Object.keys(data); for(var i=0;i<keys.length;i++){ let key = keys[i]; let val = data[key]; if(Array.isArray(val)){ // 数组类型 for(var j=0;j<val.length;j++){ reactify(val[j]); } }else{ // 引用类型 defineReactive(data, key, val, true); } } } reactify(data);