vue2中$set的原理_它对object属性做了啥?
创始人
2025-05-30 23:59:23

$set的作用

    • 背景
      • 动态添加属性,不使用$set
      • 动态添加属性,使用$set
    • $set做了啥?
      • 对象defineProperty
      • 对象的Getter和Setter
      • $set的源码
        • notify 触发update更新
      • 结束

背景

动态修改对象的属性时,vue2视图template不会跟新渲染的数据,来一探究竟

动态添加属性,不使用$set

测试案例:
config属性被我动态添加name:yma16
结论:
如果template动态添加则说明对象的属性添加删除具有响应式

效果
如下图所示。
在这里插入图片描述
结论:
vue2动态添加对象属性不具有响应式

动态添加属性,使用$set

改动点:
submit使用$set添加替换属性


效果
在这里插入图片描述
结论:
vue2动态添加对象属性使用$set可以让对象具有响应式渲染

$set做了啥?

对象defineProperty

给对象添加属性

var myObject = {};
Object.defineProperty( myObject, "a", {
value: 2,
writable: false, // 不可写!
configurable: true,
enumerable: true
} );
myObject.a = 3;
myObject.a; // 2

对象的Getter和Setter

object获取和配置属性时
定义新属性b设置为2的两倍达到监听变量啊变化的结果,只有a变量地址改变时属性b也会改变

var myObject = {
// 给 a 定义一个 getter
get a() {
return 2;
}
};
Object.defineProperty(
myObject, 
"b", 
{
get: function(){ return this.a * 2 },
enumerable: true
}
);
myObject.a; // 2
myObject.b; // 4

$set的源码

vue的原型链挂了set
set里面定义属性且可枚举
使用了枚举enumerable属性和注册configurable属性

/*** Define a reactive property on an Object.*/
export function defineReactive(obj: object,key: string,val?: any,customSetter?: Function | null,shallow?: boolean,mock?: boolean
) {const dep = new Dep()const property = Object.getOwnPropertyDescriptor(obj, key)if (property && property.configurable === false) {return}// cater for pre-defined getter/settersconst getter = property && property.getconst setter = property && property.setif ((!getter || setter) &&(val === NO_INIITIAL_VALUE || arguments.length === 2)) {val = obj[key]}let childOb = !shallow && observe(val, false, mock)Object.defineProperty(obj, key, {enumerable: true,configurable: true,get: function reactiveGetter() {const value = getter ? getter.call(obj) : valif (Dep.target) {if (__DEV__) {dep.depend({target: obj,type: TrackOpTypes.GET,key})} else {dep.depend()}if (childOb) {childOb.dep.depend()if (isArray(value)) {dependArray(value)}}}return isRef(value) && !shallow ? value.value : value},set: function reactiveSetter(newVal) {const value = getter ? getter.call(obj) : valif (!hasChanged(value, newVal)) {return}if (__DEV__ && customSetter) {customSetter()}if (setter) {setter.call(obj, newVal)} else if (getter) {// #7981: for accessor properties without setterreturn} else if (!shallow && isRef(value) && !isRef(newVal)) {value.value = newValreturn} else {val = newVal}childOb = !shallow && observe(newVal, false, mock)if (__DEV__) {dep.notify({type: TriggerOpTypes.SET,target: obj,key,newValue: newVal,oldValue: value})} else {dep.notify()}}})return dep
}

重点
当一个数据为响应式时,vue会给该数据添加一个__ob__属性,因此可以通过判断target对象是否存在__ob__属性来判断target是否是响应式数据

        dep.notify({type: TriggerOpTypes.SET,target: obj,key,newValue: newVal,oldValue: value})} else {dep.notify()}

当target对象是响应式数据时,我们将target的属性key也设置为响应式并手动触发通知其属性值的更新

defineReactive(ob.value, key, val)
ob.dep.notify()

在这里插入图片描述
在这里插入图片描述

notify 触发update更新

