手写vue2.0响应式中原理的mini版本

文章类型:Vue

发布者:hp

发布时间:2023-05-25

vue中,改变了数据,就会自动更新页面,为什么会这样呢,其实,本质上是数据的改变引起了相关函数的一个重新调用,导致了浏览器重新渲染页面,

一:流程

1:传统中,我们对html进行读取值和设置值,都需要重新进行调用

<div>
			<p>姓:<span class="firstName"></span></p>
			<p>名:<span class="lastName"></span></p>
			<p>年龄:<span class="age"></span></p>
			<input type="text" oninput="obj.name=this.value"/>
		</div>
		var obj={
		name:'张三',
		age:18
	}
	function firstName(){
		
		document.getElementsByClassName("firstName")[0].innerHTML=obj.name.substring(0,1)
	}
	function lastName(){
		document.getElementsByClassName("lastName")[0].textContent=obj.name.substring(1,obj.name.length)
	}
	function age(){
		document.getElementsByClassName("age")[0].innerHTML=obj.age
	}

2:更改了某个值,那么我们就需要重新调用这个函数,去触发,才能实现页面的更新,那么,本质上还是改变了数据,触发了跟它相关的函数

obj.name="李四"
	// firstName()
	// lastName()
	setTimeout(()=>{
		obj.name='王五'
	},5000)

3:正是因为这个原理,才产生了vue的一个数据驱动视图更新,那么,我们首先要做的是,数据怎么才能知道有变化

var obj={
		name:'张三',
		age:18
	}
	oberseve(obj)
	function oberseve(obj){
	//把传进来的obj进行遍历添加属性和方法
	 console.log(obj)
	
	for(let key in obj){
		let initVal=obj[key]
		/*采用new Set的目的是去重,
		如果对一个属性进行多次修改,
		那么方法会有多个,避免掉性能浪费
		最后统一做一次数据更新
		
		*/
		let funList=new Set()
		
		Object.defineProperty(obj,key,{
			
			get(){
				//读取的时候,进行把使用的函数进行收集
				//为后面修改数据做准备,如果修改数据
				//那么就找到这个依赖里面的数据进行修改
				window.__fun    && funList.add(window.__fun)
				
				console.log(funList)
					console.log('触发get')
				return initVal
			
				
			},
			set(val){
				console.log('触发set')
				 initVal=val
				 //改变了数据  要怎么进行方法的自动更新,
				 //怎么知道是谁,更新哪些方法
				 console.log(funList)
				 console.log(funList.length)
				 /*
				 改变数据的时候,进行通知更新(派发更新)
				 */
				for (let fun of funList) {
					fun()
				}
				
			}
		})
	}
	
}

4:我们通过js原本的一个api,通过读取进入get方法,改变值进入set方法,那么我们就知道数据的一个变化,这也是vue2的一个数据劫持方案,接下来,我们就要思考,数据变化后,怎么让视图进行更新,

要明白谁在改变我,是在用我,说白了就是那个函数读取或者修改了我,好对应触发更新重新渲染

function autoRunFunction(fun){
	/*
	做一种类似代理调用方式
	*/
   window.__fun=fun
   	fun()
   	window.__fun=null
}

5:我们在调用方法的时候,采取代理模式,说白了就是,你要调用,我帮你调用,类似中介模式,那么,我肯定知道谁在调用,把调用人记录下来,等调用完我在清空,

let funList=new Set()
get(){
				//读取的时候,进行把使用的函数进行收集
				//为后面修改数据做准备,如果修改数据
				//那么就找到这个依赖里面的数据进行修改
				window.__fun    && funList.add(window.__fun)
				
				console.log(funList)
					console.log('触发get')
				return initVal
			
				
			},

6:读取的时候,我就记录下来,这也是vue中的所谓依赖,就是数据跟函数发生关系时候,那么,如果修改数据,我就知道谁改了,我就通知谁去更新,也叫派发更新

	set(val){
				console.log('触发set')
				 initVal=val
				 //改变了数据  要怎么进行方法的自动更新,
				 //怎么知道是谁,更新哪些方法
				 console.log(funList)
				 console.log(funList.length)
				 /*
				 改变数据的时候,进行通知更新(派发更新)
				 */
				for (let fun of funList) {
					fun()
				}
				
			}

二:完整代码

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		<div>
			<p>姓:<span class="firstName"></span></p>
			<p>名:<span class="lastName"></span></p>
			<p>年龄:<span class="age"></span></p>
			<input type="text" oninput="obj.name=this.value"/>
		</div>
	</body>
</html>
<script src="js/myvue.js"></script>
<script>
	var obj={
		name:'张三',
		age:18
	}
	function firstName(){
		
		document.getElementsByClassName("firstName")[0].innerHTML=obj.name.substring(0,1)
	}
	function lastName(){
		document.getElementsByClassName("lastName")[0].textContent=obj.name.substring(1,obj.name.length)
	}
	function age(){
		document.getElementsByClassName("age")[0].innerHTML=obj.age
	}
	oberseve(obj)
	/*
	只能从调用处进行想办法
	说白了就是代理代用,
	我帮你去实现这个方法的调用
	*/
   autoRunFunction(firstName)
   autoRunFunction(lastName)
   
	autoRunFunction(age)
	//改变数据
	obj.name="李四"
	// firstName()
	// lastName()
	setTimeout(()=>{
		obj.name='王五'
	},5000)
	
</script>
/*
本质是,数据改变的时候,进行自动触发相关函数的调用
*/
function oberseve(obj){
	//把传进来的obj进行遍历添加属性和方法
	 console.log(obj)
	
	for(let key in obj){
		let initVal=obj[key]
		/*采用new Set的目的是去重,
		如果对一个属性进行多次修改,
		那么方法会有多个,避免掉性能浪费
		最后统一做一次数据更新
		
		*/
		let funList=new Set()
		
		Object.defineProperty(obj,key,{
			
			get(){
				//读取的时候,进行把使用的函数进行收集
				//为后面修改数据做准备,如果修改数据
				//那么就找到这个依赖里面的数据进行修改
				window.__fun    && funList.add(window.__fun)
				
				console.log(funList)
					console.log('触发get')
				return initVal
			
				
			},
			set(val){
				console.log('触发set')
				 initVal=val
				 //改变了数据  要怎么进行方法的自动更新,
				 //怎么知道是谁,更新哪些方法
				 console.log(funList)
				 console.log(funList.length)
				 /*
				 改变数据的时候,进行通知更新(派发更新)
				 */
				for (let fun of funList) {
					fun()
				}
				
			}
		})
	}
	
}

function autoRunFunction(fun){
	/*
	做一种类似代理调用方式
	*/
   window.__fun=fun
   	fun()
   	window.__fun=null
}

三:总结

1:响应式本质是数据改变,自动的触发了相关函数的一个调用

2:找到了一个能让对象数据读取和修改自动触发函数的一个api,(Object.defineProperty),才能实现

3:设计上通过改版调用者的一个代理方式,,采用一个读取记录关系,修改后沿着这个记录的关系进行重新调用渲染


评论
0条评论遵守法律,文明用语,共同建设文明评论区

暂无评论,快来发表第一条评论吧~