主要的功能包括:数据劫持, 编译模板,发布订阅
<!DOCTYPE html>
<head>
<script>
function Mvvm(options) {
this.$options = options;
let data = this._data = this.$options.data;
observe(data)
for (let key in data) {
Object.defineProperty(this, key, {
enumberable: true,
get() {
return this._data[key];
},
set(newVal) {
this._data[key] = newVal;
}
})
}
new Compile(this.$options.el, this)
}
// 数据劫持
function observe(data) {
if (typeof data !== 'object') {
return;
}
for (let key in data) {
let val = data[key];
observe(val);
Object.defineProperty(data, key, {
enumberable: true,
get() {
return val;
},
set(newVal) {
if (newVal === val) {
return;
}
val = newVal;
observe(val);
depObj.notify();
}
})
}
}
// 编译
function Compile(el, mvvmObj) {
let targetElement = document.querySelector(el);
let fregment = document.createDocumentFragment();
Array.from(targetElement.childNodes).forEach((element) => {
fregment.appendChild(element);
});
replace(fregment);
targetElement.appendChild(fregment);
function replace(element) {
let reg = /\{\{(.*)\}\}/;
Array.from(element.childNodes).forEach(el => {
let oroginVal = el.textContent;
if (el.nodeType === 3 && reg.test(el.textContent)) {
let arr = RegExp.$1.split('.');
let val = mvvmObj._data;
arr.forEach(val1 => {
val = val[val1]
})
depObj.addSub(new Watcher(mvvmObj, RegExp.$1, function(newVal) {
el.textContent = oroginVal.replace(reg, newVal);
}))
el.textContent = oroginVal.replace(reg, val);
}
if(el.nodeType === 1) {
Array.from(el.attributes).forEach(attribute => {
let name = attribute.name;
let value = attribute.value;
if (name.indexOf('v-model') === 0) {
el.value = mvvmObj[value]
depObj.addSub(new Watcher(mvvmObj, attribute.value, function(newVal) {
el.value = newVal;
}))
}
el.addEventListener('input', function(e){
mvvmObj[value] = e.target.value;
});
})
}
if (el.childNodes.length > 0) {
replace(el);
}
})
}
}
function Dep() {
this.subs = [];
}
// 订阅
Dep.prototype.addSub = function(sub) {
this.subs.push(sub);
}
// 发布
Dep.prototype.notify = function() {
this.subs.forEach(el => {
el.update();
})
}
function Watcher(mvvmObj, exp, fn) {
this.vm = mvvmObj;
this.exp = exp;
this.fn = fn;
}
Watcher.prototype.update = function() {
let val = this.vm;
let arr = this.exp.split('.');
arr.forEach((val1) => {
val = val[val1];
})
this.fn(val)
}
let depObj = new Dep();
</script>
</head>
<body>
<div id="app">
test: {{test}}
<div>info.name: {{info.name}}</div>
<div>time: {{time}}</div>
<div>info.body.other: <span>{{info.body.other}}</span></div>
<input v-model="test" />
</div>
<script>
var app = new Mvvm({
el: '#app',
data: {
info: {
name: 10,
body: {
other: 'this is other'
},
},
time: '12:30',
test: 'test333333'
}
})
</script>
</body>
</html>