现代 JS 中数据双向绑定 (Two-way Data Binding) 已经是一个很常见的概念了,那么双向绑定是如何实现的呢,下面就用最简单的方式实现一下这个功能。
先放代码:
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Two Way Data Binding</title>
</head>
<body>
Name: <input v-bind="name" type="text" name="" id="name">
<br />
Name: <span v-bind="name"></span>
<hr />
City: <input v-bind="city" type="text" name="city" id="city">
<br />
City: <span v-bind="city"></span>
<script src="./index.js"></script>
</body>
</html>
JS:
const elements = document.querySelectorAll('[v-bind]');
// eslint-disable-next-line no-console
console.log(elements);
const data = {};
function addProps(prop) {
let value;
// eslint-disable-next-line no-prototype-builtins
if (!data.hasOwnProperty(prop)) {
Object.defineProperty(data, prop, {
set(newValue) {
value = newValue;
elements.forEach((element) => {
const ele = element;
if (ele.getAttribute('v-bind') === prop) {
if (ele.type && (ele.type === 'text')) {
// For <input> tag
ele.value = newValue;
} else if (!ele.type) {
// For <span> tag
ele.innerHTML = newValue;
}
}
});
},
get() {
return value;
},
enumerable: true,
});
}
}
elements.forEach((element) => {
const ele = element;
if (ele.type === 'text') {
const prop = ele.getAttribute('v-bind');
addProps(prop);
ele.oninput = () => {
data[prop] = ele.value;
// eslint-disable-next-line no-console
console.log(data);
};
}
});
效果预览:
解析:这个实现其实非常简单,核心有两点,一个是定义一个 data 对象来存储数据,另一个是使用 defineProperty 方法来监听该对象的变化,并在 set 和 get 操作中,修改 DOM 的值。了解了 defineProperty 的使用方法,再结合代码,不难理解这个双向绑定的实现过程。
具体 defineProperty 详细说明可以参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
源码:https://github.com/tlbai/js-labs/tree/master/two-way-data-binding
参考文章:https://medium.com/frontend-fun/js-vanilla-two-way-binding-5a29bc86c787