vue2 Object.definProperty响应式原理(面试题)

news2024/11/26 4:32:24

注意:
响应式原理和双向数据绑定原理是两回事,一般面试官会先问响应式原理再问双向数据绑定的原理
详细文章

1.响应式原理

核心是数据劫持和依赖收集,是通过数据劫持结合发布者-订阅者模式的方式来实现的。通过Object.defineProperty()为对象添加属性,然后为对象设置getter和setter方法,之后我们每次通过点语法获取属性就会执行getter方法,在这个方法中我们会把调用 此属性的依赖收集到一个集合中;而在我们给属性赋值(修改属性)时,会触发setter方法,然后去通知集合中的依赖更新,做到数据变更 驱动视图变更。

Object.defineProperty(obj, key, {
	// writable:true,// 可写
  	// enumerable: true, // 可枚举
  	// configurable: true, //可删除
    //拦截get,当我们访问data.key时会被这个方法拦截到
    get () {
        //我们在这里收集依赖
        return obj[key];
    },
    //拦截set,当我们为data.key赋值时会被这个方法拦截到
    set (newVal) {
        //当数据变更时,通知依赖项变更UI
    } 
})

2. MVVM模式

MVVM框架的的核心就是双向绑定, 其原理是通过数据劫持+发布订阅模式相结合的方式来实现的,简单来说就是数据层发生变化的时候,可同步更新视图层,当视图层发生变化的时候,同步更新数据层

  • MVVM将数据双向绑定作为核心思想,View 和 Model之间没有联系,他们通过ViewModel这个桥梁进行交互
  • Model 和 ViewModel 之间的交互是双向的,因此View 的变化会自动同步到Model,而Model的变化也会立即反映到View上显示
  • 当用户操作View , ViewModel 感知到变化,然后通知Model发生相应改变;反之当Model发生改变,ViewModel也能感知到变化,使View作出相应更新

在这里插入图片描述

3. 如何实现?

Vue 类

首先我们实现一个 Vue 类,用于创建 vue对象,它的构造方法接收一个options参数,用于初始化vue

class Vue {
	constructor(options){
		this.$el = options.el;
		this._data = options.data;
		this.$data = this._data
		// 对data进行响应式处理
		new Observe(this._data)
	}
}

// 创建 vue 对象
new Vue ({
	el:'#app',
	data:{
		message:'hello world'
	}
})

上面的代码中我们首先创建了一个Vue的类,构造函数跟我们平时使用的Vue大致一致,为了容易理解我们这里只处理了参数el和data。
我们发现构造函数的最后一行创建了一个Observe类的对象,并传入data作为参数,这里的Observe就是对data数据进行响应式处理的类,接下来我们看一下Observe类的简单实现。

Observe类

(数据监听器:能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者)

我们在Oberve类中实现对data的监听,就是通过Object.defineProperty()方法实现的数据劫持,代码如下:

class Observe {
	constructor(data){
		// 如果传入的数据是object
		if(typeof data == 'object'){
			this.walk(data);
		}
	}

	// 这个方法遍历对象中的属性,并依次对其进行响应式处理
	walk(obj){
		// 获取所有属性
		const keys = Object.keys(obj);
		for ( let i = 0; i < keys.length; i++ ){
			// 对所有属性进行监听(数据劫持)
			this.defineReactive(obj,keys[i])
		}
	}

	defineReactive(obj,key){
		if (typeof obj[key] == 'object'){
			// 如果属性是对象,那么递归调用walk方法
			this.walk(obj[key]);
		}
		const dep = new Dep(); // Dep类用于收集依赖
		const val = obj[key];
		Object.defineProperty(obj,key,{
			enumerable:true,
			configurable:true,
			// get代理将Dep.target 即watcher对象添加到依赖集合中
			get(){
				// 这里在创建watcher对象时 会给Dep.target赋值
				if(Dep.target){
					dep.addSubs(Dep.target);
				}
				return val;
			},
			set(newVal){
				val = newVal;
				// 依赖的变更响应
				dep.notify(newVal);
			}
		})
	}
}

上述代码中我们使用到了Dep类,我们在劫持到的数据的get方法中收集到的依赖会被放到Dep类中保存。

Dep类

订阅器:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observe 和 订阅者 Watcher 进行统一管理。

下面代码是Dep类的实现,他有一个subs的数组,用于保存依赖,这里的依赖是我们后面要定义的Watcher,Watcher即观察者

class Dep (){
	static target = null
	constructor(){
		this.subs=[];
	}
	addSubs(watcher){
		this.subs.push(watcher)
	}
	notify(newVal){
		for(let i = 0; i < this.subs.length; i++){
			this.subs[i].update(newVal);	
		}
	}
}

