HTML
<div id="app">
<form>
<input type="text" v-model="number">
<input type="text" v-model="number">
</form>
</div>
创建VM实例
window.onload = function() {
var app = new VueModel({
el:'#app',
data: {
number: 0,
}
})
}
初始化函数
function VueModel(options) {
this._init(options);
}
初始化函数实现
VueModel.prototype._init = function (options) {
this.$options = options;
//判断传入的el类型
this.$el = options.el.nodeType === 1 ? options.el : document.querySelector(options.el);
this.$data = options.data;
this.$methods = options.methods;
this.vmbind = {};
this._obverse(this.$data);
this._complie(this.$el);
}
实现数据监听
//数据监听器
VueModel.prototype._obverse = function (obj) {
//获取数据对象data
var _this = this;
//遍历数据对象中的元素
Object.keys(obj).forEach(function (key) {
if (obj.hasOwnProperty(key)) {
_this.vmbind[key] = { _direc: []};
var value = obj[key];
//递归遍历类型是对象的一级属性
if (value instanceof Object) {
_this._obverse(value);
}
var vmbind = _this.vmbind[key];
//数据监听
Object.defineProperty(_this.$data, key, {
enumerable: true,
configurable: true,
get: function () {
return value;
},
set: function (newVal) {
//触发数据更新
if (value !== newVal) {
value = newVal;
vmbind._direc.forEach(function (item) {
item.update();
})
}
}
})
}
})
}
绑定view和model
//绑定view与model
VueModel.prototype._complie = function (root) {
//取出元素节点
var _this = this;
//取出所有一级子节点
var nodes = root.children;
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
//递归子节点
if (node.children.length) {
this._complie(node);
}
if (node.hasAttribute('v-model') && (node.tagName = 'INPUT' || node.tagName == 'TEXTAREA')) {
node.addEventListener('input', (function(key) {
var attrVal = node.getAttribute('v-model');
//增加订阅者
_this.vmbind[attrVal]._direc.push(new Watcher(
'input',
node,
_this,
attrVal,
'value'
))
return function() {
//数据更新
_this.$data[attrVal] = nodes[key].value;
}
})(i));
}
}
}
消息订阅
function Watcher(name, el, vm, exp, attr) {
this.name = name;
this.el = el;
this.vm = vm;
this.exp = exp;
this.attr = attr;
this.update();
}
Watcher.prototype.update = function () {
//将视图中的数据更新到model中
this.el[this.attr] = this.vm.$data[this.exp];
}
效果