模仿实现Vue的双向绑定
简单模仿Vue的单项绑定和双向绑定,可以解析v-bind和v-mode标签
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>模拟Vue单向绑定和双向绑定</title>
</head>
<body>
<div id="app">
<input type="text" :key="number" v-model="name"><br>
<br>
<input type="text" :key="number" v-model="name"><br>
<br>
<div>
<input type="text" :key="name" v-model="number"><br>
<input type="text" :key="name" v-model="number"><br>
</div>
<button type="button" onclick="showData()">查看data</button>
<button type="button" onclick="changeData()">修改data</button>
</div>
<script>
//定义Vue
window.Vue = function(option) {
var _vue = new Object();
if (!option) {
throw "配置参数错误";
}
_vue.$option = option;
//获取根元素
_vue._el = option.el;
//获取data
_vue._data = option.data;
let elementArr = new Array();
//代理data数据到vm本身
function proxyData() {
for (key in _vue._data) {
let _key = key;
Object.defineProperty(_vue, _key, {
get() {
console.log("获取" + _key + "的值");
return _vue._data[_key];
},
set(value) {
console.log("设置" + _key + "的值");
_vue._data[_key] = value;
//更新单项绑定的元素
let elViewArr = _vue["_" + _key + "_view"];
if (elViewArr) {
elViewArr.forEach(el => {
el.element.setAttribute(el.keyName, value);
});
}
//更新双向绑定的元素
let elViewModelArr = _vue["_" + _key + "_view_model"];
if (elViewModelArr) {
elViewModelArr.forEach(el => {
el.element.value = value;
});
}
}
})
}
}
//获取所有元素
function parseElement(root) {
//解析每一个元素和子元素
for (var i = 0; i < root.children.length; i++) {
let child = root.children[i];
if (child.children.length > 0) {
//递归遍历
parseElement(child);
} else {
elementArr.push(child);
}
}
}
//渲染节点
function render(obj) {
if (!obj instanceof HTMLElement) {
return;
}
//渲染
let attrNames = obj.getAttributeNames();
console.log(attrNames);
for (_name in attrNames) {
let _key = attrNames[_name];
//console.log(_key);
let param = obj.getAttribute(_key)
//console.log(param);
//v-bind解析
if (_key.indexOf(":") >= 0 || _key.indexOf("v-bind:") >= 0) {
//单向绑定
if (_vue[param] == undefined) {
throw param + "没有定义";
}
//移除现有key
obj.removeAttribute(_key);
//增加新key
let tmpKey = _key.substr(_key.indexOf(":") + 1);
obj.setAttribute(tmpKey, _vue[param]);
//记录单项绑定的view对象
if (!_vue["_" + param + "_view"]) {
_vue["_" + param + "_view"] = new Array();
}
_vue["_" + param + "_view"].push({
keyName: tmpKey,
element: obj,
valueName: param
});
}
//v-mode解析
if (_key.toLowerCase() == "v-model") {
//双向绑定
if (!obj.type) {
continue;
}
obj.removeAttribute(_key);
switch (obj.type) {
case "text":
if (_vue[param] == undefined) {
throw param + "没有定义"
}
//将绑定该变量的所有对象存到数组中
if (!_vue["_" + param + "_view_model"]) {
_vue["_" + param + "_view_model"] = new Array();
}
_vue["_" + param + "_view_model"].push({
element: obj,
valueName: param
});
//双向绑定操作 M--->V 先赋值
obj.value = _vue[param];
//V----->M
obj.oninput = function() {
_vue[param] = obj.value;
}
//M----->V M更新时可以变更V
break;
default:
break;
}
}
}
}
//显示实时的data数据
_vue.showData = function() {
alert(JSON.stringify(_vue._data));
}
//初始化页面元素
_vue.init = function(){
//劫持data
proxyData();
//获取所有元素
parseElement(document.getElementById(_vue._el));
//渲染元素
elementArr.forEach(element => {
render(element);
});
}
_vue.init();
return _vue;
}
var vm = new Vue({
el: "app",
data: {
number: 20,
name: "李四"
}
});
</script>
<script type="text/javascript">
function showData() {
vm.showData();
}
function changeData() {
vm.number = Date.now();
vm.name = "哈哈哈" + Date.now();
}
</script>
</body>
</html>