Watcher类

作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图

观察者类,它做的事情就是观察数据的变更,它会调用data中对应属性的get方法触发依赖收集,并在数据变更后执行相应的更新

let uid = 0;
class Watcher{
	// vm即一个vue对象,key要观察的属性,cb是观测到数据变化后需要做的操作,通常是指dom变更
	constructor(vm,key,cb){
		this.vm = vm;
		this.uid = uid++;
		this.cb = cb;
		 // 调用get触发依赖收集之前,把自身赋值给Dep.taget静态变量
		 Dep.target=this;
		 // 触发对象上代理的get方法,执行get添加依赖
		 this.value=vm.$data[key];
		 //用完即清空
		 Dep.target=null;
	}
	 // 在调用set触发Dep的notify时要执行的update函数,用于响应数据变化执行run函数即dom变更
	 update(newVal){
		// 值发生变化才变更
		if(this.value !== newVal){
			this.value = newVal;
			this.run()
		}
	}
	 // 执行DOM更新等操作
	 run(){
        this.cb(this.value);
    }
}

通过以上的代码我们就实现了一个去除了模板编译的简易版的Vue,我们用简单化模拟dom的变更

//======测试=======
let data={
    message:'hello',
    num:0
}
let app=new Vue({
    data:data
});
//模拟数据监听
new Watcher(app,'message',function(value){
    //模拟dom变更
    console.log('message 引起的dom变更--->',value); // world
})
new Watcher(app,'num',function(value){
    //模拟dom变更
    console.log('num 引起的dom变更--->',value); // 100
})
data.message='world';
data.num=100;

总结

1、实现一个监听器 Observe:对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。并且可以拿到最新值通知订阅者。

2、实现一个订阅者 Watcher:Watcher 订阅者是 Observe 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observe 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。

3、实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。

4、实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/345785.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

找出字符串中第一个匹配项的下标-力扣28-java

一、题目描述给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。示例 1&#xff1a;输入&#xff1a;hayst…

java ssm计算机系统在线考试平台idea

本系统主要包括以下功能模块学生、教师、班级、考试评阅、在线考试、试题内容、考试等模块&#xff0c;通过这些模块的实现能够基本满足日常计算机系统平台的操作。 本文着重阐述了计算机系统平台的分析、设计与实现&#xff0c;首先介绍开发系统和环境配置、数据库的设计&…

ASP.NET大型绩效考核评估系统源码

分享一套ASP.NET大型绩效考核评估系统源码&#xff0c;功能基本完善&#xff0c;代码完整&#xff0c;适合学习。本系统采用.Net2010开发&#xff0c;数据库基于SQL2000/2005/2008引擎开发。系统运行环境为.NET2.0IIS6.0基础环境。 源码分享学习&#xff0c;私信获取&#xff…

第40天|LeetCode139. 单词拆分、多重背包

1.题目链接&#xff1a;139. 单词拆分 题目描述&#xff1a; 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 注意&#xff1a;不要求字典中出现的单词全部都使用&#xff0c;并且字典中的单词可以重复使用。 解法&…

SpringBoot 指标监控 Actuator

Spring Boot Actuator为 Micrometer 提供了依赖管理和自动配置&#xff0c;Micrometer是一个支持 众多监控系统 的应用程序指标接口 该功能与&#xff1a;java\jdk\bin 下的 Jconsole 功能雷同 1、pom文件中引入依赖&#xff08;使用的springboot是2.7.2&#xff09; <dep…

15- 决策回归树, 随机森林, 极限森林 (决策树优化) (算法)

1. 决策回归树: from sklearn.tree import DecisionTreeRegressor model DecisionTreeRegressor(criterionmse,max_depth3) model.fit(X,y) # X是40个点 y是一个圆 2. 随机森林 稳定预测: from sklearn.ensemble import RandomForestClassifier # model RandomForestC…

Flink相关的记录

Flink源码编译首次编译的时候&#xff0c;去除不必要的操作&#xff0c;同时install会把Flink中的module安装到本地仓库&#xff0c;这样依赖当前module的其他组件就无需去远程仓库拉取当前module&#xff0c;节省了时间。mvn clean install -T 4 -DskipTests -Dfast -Dmaven.c…

对比Vector、ArrayList、LinkedList有何区别?

第8讲 | 对比Vector、ArrayList、LinkedList有何区别&#xff1f; 我们在日常的工作中&#xff0c;能够高效地管理和操作数据是非常重要的。由于每个编程语言支持的数据结构不尽相同&#xff0c;比如我最早学习的 C 语言&#xff0c;需要自己实现很多基础数据结构&#xff0c;管…

