Vue - (一) Vue中双向绑定的原理-Object.defineProperty

本文深入解析Vue框架中双向绑定的实现原理,通过Object.defineProperty技术,详细介绍了如何将数据属性转换为响应式的getter/setter,实现视图与数据的同步更新。

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

Vue中双向绑定的原理是使用了Object.defineProperty进行绑定的。

我们先来看下官方给出的说法:

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setterObject.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

看一下Vue源码对于双向绑定的初期实现:

1. Even You 的初期定义(2013-07初次提交)

2. 在源码基础上进行了注释,注释的内容是个人理解,仅供参考。

<!DOCTYPE html>
<html>
	<head>
		<title>ideal</title>
		<meta charset="utf-8">
	</head>
	<body>
		<div id="test">
			<p>{{msg}}</p>
			<p>{{msg}}</p>
			<p>{{msg}}</p>
			<p>{{what}}</p>
			<p>{{hey}}</p>
		</div>
		<script>
			// span元素上绑定的自定义属性
			var bindingMark = 'data-element-binding';
			function Element (id, initData) {
				var self	 = this,
					// 当前绑定的ID元素,div id = test.
					el 		 = self.el = document.getElementById(id);
					// 需要绑定的span
					bindings = {} ;// the internal copy
					// 用于set的data
					data 	 = self.data = {}; // the external interface
					// 将{{mes}}转变成 <span data-element-binding='mes'></span>
					content  = el.innerHTML.replace(/\{\{(.*)\}\}/g, markToken);

				el.innerHTML = content;
				// 1.将页面上转换好的span移除额外样式,这是因为bindings已经绑定上对应的span了,没有那些样式也可以。
				// bindings[variable].els = el.querySelectorAll('[' + bindingMark + '="' + variable + '"]');
				for (var variable in bindings) {
					bind(variable);
				}
				// 2.触发Object.defineProperty的set,将值更改到span上。
				if (initData) {
					for (var variable in initData) {
						data[variable] = initData[variable]
					}
				}

				// 修改span标签样式
				function markToken (match, variable) {
					bindings[variable] = {};
					return '<span ' + bindingMark + '="' + variable +'"></span>'
				}

				function bind (variable) {
					// 绑定span
					bindings[variable].els = el.querySelectorAll('[' + bindingMark + '="' + variable + '"]');
					// 移除样式
					[].forEach.call(bindings[variable].els, function (e) {
				        e.removeAttribute(bindingMark)
				    });
					Object.defineProperty(data, variable, {
						set: function (newVal) {
						    [].forEach.call(bindings[variable].els, function (e) {
								e.textContent = newVal;
						        bindings[variable].value = newVal
						    })
						},
						get: function () {
						    return bindings[variable].value
						}
					})
				}

			}

			 new Element('test',{
				msg: 'hello',
				hey: 'hey'
			});

		</script>
	</body>
</html>

看一下Object.defineProperty的文档说明:

Object.defineProperty(obj, prop, descriptor)

 

obj
要在其上定义属性的对象。
prop
要定义或修改的属性的名称。
descriptor
将被定义或修改的属性描述符。

文档地址: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

基于Object.defineProperty实现一个类似的功能:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Bindings</title>
</head>
<body>
    <span id="mes"></span>
    <span id="hello"></span>
</body>
</html>
<script>
    let initData = {
        mes: 'mes',
        hello: 'hello'
    };
    let spans = [];
    let data = {};
    function bind(key){
        Object.defineProperty(data ,key, {
            set: function (newValue) {
                spans[key].textContent = newValue;
                spans[key].value = newValue
            },
            get: function () {
                return spans[key].value;
            }
        });
    };
    // 绑定span
    for (let dataKey in initData) {
        spans[dataKey] = document.getElementById(dataKey);
        bind(dataKey);
    }

    // 触发set
    for (let dataKey in initData) {
        data[dataKey] = initData[dataKey];
    }

</script>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值