模仿实现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>
posted @ 2024-04-26 11:59  xuxianshun  阅读(5)  评论(0编辑  收藏  举报