数据双向绑定实现
Posted 1024l
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据双向绑定实现相关的知识,希望对你有一定的参考价值。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<input type="text" v-model="text" /> {{text}}
</div>
</body>
<script type="text/javascript">
// 编译函数
function compile(node, vm) { //整个id的片段
var reg = /{{(.*)}}/; // 来匹配{{xxx}}中的xxx
//如果是元素节点
if(node.nodeType === 1) {
var attr = node.attributes; //节点的属性集合
//解析元素节点的所有属性
console.log(attr);
for(let i = 0; i < attr.length; i++) {
console.log(attr[i]);
console.log(attr[i].nodeName);
if(attr[i].nodeName == ‘v-model‘) {
var name = attr[i].nodeValue //看看是与哪一个数据相关
node.addEventListener(‘input‘, function(e) { //将与其相关的数据改为最新值
vm[name] = e.target.value //e.target指向事件执行时鼠标所点击区域的那个元素 data的值随着输入的值变化
})
console.log(node);
console.log(node.value);
//node.value = vm[name]; //将data中的值赋予给该node node的值随着data变化????未实现 注意这个地方不会被放入监听数组subs中
new Watcher(vm, node, name)
node.removeAttribute(‘v-model‘)
}
}
}
//如果是文本节点
if(node.nodeType === 3) {
if(reg.test(node.nodeValue)) {
var name = RegExp.$1; //获取到匹配的字符串
console.log(name);
name = name.trim();
// node.nodeValue = vm[name]; //将data中的值赋予给该node
new Watcher(vm, node, name) //绑定一个订阅者 让文本随着data的值变化
}
}
}
// 在向碎片化文档中添加节点时,每个节点都处理一下
function nodeToFragment(node, vm) {
var fragment = document.createDocumentFragment();
//createdocumentfragment()方法创建了一虚拟的节点对象,节点对象包含所有属性和方法。
console.log(fragment);
var child;
while(child = node.firstChild) {
console.log(child);
compile(child, vm);
fragment.appendChild(child); //appendChild 方法具有可移动性 node.firstChild 最后为null
}
console.log(node.firstChild);
return fragment
}
// Vue构造函数
// 观察data中的所有属性值,注意增添了observe
function Vue(options) {
this.data = options.data;
observe(this.data, this) //观察者
var id = options.el;
var dom = nodeToFragment(document.getElementById(id), this)
//处理完所有节点后,重新把内容添加回去
document.getElementById(id).appendChild(dom)
}
// 实现一个响应式监听属性的函数。一旦有赋新值就发生变化
function defineReactive(obj, key, val) { //关于data的监听
var dep = new Dep();
Object.defineProperty(obj, key, {
get: function() { //当访问该属性时,会调用此函数
console.log(‘==========Dep.target============‘+Dep.target);
console.log(Dep.target);
if(Dep.target) { //判断监听者 为null时,不放入数组中
dep.addSub(Dep.target)
}
return val
},
set: function(newVal) { //当属性值被修改时,会调用此函数(包括主动修改data,和改变input等的值修改data)
if(newVal === val) {
return
}
val = newVal;
console.log(‘新值‘ + val);
//一旦更新立马通知
dep.notify(); //更改节点内容
}
})
}
// 实现一个观察者,对于一个实例 每一个属性值都进行观察。
function observe(obj, vm) {
for(let key of Object.keys(obj)) {
defineReactive(vm, key, obj[key]);
}
}
// Watcher监听者
function Watcher(vm, node, name) {
Dep.target = this;
this.vm = vm; //this
this.node = node; //节点
this.name = name; //data的key的name
this.update();
Dep.target = null;
}
Watcher.prototype = {
update() {
this.get();
if(this.node.nodeType==3){
this.node.nodeValue = this.value //更改节点内容的关键 nodeValue:节点值
}else{
this.node.value = this.value
}
},
get() {
console.log(‘get‘);
this.value = this.vm[this.name] //触发相应的get 注意只有这一个地方触发了get函数(除了第一次的target,其余都是null,注意此时是不会再被放入监听数组的)
}
}
// dep构造函数
function Dep() {
this.subs = []
}
Dep.prototype = {
addSub(sub) {
this.subs.push(sub) //把需要监听的内容放数组里
},
notify() {
this.subs.forEach(function(sub) {
sub.update();
})
}
}
var vm = new Vue({
el: ‘app‘,
data: {
text: ‘赵刚‘
}
})
vm.text = ‘赵‘
</script>
</html>
以上是关于数据双向绑定实现的主要内容,如果未能解决你的问题,请参考以下文章