<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vue双向数据绑定</title>
<script type="text/javascript">
class Vue{
constructor(options){
this.options = options;
// 获取data对象
this.$data = options.data;
// 获取定义的el元素(#app)
this.$el = document.querySelector(options.el);
// 定义容器收集订阅者
this._directives = {};
// 数据劫持
this.Observer(this.$data);
// 解析指令
this.Compile(this.$el);
}
Observer(data){
//初始化订阅者容器数组,有多少属性就准备多少数组
for (let key in data) {
//遍历对象并存入 _directives对象中,例如: { myText:[], myGod:[] }
this._directives[key] = [];
// 当前值(旧的值)
let Val = data[key];
let _obj = this._directives[key];
//第一个参数:当前对象,第二个参数:当前对象的属性,第三个参数:
Object.defineProperty(this.$data,key,{
get: function(){
return Val;
},
// set方法里的参数是默认自带的(名字自定义)表示新的值
set: function(newVal){
// 如果旧的值与新的值不相等
if( Val !== newVal ){
// 旧的值从新赋值(新的值给旧的值)
Val = newVal;
// 数组遍历
_obj.forEach( element =>{
element.update();
} );
}
}
})
}
console.log(this._directives)
}
Compile(el){
// 获取app下面的所有子元素
let nodes = el.children;
//console.log(nodes)
//遍历元素集合nodes
for(let i = 0; i < nodes.length; i++){
// node[i]是元素对象
let node = nodes[i];
// 如果当前元素的子元素长度大于零
if(node.children.length){
// 递归(自己调用自己)
this.Compile(node);
}
// 如果node元素上有v-text属性
if(node.hasAttribute("v-text")){
// 获取v-text属性值
let attrValue = node.getAttribute("v-text");
// 往订阅者_directives里的对应数组添加Watcher实例对象(就是{ myText:[], myGod:[] }里的myText: [] )
this._directives[attrValue].push(new Watcher(node,this,attrValue,"innerHTML"))
}
// 文本框元素才可以绑定v-model
if(node.hasAttribute("v-model")){
let _this = this;
// 获取v-text属性值
let attrValue = node.getAttribute("v-model");
// 往订阅者_directives里的对应数组添加Watcher实例对象(就是{ myText:[], myGod:[] }里的myText: [] )
this._directives[attrValue].push(new Watcher(node,this,attrValue,"value"))
/**
* EventTarget.addEventListener() 方法将指定的监听器注册到 EventTarget 上,当该对象触发指定的事件时,
* 指定的回调函数就会被执行。 事件目标可以是一个文档上的元素 Element,Document和Window或者任何其他支持
* 事件的对象 (比如 XMLHttpRequest),参数有多个,这里就讲常用的两个。
* 第一个参数:监听的事件类型(很多),查看 https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener
* 第二个参数:事件处理函数
*/
// 监听input值发生变化
node.addEventListener("input",(function(){
// input值发生变化就会执行下面代码
return function(){
// console.log(node.value)
//把node.value(输入框里的文本值)赋值给data里的属性如: myText,myGod
_this.$data[attrValue] = node.value;
}
})())
}
}
}
}
// 订阅者对象,负责更新
class Watcher{
constructor(el,vm,exp,attr){
this.el = el;
this.vm = vm;
this.exp = exp;
this.attr = attr;
// 调用更新方法
this.update();
}
//更新
update(){
this.el[this.attr] = this.vm.$data[this.exp];
}
}
</script>
</head>
<body>
<div id="app">
<h1>双向数据绑定</h1>
<div class="">
<div v-text="myText"></div>
<input type="text" v-model="myText" />
</div>
</div>
<script type="text/javascript">
const app = new Vue({
el: "#app",
data: {
myText: 'vue双向数据绑定',
myGod: '大吉大利'
}
})
</script>
</body>
</html>
js实现双向数据绑定
最新推荐文章于 2023-12-29 21:15:01 发布