承接$set的notify

  notify(info?: DebuggerEventExtraInfo) {// stabilize the subscriber list firstconst subs = this.subs.filter(s => s) as DepTarget[]if (__DEV__ && !config.async) {// subs aren't sorted in scheduler if not running async// we need to sort them now to make sure they fire in correct// ordersubs.sort((a, b) => a.id - b.id)}for (let i = 0, l = subs.length; i < l; i++) {const sub = subs[i]if (__DEV__ && info) {sub.onTrigger &&sub.onTrigger({effect: subs[i],...info})}sub.update()}}

update 继承 DebuggerOptions
在这里插入图片描述

/*** @internal*/
export interface DepTarget extends DebuggerOptions {id: numberaddDep(dep: Dep): voidupdate(): void
}

onTrack和onTrigger在这里插入图片描述

watch update
_update更新渲染节点

function _update(oldVnode, vnode) {const isCreate = oldVnode === emptyNodeconst isDestroy = vnode === emptyNodeconst oldDirs = normalizeDirectives(oldVnode.data.directives,oldVnode.context)const newDirs = normalizeDirectives(vnode.data.directives, vnode.context)const dirsWithInsert: any[] = []const dirsWithPostpatch: any[] = []let key, oldDir, dirfor (key in newDirs) {oldDir = oldDirs[key]dir = newDirs[key]if (!oldDir) {// new directive, bindcallHook(dir, 'bind', vnode, oldVnode)if (dir.def && dir.def.inserted) {dirsWithInsert.push(dir)}} else {// existing directive, updatedir.oldValue = oldDir.valuedir.oldArg = oldDir.arg// update 更新 vnodecallHook(dir, 'update', vnode, oldVnode)if (dir.def && dir.def.componentUpdated) {dirsWithPostpatch.push(dir)}}}if (dirsWithInsert.length) {const callInsert = () => {for (let i = 0; i < dirsWithInsert.length; i++) {callHook(dirsWithInsert[i], 'inserted', vnode, oldVnode)}}if (isCreate) {mergeVNodeHook(vnode, 'insert', callInsert)} else {callInsert()}}if (dirsWithPostpatch.length) {mergeVNodeHook(vnode, 'postpatch', () => {for (let i = 0; i < dirsWithPostpatch.length; i++) {callHook(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode)}})}if (!isCreate) {for (key in oldDirs) {if (!newDirs[key]) {// no longer present, unbindcallHook(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy)}}}
}

update的调用地方太多
找了一个监听的update

/*** @internal since we are not exposing this in Vue 2, it's used only for* internal testing.*/
export function effect(fn: () => any, scheduler?: (cb: any) => void) {const watcher = new Watcher(currentInstance, fn, noop, {sync: true})if (scheduler) {watcher.update = () => {scheduler(() => watcher.run())}}
}

在这里插入图片描述

结束

感谢阅读,如有不足或者错误欢迎指正!
在这里插入图片描述

相关内容

热门资讯

Mysql常用数据类型总结 整形 枚举类型ENUE整形       TINYINT,SMALLINT,MEDIUMINT,IN...
【flink sql】创建表 flink sql创建表语法 CREATE TABLE [IF NOT EXISTS] [catal...
python opencv 保... 👨‍💻个人简介: 深度学习图像领域工作者 dz...
Pytorch深度学习实战3-... 目录1 数据集Dataset2 数据加载DataLoader3 常用预处理方法4 模型处理5 实例&...
自定义类型的超详细讲解ᵎᵎ了解...   目录 1.结构体的声明 1.1基础知识 1.2结构体的声明 1.3结构体的特殊声明  1.4结构...
Docker等容器技术如何与移... 移动应用程序的开发面临着很多挑战,包括开发环境的设置、测试的困难、部署的复杂性等。由于...
【微服务】—— Nacos安装... 文章目录1. Windows安装1.1 下载安装包1.2 解压1.3 端口配置1.4 启动1.5 访...
【OpenGL】 为了理解这个函数我们需要先学习一些OpenGL的内容 OpenGL可视化 https://g...
hjr-详细说一下Redis集... Redis作用 缓存 一般我们用Redis做缓存,热点数据 击穿:访问到...
【蓝桥杯】 C++ 数字三角形... 文章目录题目描述输入描述输出描述实现代码解题思路注意点知识点 题目描述 上图给出了一个数字三角形。从...
VR全景展会丨探索未来,重塑现... 随着科技的不断发展,虚拟现实(VR)技术逐渐成为一个重要的...
C++数据类型 目录 C++基础数据类型 指针 指针类型 指针赋值 引用 参考:《深...
超实用!!! 三分钟将你的项目... 文章目录前言一、在项目中新增配置二、配置github page setting?三、如...
数据结构---队列 专栏:数据结构 个人主页:HaiFan. 专栏简介:这里是...
数字操作方法 系列文章目录 前端系列文章——传送门 JavaScript系列文章——传送门 文章目录系列文章目录...
Cartesi 2023 年 ... 查看 Cartesi Machine、Cartesi Rollups 和 Noether 的更新正在...
JavaWeb——jsp概述入... JSP定义:  在如下一个jsp文件里面有如下的代码  <%@ page content...
一切喜怒哀乐都来自于你的认知 01 有个学子,准备出国,父母请来清华的教授宁向东。请问教授࿱...
JAVA并发编程——synch... 引言         Java语言为了解决并发编程中存在的原子性、可见性和有序性问题,...
git学习----3.21 未... 文章目录前言Git :一个分布式版本控制工具目标一、概述1.1 开发中的实际场景1.2...
Qt优秀开源项目之十七:QtP... QtPromise是Promises/A+规范的Qt/C++实现。该规范的译...
【前端八股文】JavaScri... 文章目录Set概念与arr的比较属性和方法并集、交集、差集Map概念属性和方法String用索引值和...
海康硬盘录像机接入RTSP/o... EasyNVR安防视频云服务平台可支持设备通过RTSP/Onvif协议接入平台,能提供...
在混合劳动力时代如何避免网络安... 在混合劳动力时代如何避免安全网络风险 三年多来,混合工作一直是工作生活中不可或缺的一...
2023还不懂Jmeter接口... 这里介绍的Jmeter接口测试的的实战,如果文章内容没遇看懂的话,我这边...
基于4G/5G弱网聚合的多链路... 基于4G/5G多卡聚合(弱网聚合)的智能融合通信设备技术亮点 增强带宽提供可靠连接 通过将多个有线和...
如何使用Synplify综合v... 文章目录使用Synplify综合的好处synplify的教程方法1(无效)...
2023年全国最新高校辅导员精... 百分百题库提供高校辅导员考试试题、辅导员考试预测题、高校辅导员考试真题、辅导员证考试题库等ÿ...
2022年18个值得期待的Le... 有数百个独特的LearnDash附加组件,您可能很难选择您的LearnDash LMS...
【java基础】Stream流... 文章目录基本介绍流的创建流的各种常见操作forEach方法filter方法map方法peek方法fl...