SpringCloud入门实战(六)-OpenFeign服务调用

⭐️ SpringCloud 入门实战系列不迷路&#xff1a; SpringCloud 入门实战&#xff08;一&#xff09;什么是SpringCloud&#xff1f;SpringCloud 入门实战&#xff08;二&#xff09;-SpringCloud项目搭建SpringCloud 入门实战&#xff08;三&#xff09;-Eureka注册中心集成S…

基于JAVA+SpringBoot+LayUI+Shiro的仓库管理系统

基于JAVASpringBootLayUIShiro的仓库管理系统 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项…

React基础用法,脚手架创建项目。父子及兄弟通信,跨组件通信,定时器时钟案例

create React App 脚手架工具创建项目1.下载插件2.打开终端npx create-react-app my-app //my-app是自己创建的项目名创建完成后cd my-app&#xff0c;到该项目的盘符执行npm start&#xff0c;就可以运行起来了组件通讯父传子在父亲组件中引用子组件在render&#xff08;&…

基于商品理解的成交能力和成交满意度优化在Lazada的实践

作者&#xff1a;马蕊 Lazada推荐算法团队 在Lazada各域推荐场景中&#xff0c;既有优质商品优质卖家不断涌现带来的机会&#xff0c;也有商品质量参差带来的问题。如何才能为用户提供更好的体验&#xff0c;对卖家变化行为进行正向激励呢&#xff1f;下面本文将为大家分享我们…

在TheSandbox 的「BOYS PLANET」元宇宙中与你的男孩们见面吧!

世界各的男孩们成为 K-Pop 男团的旅程。 Mnet 的全球项目 BOYS PLANET 终于在 2 月 2 日首次亮相&#xff01; The Sandbox 与 CJ ENM 合作&#xff0c;于 2 月 6 日晚上 10 点开始举办两个基于 BOYS PLANET 生存节目的虚拟体验&#xff1a;BOYS PLANET&#xff1a;BOYS LAND 和…

五年制转本学历很重要江苏专转本

五年制转本学历很重要&#xff01; 大专和本科是有区别的 越好的公司&#xff0c;越重要的职位&#xff0c;要求越高。 目前在中大型企业&#xff0c;除了销售、行政等岗位&#xff0c;其他普遍要求本科学历&#xff0c;有些可以放宽到大专。很多公司对于程序员等岗位的要求不仅…

java中方法的学习笔记

java中方法是完成特定的功能的&#xff0c;相对独立的程序段&#xff0c;与其他编程语言中的子程序&#xff0c;函数等概念相当。 方法一定义&#xff0c;就可以在不同的程序段中调用&#xff0c;因此方法可以增强程序的清晰度&#xff0c;提高编码的效率 方法的声明 [修饰符…

装备制造业数字化转型CRM系统解决方案(信息图)

一、制造企业面临的机遇与挑战 2021年12月28日&#xff0c;工业和信息化部等八部门联合对外发布《“十四五”智能制造发展规划》&#xff0c;明确提到“推进智能制造&#xff0c;要立足制造本质&#xff0c;紧扣智能特征&#xff0c;以工艺、装备为核心&#xff0c;以数据为基…

jsp(全部知识点)

&#x1f44c; 棒棒有言&#xff1a;也许我一直照着别人的方向飞&#xff0c;可是这次&#xff0c;我想要用我的方式飞翔一次&#xff01;人生&#xff0c;既要淡&#xff0c;又要有味。凡事不必太在意&#xff0c;一切随缘&#xff0c;缘深多聚聚&#xff0c;缘浅随它去。凡事…

窗口置顶工具v2.6.0(截图+贴图)

大家好&#xff0c;很高兴能再次更新版本&#xff0c;距上一年12月份到现在&#xff0c;有差不多两个月没有更新了&#xff0c;主要是年底&#xff0c;工作方面要冲刺&#xff0c;直到上周才有空继续开发置顶工具。 本来想再偷懒一段时间再开发&#xff0c;但最近在工作中经常…

用于高效高光谱图像分类的多尺度上下文感知集成深度 KELM(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 高光谱遥感&#xff0c;作为成像技术与细分光谱技术有机结合的成像光谱遥感&#xff0c;可以获取在可见光到短波红外甚至中红外…

重构之改善既有代码的设计(一)

1.1 何为重构&#xff0c;为何重构 第一个定义是名词形式&#xff1a; 重构&#xff08;名词&#xff09;&#xff1a;对软件内部结构的一种调整&#xff0c;目的是在不改变「软件可察行为」前提下&#xff0c;提高其可理解性&#xff0c;降低修改成本。 「重构」的另一个用…