Vue双向绑定代码实现MVVM原理
程序员文章站
2022-04-19 07:57:27
...
Vue三要素
响应式: 例如如何监听数据变化,其中的实现方法就是我们提到的双向绑定
模板引擎: 如何解析模板
渲染: Vue如何将监听到的数据变化和解析后的HTML进行渲染
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>
实现vue双向绑定
</title>
<script>
//1、首先实现一个整体架构(VUE类、Watcher类),订阅发布者设计模式
//2、实现MVVM中的由M到V,把模型里面的数据绑定到视图
//3、(1)最后实现V-M,当文本框输入文本的时候,由文本事件触发更新模型中的数据
// (2)同时更新相对应的视图
//发布者
class myVue {
constructor(options) {
this.$data = options.data;//获取数据
this.$el = document.querySelector(options.el);//获取对象
this._directive = {}//存放订阅者容器
//改变数据 是全页面刷新还是局部刷新
//{订阅者1,订阅者2,订阅者3,订阅者4}
//{mytext:[订阅者1,订阅者2],mybox:[订阅者3]}
this.Observer(this.$data);
this.Compile(this.$el)
}
Observer(data) {
for (let key in data) {
this._directive[key] = [];
let val = data[key];
let watch = this._directive[key];//数组
//this.$data里的每一个属性发生赋值 都要更新视图
// console.log(this._directive) this_directive含Watcher实例对象
Object.defineProperty(this.$data, key, {
get() {
return val;
},
set(newVal) {
if (newVal !== val) {
val = newVal;
//更新视图
// watcher实例.update
watch.forEach(element => {
element.update();
});
}
}
})
}
}
Compile(el) {
//创建文档片段,将DOM操作放内存提高性能
// this.$fragment = this.node2Fragment(el);
// let nodes = this.$fragment.children;
let nodes = el.children;
// console.log(nodes)
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i];
if (node.children.length) {
this.Compile(node)
}
//怎样解析指令
if (node.hasAttribute('v-text')) {
//怎样添加订阅者
//首先获取订阅者名
let attrVal = node.getAttribute('v-text');
// this._directive[arrtVal].push("订阅者1"); //指令对象
// console.log(this._directive)
//订阅数据变化,绑定更新函数
this._directive[attrVal].push(new Watcher(node, this, attrVal, 'textContent'));
// console.log(this._directive)
}
if (node.hasAttribute('v-model')) {
let attrVal = node.getAttribute('v-model');
this._directive[attrVal].push(new Watcher(node, this, attrVal, 'value'));
node.addEventListener('input', () => {
// console.log("哈哈哈哈")
this.$data[attrVal] = node.value
// console.log(this.$data[attrVal])
})
}
}
}
node2Fragment(el) {
let fragment = document.createDocumentFragment()
let child;
while (child = el.firstChild) {
fragment.appendChild(child)
}
return fragment;
}
}
//订阅者
class Watcher {//更新视图
constructor(el, vm, exp, attr) {
this.el = el;
this.vm = vm;
this.exp = exp;
this.attr = attr;
this.update();//初始化视图
}
update() {
// div.textContent=vue对象.$data['mytext']
// input.value=vue对象.$data['mytext']
this.el[this.attr] = this.vm.$data[this.exp]
}
}
</script>
</head>
<body>
<div id="app">
<h1>双向绑定</h1>
<div v-text="mytext">
<div v-text="mybox"></div>
</div>
<div v-text="mybox"> </div>
<input type="text" v-model="mytext">
</div>
</body>
<script>
var app = new myVue({
el: '#app',
data: {
mytext: '青鸟软通公开课',
mybox: 'vue实现MVVM'
}
});
</script>
</html>