Vue2 响应式系统之深度响应
作者:windliang 发布时间:2024-04-26 17:40:05
标签:Vue2,响应式,系统,深度,响应
1、场景
import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
text: {
innerText: {
childText: "hello",
},
},
};
observe(data);
const updateComponent = () => {
console.log(data.text.innerText.childText);
};
new Watcher(updateComponent);
data.text.innerText.childText = "liang";
我们的响应式系统到现在还没有支持属性是对象时候的响应,因此我们改变 的时候不会有任何输出。childText
我们只收集了 的依赖,所以如果想要响应的话必须给 整个赋值为一个新对象。data.text
data.text
import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
text: {
innerText: {
childText: "hello",
},
},
};
observe(data);
const updateComponent = () => {
console.log(data.text.innerText.childText);
};
new Watcher(updateComponent);
data.text = {
innerText: {
childText: "liang",
},
};
我们当然不希望每次都赋值整个对象,我们需要做一些修改,把嵌套的对象也变成响应式的。
2、方案
我们只需要在给某个 重写 和 之前,把它的 就像上边给 调用 函数一样,也调用一次 函数即可。key
get
set
value
data
observe
observe
同时提供 参数,留下扩展,让外界决定是否需要深度响应。shallow
/*******************新增 shallow*******************/
export function defineReactive(obj, key, val, shallow) {
/****************************************************/
const property = Object.getOwnPropertyDescriptor(obj, key);
// 读取用户可能自己定义了的 get、set
const getter = property && property.get;
const setter = property && property.set;
// val 没有传进来话进行手动赋值
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}
const dep = new Dep(); // 持有一个 Dep 对象,用来保存所有依赖于该变量的 Watcher
/*******************新增****************************/
!shallow && observe(val);
/******************************************************/
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend();
}
return value;
},
set: function reactiveSetter(newVal) {
const value = getter ? getter.call(obj) : val;
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
dep.notify();
},
});
}
同时,在 函数中,传进来的 不是对象的话我们直接 。observe
value
return
/*
util.js
export function isObject(obj) {
return obj !== null && typeof obj === "object";
}
*/
export function observe(value) {
if (!isObject(value)) {
return;
}
let ob = new Observer(value);
return ob;
}
3、场景2
import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
text: {
innerText: {
childText: "hello",
},
},
};
observe(data);
const updateComponent = () => {
console.log(data.text.innerText.childText);
};
new Watcher(updateComponent);
data.text.innerText.childText = "liang";
data.text = {
innerText: {
childText: "liang2",
},
};
data.text.innerText.childText = "liang3";
可以一分钟想一下上边会输出什么。
new Watcher(updateComponent);
,执行一次 输出 。updateComponent
hello
data.text.innerText.childText = "liang";
,我们已经解决了属性是对象的情况,因此这里也会输出 。liang
data.text = {
innerText: {
childText: "liang2",
},
};
上边代码就是文章最开头的方法,因此也会触发函数执行,输出 。liang2
data.text.innerText.childText = "liang3";
最后这句会执行吗?
答案是否定的了,因为我们的 赋值为了一个新对象,但这个新对象我们并没有将其设置为响应式的。data.text
因此我们需要在 的时候把对象也设置为响应式的。set
/**
* Define a reactive property on an Object.
*/
export function defineReactive(obj, key, val, shallow) {
const property = Object.getOwnPropertyDescriptor(obj, key);
// 读取用户可能自己定义了的 get、set
const getter = property && property.get;
const setter = property && property.set;
// val 没有传进来话进行手动赋值
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}
const dep = new Dep(); // 持有一个 Dep 对象,用来保存所有依赖于该变量的 Watcher
let childOb = !shallow && observe(val);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend();
}
return value;
},
set: function reactiveSetter(newVal) {
const value = getter ? getter.call(obj) : val;
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
/******新增 *************************/
childOb = !shallow && observe(newVal);
/************************************/
dep.notify();
},
});
}
4、总结
通过递归解决了属性是对象的依赖,可以为未来数组的依赖留下基础。
来源:https://vue.windliang.wang/


猜你喜欢
- 最近一直在用TF做CNN的图像分类,当softmax层得到预测结果后,我希望能够看到预测结果,以便和标签之间进行比较。特此补上,以便自己记忆
- 哎~工作忙死了!!!!!!今天在百度老年看到一个手写输入法,颇感新鲜。so把其框下!请不要用在商业用途,学习之用,版权百度所有。看代码!注:
- 如何使用mailto?1)基本用法<a href=mailto:sample@163.com>send em
- php创建JSON数据详解:<?php //创建一个字符数组 $arr=array( 'id'
- 大家知道,在js里encodeURIComponent 方法是一个比较常用的编码方法,但因工作需要,在asp里需用到此方法,查了好多资料,没
- 技巧 16:如果页面需要很长时间才能完成,那么执行前使用 Response.IsClientConnected 如果用户性急,他们可能会在您
- 问题:如何在报表中每隔N行显示一条粗线如何为报表增加一个行号列?回答:1、在设计模式里打开该报表,在报表主体里面加一个TextBox,把Na
- []*int是一个指向指针的切片,本质上是切片,只不过切片里面存放的元素是指针;*[]int是一个指向切片的指针,本质上是指针,可以用*来获
- Django中上传文件方式。如何实现文件上传功能?1创建项目uploadfile:创建app:front项目设置INSTALLED_APPS
- 1、typeof 用来检测数据类型的运算符typeof value 返回值首先是一个字符串,其次里面包含了对应的数据类型,例如:"
- 求3721,163,1,4832,1980,2008,68686688,9999,17173,5173,8848中最大的数明白后,试着求一下
- 今天 大白 问了一个关于CSS权重的问题:关于选择器权重的问题 。class的权重是10 标签权重是 1 。比如说 p span{} 权重是
- 在做数据库修改或删除操作中,可能会导致数据错误,甚至数据库奔溃,而有效的定时备份能很好地保护数据库。本篇文章主要讲述Navicat for
- 用python写了一个简单版本的textrank,实现提取关键词的功能。import numpy as np import jieba im
- Python 操作 MySQL配置win_64Ubuntu14.04Python3.xpip安装pymysql模块直接使用pip安装 pip
- /* *使用方法: * var d = new Drag({id:'dragPannel',maxLeft:500,maxT
- 一、var声明的变量会挂载在window上,而let和const声明的变量不会:var a = 100;console.log(a,wind
- 通过设置全局随机种子使得每次的训练结果相同可以复现def seed_torch(seed=2018): rando
- 什么是list?list 是一个序列!一串数据,这个串可以追加数据。我们可以把它看成大型商场内,儿童游玩串串车,它就像一趟一趟车厢一样,可以
- 原因Blog是一个更新并不很频繁的一套系统,但是每次刷新页面都要更新数据库反而很浪费资源,添加静态页面生成是一个解决办法,同时缓存是一个更好