整体思路:Observer 作为发布者,Watcher 作为订阅者,Compiler 将二者连接起来
1、实现 Observer
function Obsever(data) {
Object.keys(data).forEach(key => defineReactive(data, key, data[key]))
}
function observe(data) {
if (typeof data !== 'object' || data === null) {
return
}
return new Obsever(data)
}
function defineReactive(obj, key, val) {
observe(val)
var dep = new Dep()
Object.defineProperty(obj, key, {
configurable: false,
enumerable: true,
get: function () {
Dep.target && dep.add(Dep.target)
return val
},
set: function (newValue) {
if (newValue !== val) {
val = newValue
dep.notify(newValue)
}
}
})
}
function Dep() {
this.subs = []
}
Dep.target = null
Dep.prototype = {
constructor: Dep,
add: function (sub) {
this.subs.push(sub)
},
notify: function (value) {
this.subs.forEach(sub => sub.update(value))
}
}
2、实现 Watcher
function Watcher(vm, exp, cb) {
this.vm = vm;
this.exp = exp;
this.cb = cb;
this.val = this.get()
}
Watcher.prototype = {
constructor: Watcher,
get: function () {
Dep.target = this;
var val = this.vm[this.exp];
Dep.target = null;
return val
},
update: function (newValue) {
this.cb(newValue)
}
}
3、实现 Compiler
function Compiler(vm, el) {
var dom = document.querySelector(el);
this.vm = vm;
this.$el = dom;
this.init();
}
Compiler.prototype = {
constructor: Compiler,
init: function () {
var childNodes = this.$el.childNodes;
[].slice.call(childNodes).forEach(child => this.compile(child));
},
compile: function (child) {
if (child.nodeType === 3) {
var val = child.textContent;
var reg = /\{\{(.*)\}\}/;
var result = val.match(reg);
if (!result) return;
this.bind(val.match(reg)[1], "text", child);
} else {
var attrs = child.attributes;
[].slice.call(attrs).forEach(attr => {
var directiveReg = /v-/,
eventDirectiveReg = /v-on/,
attrName = attr.name,
attrValue = attr.value;
if (attrName.match(directiveReg)) {
if (attrName.match(eventDirectiveReg)) {
this.event(attrName.slice(5), attrValue, child);
} else {
this.bind(attrValue, attrName.slice(2), child);
}
child.removeAttribute(attrName);
}
});
[].slice.call(child.childNodes).forEach(child => this.compile(child));
}
},
bind: function (exp, type, node) {
var me = this;
if (type === "model") {
node.addEventListener("input", function (e) {
me.vm[exp] = e.target.value;
});
}
var updateFn = updateUtil[type];
console.log('me.vm[exp]', me.vm[exp])
updateFn && updateFn(node, me.vm[exp]);
new Watcher(me.vm, exp, function () {
updateFn && updateFn(node, me.vm[exp]);
});
},
event: function (eventType, exp, node) {
var handler = this.vm.options.methods[exp].bind(this.vm)
node.addEventListener(eventType, handler, false);
}
};
var updateUtil = {
text: function (node, newValue) {
node.textContent = newValue;
},
model: function (node, newValue) {
node.value = newValue;
}
};
4、实现 mvvm
function MVVM(options) {
this.options = options
this.proxy(options.data)
this.init(options)
}
MVVM.prototype = {
constructor: MVVM,
init: function (opt) {
observe(opt.data)
this.initComputed()
this.initWatch()
this.$compiler = new Compiler(this, opt.el)
},
initComputed() {
var computed = this.options.computed
Object.keys(computed).forEach(key => {
var valueFn = computed[key].bind(this)
Object.defineProperty(this, key, {
configurable: false,
enumerable: true,
get: function () {
return valueFn()
},
set: function (newValue) {
return newValue
}
})
})
},
initWatch() {
var watch = this.options.watch
Object.keys(watch).forEach(key => {
new Watcher(this, key, watch[key].bind(this))
})
},
proxy: function (data) {
Object.keys(data).forEach(key => {
Object.defineProperty(this, key, {
configurable: false,
enumerable: true,
get: function () {
return data[key]
},
set: function (newValue) {
return data[key] = newValue
}
})
})
}
}
5、测试
MVVM 框架测试
{{message}}
{{messages}}
参考
- 本文源码
- 剖析vue实现原理,自己动手实现mvvm