vue实现数据同步以及数据双向绑定的简单原理

本文简要介绍Vue框架如何实现数据同步和双向绑定。通过创建Vue实例、设置数据和使用Proxy对象监听数据变化,模拟数据同步过程。在双向绑定方面,利用v-model结合render函数实现表单数据与视图的实时同步。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

vue框架使用起来十分的方便,因为不用过多地去关注DOM的操作,大部分只需要关注视图层以及数据的变化,但是它是怎么实现数据同步以及双向绑定的呢,以下是我简单的概述,因为我也刚入门没有多久,所以写下这篇文章加深自己对vue的理解,文章若有错误,可以随时指正。

数据同步

首先看看vue是怎么实现数据同步的:
1.创建一个vue实例
2.给el属性设置一个接管范围,也就是vue实例在这个区域才有作用
3.设置数据
4.在html中使用插值来绑定数据
5.数据变更后,同步到html中,然后渲染到界面

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>

<body>
	<div id="app">{{ message }}</div>

	<!-- 引用Vue库 -->
	<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
	<script>
		let app = new Vue({
			el: "#app",// vue实例接管的区域
			data: {
				message: "Hello Vue!"
			}
		})

		// 2s后数据变更
		setTimeout(() => {
			app.message = "bye vue!";
		}, 2000);

	</script>
</body>

</html>

那么按照这个思路,可以借助ES6的一个对象Proxy来模拟这个过程:

  1. 获取DOM,设置接管范围====>模拟el
let el = document.querySelector('#app');
  1. 暂存元素里面的内容,因为数据每次变更就会编译一次,那么原来的内容就不会再存在了,需要暂存
let template = el.innerHTML;
  1. 定义数据===>模拟data
let _data = {
	name: 'laocao',
	age: 22
};
  1. 实例化一个Proxy对象(原生JS对象)
let data = new Proxy(_data, {
	
	set (obj, name, value) {
		console.log(obj)
		//alert(`外面要修改${name}为${value}`)
		obj[name] = value;// 修改数据
		//console.log(`数据修改了`);
		
		// 数据变更后渲染
		render();
	}
});

说明:

  • 这个代理对象负责监听数据变化,将真正数据隐藏,这个代理对象其实就像是一个栏杆。
  • 第一个参数是“躲在栏杆后面真正的数据”,也就是proxy代理的对象。
  • 第二个参数是为代理对象设置的“拦截方法”,也就是说只有通过了代理的检查,外部才能动这个真正的数据对象。
  • 通过proxy代理就可以实现监听数据的变化,内部数据变化后,通过这个“栏杆”之后就可以渲染在界面上了
  • proxy里面有13个方法,这是其中一个,obj其实就是“躲在栏杆背后”的数据对象,name是这个obj的属性,value是值,其实后面还有一个参数是proxy实例本身
  • 只要外部请求数据,就要通过这个“栏杆”的检测,那么set就是其中一个“栏杆”。其实在vue内部不止是那么简单了,它里面会有很多的其他方法,这里为了大概说明原理,就暂时不继续深入了。
  1. 模拟渲染函数render
function render() {
			el.innerHTML = template.replace(/\{\{\s*\w+\s*\}\}/g, str => {
				str = str.substring(2, str.length - 2).trim();
				return _data[str];
			});
		}

说明:

  • 使用string原型的replace方法配合正则表达式找到html里面与数据绑定的变量,即插值表达式里面的内容,这里完全可以自己定制,插值里面可以有括号。
  • replace方法第一个参数是需要替换的字符串,第二个参数是要替换的字符串或者是一个回调函数,最后replace方法返回一个新字符串
  • 找到插值表达式后,先利用string原型的一个substring方法找到变量,并使用trim去除两边的括号
  • 此后数据变更,就会调用这个函数,并且再将上一次div里面的模板字符串重新渲染一次,完成数据同步

注意:原字符串没有被改变!

完整代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<title>Document</title>

</head>

<body>
	<div id="app">
		姓名:{{ name }}<br/>
		年龄:{{ age  }}
	</div>

	<script>
		// 1.获取DOM,设置接管范围====>模拟el
		let el = document.querySelector('#app');
		// 2. 暂存元素里面的内容====>因为数据每次变更就会编译一次,那么原来的内容就不会再存在了,需要暂存
		let template = el.innerHTML;
		// 3. 定义数据===>模拟data
		let _data = {
			name: 'laocao',
			age: 22
		};
		let data = new Proxy(_data, {
			set (obj, name, value) {
				console.log(obj)
				//alert(`外面要修改${name}为${value}`)
				obj[name] = value;// 修改数据
				//console.log(`数据修改了`);
				// 数据变更后渲染
				render();
			}
		});

		//这个方法是数据还没变更之前,先将data里面默认的数据渲染到页面上
		render();
		function render() {
			el.innerHTML = template.replace(/\{\{\s*\w+\s*\}\}/g, str => {
				str = str.substring(2, str.length - 2).trim();
				return _data[str];
			});
		}

	</script>
</body>

</html>

双向绑定

在vue中实现双向绑定主要是应用在表单数据中,使用v-model可以实现双向数据绑定。

一个简单的例子:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<div id="app">
		<input type="text" v-model="msg" /><br>
		{{ msg }}
	</div>

	<script src="https://cdn.jsdelivr.net/npm/vue"></script>
	<script>
		let vm = new Vue({
			el: '#app',
			data: {
				msg: 'hello'
			}
		});
	</script>
</body>
</html>

1.表单输入框的值通过v-model和data的数据msg进行绑定
2.msg与与DOM中的插值表达式又是同步的,所以表单的变化会引起插值表达式的变化。

根据这个思路,对render函数的代码进行改动:

//双向绑定
Array.from(el.getElementsByTagName('input'))
	 .filter(element => element.getAttribute('v-model'))
	 .forEach(input => {
	 	let bindData = input.getAttribute('v-model');
	 	input.value = _data[bindData];

	 	input.addEventListener('input', function() {
	 		data[bindData] = this.value;
	 	}, false)
	 });
  1. 首先需要将数据渲染到表单中,需要找到有v-model属性的标签
  2. 遍历这些标签,将v-model的属性值提出来,从data中找到v-model属性中绑定的值
  3. 另表单输入框的value等于刚刚在data中找到的值,暂时完成一方的绑定
  4. 监听输入框的变化,输入产生的值传入data中,完成双向数据绑定

注意:Array中的from方法是将一个伪数组转化成一个真正的数组。

总结

目前接触到只有proxy对象可以模拟,以后遇到别的方法,或者翻看vue源码的时候来继续补充这篇文章。

参考

【1】阮一峰es6中的Proxy对象
【2】MDN中String对象的replace方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值